Skip to content

Commit d7d252d

Browse files
committed
add: boolean obfuscator
1 parent 3f37b0d commit d7d252d

6 files changed

Lines changed: 200 additions & 10 deletions

File tree

pof/main.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
from pof.logger import logger
4242
from pof.obfuscator import (
4343
AddCommentsObfuscator,
44+
BooleanObfuscator,
4445
BuiltinsObfuscator,
4546
CommentsObfuscator,
4647
ConstantsObfuscator,
@@ -195,9 +196,16 @@ def obfuscate(
195196

196197
for _ in range(2):
197198
tokens = NumberObfuscator().obfuscate_tokens(tokens)
199+
198200
tokens = BuiltinsObfuscator().obfuscate_tokens(tokens)
201+
199202
for _ in range(2):
200203
tokens = string_obfuscator.obfuscate_tokens(tokens)
204+
205+
# TODO (deoktr): enable once fully tested
206+
# for _ in range(2):
207+
# tokens = BooleanObfuscator().obfuscate_tokens(tokens)
208+
201209
tokens = AddCommentsObfuscator().obfuscate_tokens(tokens)
202210

203211
# clean output
@@ -370,9 +378,10 @@ def test(self, source):
370378

371379
tokens = CommentsObfuscator().obfuscate_tokens(tokens)
372380
# tokens = DeepEncryptionEvasion().add_evasion(tokens) # TODO (deoktr): fix
373-
tokens = NamesObfuscator(
374-
generator=AdvancedGenerator.fixed_length_generator(),
375-
).obfuscate_tokens(tokens)
381+
# tokens = NamesObfuscator(
382+
# generator=AdvancedGenerator.fixed_length_generator(),
383+
# ).obfuscate_tokens(tokens)
384+
tokens = BooleanObfuscator().obfuscate_tokens(tokens)
376385
tokens = IndentsObfuscator().obfuscate_tokens(tokens)
377386
tokens = NewlineObfuscator().obfuscate_tokens(tokens)
378387

pof/obfuscator/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
# You should have received a copy of the GNU General Public License
1515
# along with this program. If not, see <https://www.gnu.org/licenses/>.
1616

17+
from .boolean import BooleanObfuscator
1718
from .builtins import BuiltinsObfuscator
1819
from .cipher.deep_encryption import DeepEncryptionObfuscator
1920
from .cipher.rc4 import RC4Obfuscator
@@ -67,6 +68,7 @@
6768
"Base64Obfuscator",
6869
"Base85Obfuscator",
6970
"BinasciiObfuscator",
71+
"BooleanObfuscator",
7072
"BuiltinsObfuscator",
7173
"Bz2Obfuscator",
7274
"CallObfuscator",

pof/obfuscator/boolean.py

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
# POF, a free and open source Python obfuscation framework.
2+
# Copyright (C) 2022 - 2026 Deoktr
3+
#
4+
# This program is free software: you can redistribute it and/or modify
5+
# it under the terms of the GNU General Public License as published by
6+
# the Free Software Foundation, either version 3 of the License, or
7+
# (at your option) any later version.
8+
#
9+
# This program is distributed in the hope that it will be useful,
10+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
# GNU General Public License for more details.
13+
#
14+
# You should have received a copy of the GNU General Public License
15+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
17+
import random
18+
from tokenize import LPAR, LSQB, NAME, NUMBER, RPAR, RSQB, STRING
19+
20+
21+
class BooleanObfuscator:
22+
"""Obfuscate booleans with multiple methods."""
23+
24+
@staticmethod
25+
def obf_true():
26+
match random.randint(1, 6):
27+
case 1:
28+
# all([])
29+
return [
30+
(NAME, "all"),
31+
(LPAR, "("),
32+
(LSQB, "["),
33+
(RSQB, "]"),
34+
(RPAR, ")"),
35+
]
36+
case 2:
37+
# any([True])
38+
return [
39+
(NAME, "any"),
40+
(LPAR, "("),
41+
(LSQB, "["),
42+
(NAME, "True"),
43+
(RSQB, "]"),
44+
(RPAR, ")"),
45+
]
46+
case 3:
47+
# not False
48+
return [
49+
(NAME, "not"),
50+
(NAME, "False"),
51+
]
52+
case 4:
53+
# not not True
54+
return [
55+
(NAME, "not"),
56+
(NAME, "not"),
57+
(NAME, "True"),
58+
]
59+
case 5:
60+
# "" in ""
61+
return [
62+
(STRING, "''"),
63+
(NAME, "in"),
64+
(STRING, "''"),
65+
]
66+
case 6:
67+
# bool(1)
68+
return [
69+
(NAME, "bool"),
70+
(LPAR, "("),
71+
(NUMBER, "1"),
72+
(RPAR, ")"),
73+
]
74+
75+
@staticmethod
76+
def obf_false():
77+
match random.randint(1, 6):
78+
case 1:
79+
# False = all([[]])
80+
return [
81+
(NAME, "all"),
82+
(LPAR, "("),
83+
(LSQB, "["),
84+
(LSQB, "["),
85+
(RSQB, "]"),
86+
(RSQB, "]"),
87+
(RPAR, ")"),
88+
]
89+
case 2:
90+
# all([False])
91+
return [
92+
(NAME, "all"),
93+
(LPAR, "("),
94+
(LSQB, "["),
95+
(NAME, "False"),
96+
(RSQB, "]"),
97+
(RPAR, ")"),
98+
]
99+
case 3:
100+
# not True
101+
return [
102+
(NAME, "not"),
103+
(NAME, "True"),
104+
]
105+
case 4:
106+
# not not False
107+
return [
108+
(NAME, "not"),
109+
(NAME, "not"),
110+
(NAME, "False"),
111+
]
112+
case 5:
113+
# "" not in ""
114+
return [
115+
(STRING, "''"),
116+
(NAME, "not"),
117+
(NAME, "in"),
118+
(STRING, "''"),
119+
]
120+
case 6:
121+
# bool(0)
122+
return [
123+
(NAME, "bool"),
124+
(LPAR, "("),
125+
(NUMBER, "0"),
126+
(RPAR, ")"),
127+
]
128+
129+
def obfuscate_boolean(self, tokval):
130+
if tokval == "True":
131+
return self.obf_true()
132+
return self.obf_false()
133+
134+
def obfuscate_tokens(self, tokens):
135+
result = []
136+
for toknum, tokval, *_ in tokens:
137+
new_tokens = [(toknum, tokval)]
138+
139+
if toknum == NAME and tokval in ["True", "False"]:
140+
new_tokens = self.obfuscate_boolean(tokval)
141+
142+
if new_tokens:
143+
result.extend(new_tokens)
144+
return result

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "python-obfuscation-framework"
7-
version = "1.7.5"
7+
version = "1.8.0"
88
description = "Python Obfuscation Framework"
99
readme = "README.md"
1010
requires-python = ">=3.5"

scripts/tokens_generator.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,8 @@
2525
from tokenize import (
2626
AMPER,
2727
AMPEREQUAL,
28-
ASYNC,
2928
AT,
3029
ATEQUAL,
31-
AWAIT,
3230
CIRCUMFLEX,
3331
CIRCUMFLEXEQUAL,
3432
COLON,
@@ -148,8 +146,6 @@
148146
RARROW: "RARROW",
149147
ELLIPSIS: "ELLIPSIS",
150148
COLONEQUAL: "COLONEQUAL",
151-
AWAIT: "AWAIT",
152-
ASYNC: "ASYNC",
153149
TYPE_IGNORE: "TYPE_IGNORE",
154150
TYPE_COMMENT: "TYPE_COMMENT",
155151
SOFT_KEYWORD: "SOFT_KEYWORD",
@@ -179,8 +175,12 @@ def tokens_of_tokens(tokens, indent: str = " "):
179175

180176
file = sys.argv[1]
181177
logger.info("opening file {file}", args={"file": file})
182-
with Path(file).open() as f:
183-
code = f.read()
178+
179+
if file == "-":
180+
code = sys.stdin.read()
181+
else:
182+
with Path(file).open() as f:
183+
code = f.read()
184184

185185
io_obj = io.StringIO(code)
186186
tokens = list(generate_tokens(io_obj.readline))

tests/obfuscator/test_boolean.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# POF, a free and open source Python obfuscation framework.
2+
# Copyright (C) 2022 - 2026 Deoktr
3+
#
4+
# This program is free software: you can redistribute it and/or modify
5+
# it under the terms of the GNU General Public License as published by
6+
# the Free Software Foundation, either version 3 of the License, or
7+
# (at your option) any later version.
8+
#
9+
# This program is distributed in the hope that it will be useful,
10+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
# GNU General Public License for more details.
13+
#
14+
# You should have received a copy of the GNU General Public License
15+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
17+
import io
18+
from tokenize import generate_tokens, untokenize
19+
20+
from pof.obfuscator import BooleanObfuscator
21+
from .utils import exec_capture
22+
23+
source = """
24+
print(True)
25+
print(False)
26+
"""
27+
28+
29+
def test_TokensObfuscator():
30+
io_obj = io.StringIO(source)
31+
tokens = list(generate_tokens(io_obj.readline))
32+
captured_output = exec_capture(
33+
untokenize(BooleanObfuscator().obfuscate_tokens(tokens))
34+
)
35+
assert captured_output == "True\nFalse\n"

0 commit comments

Comments
 (0)