python-obfuscation-framework 1.9.4__py3-none-any.whl → 1.11.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pof/cli_v2.py +0 -6
- pof/main.py +6 -59
- pof/obfuscator/__init__.py +0 -6
- pof/obfuscator/esoteric/doc.py +6 -6
- pof/obfuscator/esoteric/globals.py +2 -2
- pof/obfuscator/esoteric/imports.py +226 -40
- pof/obfuscator/junk/dead_code.py +37 -11
- pof/obfuscator/names.py +203 -276
- pof/utils/stegano/ipv6encoding.py +9 -5
- pof/utils/stegano/macencoding.py +9 -5
- pof/utils/stegano/uuidencoding.py +15 -5
- {python_obfuscation_framework-1.9.4.dist-info → python_obfuscation_framework-1.11.0.dist-info}/METADATA +67 -33
- {python_obfuscation_framework-1.9.4.dist-info → python_obfuscation_framework-1.11.0.dist-info}/RECORD +17 -20
- pof/obfuscator/definitions.py +0 -356
- pof/obfuscator/names_rope.py +0 -397
- pof/obfuscator/variables.py +0 -116
- {python_obfuscation_framework-1.9.4.dist-info → python_obfuscation_framework-1.11.0.dist-info}/WHEEL +0 -0
- {python_obfuscation_framework-1.9.4.dist-info → python_obfuscation_framework-1.11.0.dist-info}/entry_points.txt +0 -0
- {python_obfuscation_framework-1.9.4.dist-info → python_obfuscation_framework-1.11.0.dist-info}/licenses/LICENSE +0 -0
- {python_obfuscation_framework-1.9.4.dist-info → python_obfuscation_framework-1.11.0.dist-info}/top_level.txt +0 -0
pof/cli_v2.py
CHANGED
|
@@ -92,9 +92,6 @@ def add_obfuscation(tokens, args):
|
|
|
92
92
|
if args.obf_constants:
|
|
93
93
|
logger.debug("obfuscating constants")
|
|
94
94
|
tokens = ConstantsObfuscator().obfuscate_tokens(tokens)
|
|
95
|
-
if args.obf_definitions:
|
|
96
|
-
logger.debug("obfuscating definitions")
|
|
97
|
-
tokens = DefinitionsObfuscator().obfuscate_tokens(tokens)
|
|
98
95
|
if args.obf_a85:
|
|
99
96
|
logger.debug("obfuscating a85")
|
|
100
97
|
tokens = ASCII85Obfuscator().obfuscate_tokens(tokens)
|
|
@@ -146,9 +143,6 @@ def add_obfuscation(tokens, args):
|
|
|
146
143
|
if args.obf_names:
|
|
147
144
|
logger.debug("obfuscating names")
|
|
148
145
|
tokens = NamesObfuscator().obfuscate_tokens(tokens)
|
|
149
|
-
if args.obf_names_rope:
|
|
150
|
-
logger.debug("obfuscating names_rope")
|
|
151
|
-
tokens = NamesRopeObfuscator().obfuscate_tokens(tokens)
|
|
152
146
|
if args.obf_numbers:
|
|
153
147
|
logger.debug("obfuscating numbers")
|
|
154
148
|
tokens = NumberObfuscator().obfuscate_tokens(tokens)
|
pof/main.py
CHANGED
|
@@ -45,7 +45,6 @@ from pof.obfuscator import (
|
|
|
45
45
|
BuiltinsObfuscator,
|
|
46
46
|
CommentsObfuscator,
|
|
47
47
|
ConstantsObfuscator,
|
|
48
|
-
DefinitionsObfuscator,
|
|
49
48
|
DocstringObfuscator,
|
|
50
49
|
ExceptionObfuscator,
|
|
51
50
|
GlobalsObfuscator,
|
|
@@ -56,7 +55,6 @@ from pof.obfuscator import (
|
|
|
56
55
|
NumberObfuscator,
|
|
57
56
|
PrintObfuscator,
|
|
58
57
|
StringsObfuscator,
|
|
59
|
-
VariablesObfuscator,
|
|
60
58
|
XORObfuscator,
|
|
61
59
|
)
|
|
62
60
|
from pof.stager import ImageStager, RC4Stager
|
|
@@ -123,8 +121,6 @@ class Obfuscator(BaseObfuscator):
|
|
|
123
121
|
generator=ex_generator,
|
|
124
122
|
).obfuscate_tokens(tokens)
|
|
125
123
|
|
|
126
|
-
tokens = DefinitionsObfuscator().obfuscate_tokens(tokens)
|
|
127
|
-
|
|
128
124
|
# configure generator
|
|
129
125
|
# generator = alphabet_generator()
|
|
130
126
|
gen_dict = {
|
|
@@ -145,42 +141,7 @@ class Obfuscator(BaseObfuscator):
|
|
|
145
141
|
obf_builtins_rate=0.3,
|
|
146
142
|
).obfuscate_tokens(tokens)
|
|
147
143
|
|
|
148
|
-
|
|
149
|
-
# for detailed explanation just consider the following:
|
|
150
|
-
# ```
|
|
151
|
-
# import module
|
|
152
|
-
# class Bar:
|
|
153
|
-
# def __init__(self):
|
|
154
|
-
# self.foo = getattr(config, "FOO", True)
|
|
155
|
-
# module.imported.function(self.foo)
|
|
156
|
-
# ```
|
|
157
|
-
# in this case the first instance of `foo` will successfully be
|
|
158
|
-
# obfuscated (as it should) but then with the getattr it's marked has
|
|
159
|
-
# "imported" because it's the result of a builtin function, but notice
|
|
160
|
-
# that it's not a "simple" variable but rather it's a class attribute
|
|
161
|
-
# and has a 'self' behind it, so if the variable is marked has imported
|
|
162
|
-
# and a `.` is placed behind it, it won't be changed, this is the case
|
|
163
|
-
# in the function call, which is itself imported, and thus set the
|
|
164
|
-
# imported attribute for itself and all the parameters given to it
|
|
165
|
-
# this is because we don't keep track of the level of the imported
|
|
166
|
-
#
|
|
167
|
-
# FIXME (deoktr): breaks !
|
|
168
|
-
# Another problem is related to result of function, this is, ofc very
|
|
169
|
-
# hard to deal with, but if a function returns an object, such has for
|
|
170
|
-
# example an object of an imported class, which attribute are not
|
|
171
|
-
# obfuscatable, then it breaks.
|
|
172
|
-
# ```
|
|
173
|
-
# import foo
|
|
174
|
-
# def a():
|
|
175
|
-
# return foo.bar()
|
|
176
|
-
# x = a()
|
|
177
|
-
# x.baz()
|
|
178
|
-
# ```
|
|
179
|
-
# In this context, `baz` would be obfuscated, but it shouldn't because
|
|
180
|
-
# the function is part of the `foo` imported module
|
|
181
|
-
# tokens = NamesObfuscator(generator=generator).obfuscate_tokens(tokens)
|
|
182
|
-
# TODO (deoktr): use alternative variable obfuscator using the AST
|
|
183
|
-
# tokens = VariablesObfuscator().obfuscate_tokens(tokens)
|
|
144
|
+
tokens = NamesObfuscator(generator=generator).obfuscate_tokens(tokens)
|
|
184
145
|
|
|
185
146
|
tokens = GlobalsObfuscator().obfuscate_tokens(tokens)
|
|
186
147
|
tokens = BuiltinsObfuscator().obfuscate_tokens(tokens)
|
|
@@ -241,9 +202,7 @@ class Obfuscator(BaseObfuscator):
|
|
|
241
202
|
tokens = self._get_tokens(source)
|
|
242
203
|
tokens = CommentsObfuscator().obfuscate_tokens(tokens)
|
|
243
204
|
generator = BasicGenerator.alphabet_generator()
|
|
244
|
-
|
|
245
|
-
tokens = VariablesObfuscator(generator=generator).obfuscate_tokens(tokens)
|
|
246
|
-
tokens = DefinitionsObfuscator(generator=generator).obfuscate_tokens(tokens)
|
|
205
|
+
tokens = NamesObfuscator(generator=generator).obfuscate_tokens(tokens)
|
|
247
206
|
tokens = IndentsObfuscator().obfuscate_tokens(tokens)
|
|
248
207
|
tokens = NewlineObfuscator().obfuscate_tokens(tokens)
|
|
249
208
|
return self._untokenize(tokens)
|
|
@@ -309,18 +268,7 @@ class Obfuscator(BaseObfuscator):
|
|
|
309
268
|
tokens = CommentsObfuscator().obfuscate_tokens(tokens)
|
|
310
269
|
tokens = ExceptionObfuscator(generator=generator).obfuscate_tokens(tokens)
|
|
311
270
|
tokens = LoggingObfuscator(generator=generator).obfuscate_tokens(tokens)
|
|
312
|
-
|
|
313
|
-
# tokens = ConstantsObfuscator(
|
|
314
|
-
# generator=generator,
|
|
315
|
-
# obf_number_rate=1,
|
|
316
|
-
# # FIXME (deoktr): breaks if obf_string_rate=1 with NamesObfuscator
|
|
317
|
-
# obf_string_rate=0,
|
|
318
|
-
# # FIXME (deoktr): breaks if obf_builtins_rate=1 with NamesObfuscator
|
|
319
|
-
# obf_builtins_rate=0,
|
|
320
|
-
# ).obfuscate_tokens(tokens)
|
|
321
|
-
# tokens = NamesObfuscator(generator=generator).obfuscate_tokens(tokens)
|
|
322
|
-
tokens = VariablesObfuscator(generator=generator).obfuscate_tokens(tokens)
|
|
323
|
-
tokens = DefinitionsObfuscator(generator=generator).obfuscate_tokens(tokens)
|
|
271
|
+
tokens = NamesObfuscator(generator=generator).obfuscate_tokens(tokens)
|
|
324
272
|
tokens = ConstantsObfuscator(
|
|
325
273
|
generator=generator,
|
|
326
274
|
obf_number_rate=1,
|
|
@@ -384,10 +332,9 @@ class Obfuscator(BaseObfuscator):
|
|
|
384
332
|
# tokens = ConstantsObfuscator(generator=generator).obfuscate_tokens(tokens)
|
|
385
333
|
|
|
386
334
|
tokens = CommentsObfuscator().obfuscate_tokens(tokens)
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
# ).obfuscate_tokens(tokens)
|
|
335
|
+
tokens = NamesObfuscator(
|
|
336
|
+
generator=AdvancedGenerator.fixed_length_generator(),
|
|
337
|
+
).obfuscate_tokens(tokens)
|
|
391
338
|
tokens = BooleanObfuscator().obfuscate_tokens(tokens)
|
|
392
339
|
tokens = IndentsObfuscator().obfuscate_tokens(tokens)
|
|
393
340
|
tokens = NewlineObfuscator().obfuscate_tokens(tokens)
|
pof/obfuscator/__init__.py
CHANGED
|
@@ -26,7 +26,6 @@ from .compression.lzma import LzmaObfuscator
|
|
|
26
26
|
from .compression.zlib import ZlibObfuscator
|
|
27
27
|
from .constants import ConstantsObfuscator
|
|
28
28
|
from .controlflow.control_flow_flatten import ControlFlowFlattenObfuscator
|
|
29
|
-
from .definitions import DefinitionsObfuscator
|
|
30
29
|
from .encoding.a85 import ASCII85Obfuscator
|
|
31
30
|
from .encoding.b16 import Base16Obfuscator
|
|
32
31
|
from .encoding.b32 import Base32Obfuscator
|
|
@@ -45,7 +44,6 @@ from .junk.add_comments import AddCommentsObfuscator
|
|
|
45
44
|
from .junk.add_newlines import AddNewlinesObfuscator
|
|
46
45
|
from .junk.dead_code import DeadCodeObfuscator
|
|
47
46
|
from .names import NamesObfuscator
|
|
48
|
-
from .names_rope import NamesRopeObfuscator
|
|
49
47
|
from .numbers import NumberObfuscator
|
|
50
48
|
from .other.tokens import TokensObfuscator
|
|
51
49
|
from .remove.comments import CommentsObfuscator
|
|
@@ -59,7 +57,6 @@ from .stegano.ipv6encoding import IPv6Obfuscator
|
|
|
59
57
|
from .stegano.macencoding import MACObfuscator
|
|
60
58
|
from .stegano.uuidencoding import UUIDObfuscator
|
|
61
59
|
from .strings import StringsObfuscator
|
|
62
|
-
from .variables import VariablesObfuscator
|
|
63
60
|
|
|
64
61
|
__all__ = [
|
|
65
62
|
"ASCII85Obfuscator",
|
|
@@ -81,7 +78,6 @@ __all__ = [
|
|
|
81
78
|
"ControlFlowFlattenObfuscator",
|
|
82
79
|
"DeadCodeObfuscator",
|
|
83
80
|
"DeepEncryptionObfuscator",
|
|
84
|
-
"DefinitionsObfuscator",
|
|
85
81
|
"DocstringObfuscator",
|
|
86
82
|
"ExceptionObfuscator",
|
|
87
83
|
"ExtractVariablesObfuscator",
|
|
@@ -95,7 +91,6 @@ __all__ = [
|
|
|
95
91
|
"LzmaObfuscator",
|
|
96
92
|
"MACObfuscator",
|
|
97
93
|
"NamesObfuscator",
|
|
98
|
-
"NamesRopeObfuscator",
|
|
99
94
|
"NewlineObfuscator",
|
|
100
95
|
"NumberObfuscator",
|
|
101
96
|
"PrintObfuscator",
|
|
@@ -105,7 +100,6 @@ __all__ = [
|
|
|
105
100
|
"StringsObfuscator",
|
|
106
101
|
"TokensObfuscator",
|
|
107
102
|
"UUIDObfuscator",
|
|
108
|
-
"VariablesObfuscator",
|
|
109
103
|
"WhitespaceObfuscator",
|
|
110
104
|
"XORObfuscator",
|
|
111
105
|
"ZlibObfuscator",
|
pof/obfuscator/esoteric/doc.py
CHANGED
|
@@ -183,31 +183,31 @@ class CharFromDocObfuscator:
|
|
|
183
183
|
)
|
|
184
184
|
|
|
185
185
|
@staticmethod
|
|
186
|
-
def
|
|
186
|
+
def _get_char_indexes(string, char):
|
|
187
187
|
return [pos for pos, c in enumerate(string) if c == char]
|
|
188
188
|
|
|
189
189
|
@classmethod
|
|
190
|
-
def
|
|
190
|
+
def _try_find_doc_index(cls, char):
|
|
191
191
|
# take a random builtin doc and search for index in it
|
|
192
192
|
builtin = random.choice(cls.BUILTINS)
|
|
193
193
|
doc = __builtins__[builtin].__doc__
|
|
194
194
|
if not doc:
|
|
195
195
|
msg = f"doc for {builtin} is not a string"
|
|
196
196
|
raise PofError(msg)
|
|
197
|
-
indexes = cls.
|
|
197
|
+
indexes = cls._get_char_indexes(doc, char)
|
|
198
198
|
if len(indexes) == 0:
|
|
199
199
|
msg = "char not present"
|
|
200
200
|
raise PofError(msg)
|
|
201
201
|
index = random.choice(indexes)
|
|
202
202
|
return builtin, index
|
|
203
203
|
|
|
204
|
-
def
|
|
204
|
+
def _obfuscate_char(self, char):
|
|
205
205
|
builtin = None
|
|
206
206
|
index = None
|
|
207
207
|
retry = 3
|
|
208
208
|
for _ in range(retry):
|
|
209
209
|
try:
|
|
210
|
-
builtin, index = self.
|
|
210
|
+
builtin, index = self._try_find_doc_index(char)
|
|
211
211
|
except PofError:
|
|
212
212
|
pass
|
|
213
213
|
else:
|
|
@@ -239,7 +239,7 @@ class CharFromDocObfuscator:
|
|
|
239
239
|
string = ast.literal_eval(tokval)
|
|
240
240
|
if len(string) == 1:
|
|
241
241
|
try:
|
|
242
|
-
new_tokens = self.
|
|
242
|
+
new_tokens = self._obfuscate_char(string)
|
|
243
243
|
except PofError as e:
|
|
244
244
|
logger.debug(str(e))
|
|
245
245
|
except Exception: # noqa: BLE001
|
|
@@ -47,7 +47,7 @@ class GlobalsObfuscator:
|
|
|
47
47
|
local_functions.append(tokval)
|
|
48
48
|
prev_tokval = tokval
|
|
49
49
|
|
|
50
|
-
result = []
|
|
50
|
+
result = []
|
|
51
51
|
prev_tokval = None
|
|
52
52
|
for index, (toknum, tokval, *_) in enumerate(tokens):
|
|
53
53
|
new_tokens = [(toknum, tokval)]
|
|
@@ -60,7 +60,7 @@ class GlobalsObfuscator:
|
|
|
60
60
|
# ensure it's not a definition
|
|
61
61
|
and prev_tokval not in ["def", "class", "."]
|
|
62
62
|
# ensure it's not an argument of a call
|
|
63
|
-
and next_tokval
|
|
63
|
+
and next_tokval != "="
|
|
64
64
|
and tokval not in cls.RESERVED
|
|
65
65
|
):
|
|
66
66
|
new_tokens = [
|
|
@@ -14,58 +14,244 @@
|
|
|
14
14
|
# You should have received a copy of the GNU General Public License
|
|
15
15
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
16
16
|
|
|
17
|
-
from tokenize import
|
|
17
|
+
from tokenize import ENDMARKER, NAME, NEWLINE, NL, OP, STRING
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
class ImportsObfuscator:
|
|
21
|
-
"""Obfuscate
|
|
21
|
+
"""Obfuscate import statements using __import__().
|
|
22
|
+
|
|
23
|
+
Supported forms:
|
|
22
24
|
|
|
23
25
|
```
|
|
24
|
-
import
|
|
26
|
+
# import X
|
|
27
|
+
X = __import__("X")
|
|
28
|
+
|
|
29
|
+
# import X as Y
|
|
30
|
+
Y = __import__("X")
|
|
31
|
+
|
|
32
|
+
# import X, Y
|
|
33
|
+
X = __import__("X")
|
|
34
|
+
Y = __import__("Y")
|
|
35
|
+
|
|
36
|
+
# import X.Y
|
|
37
|
+
X = __import__("X.Y")
|
|
38
|
+
|
|
39
|
+
# from X import Y
|
|
40
|
+
Y = __import__("X", fromlist=["Y"]).Y
|
|
41
|
+
|
|
42
|
+
# from X import Y as Z
|
|
43
|
+
Z = __import__("X", fromlist=["Y"]).Y
|
|
44
|
+
|
|
45
|
+
# from X import Y, Z
|
|
46
|
+
Y = __import__("X", fromlist=["Y"]).Y
|
|
47
|
+
Z = __import__("X", fromlist=["Z"]).Z
|
|
25
48
|
```
|
|
26
49
|
|
|
27
|
-
|
|
50
|
+
Left unchanged:
|
|
51
|
+
|
|
28
52
|
```
|
|
29
|
-
|
|
53
|
+
from X import *
|
|
54
|
+
from . import X
|
|
30
55
|
```
|
|
31
|
-
|
|
32
|
-
Note that this has not been tested very well.
|
|
33
56
|
"""
|
|
34
57
|
|
|
58
|
+
@staticmethod
|
|
59
|
+
def _is_statement_start(preceding_tokens: list) -> bool:
|
|
60
|
+
"""Check if the current position is a valid statement start."""
|
|
61
|
+
if not preceding_tokens:
|
|
62
|
+
return True
|
|
63
|
+
for toknum, _ in reversed(preceding_tokens):
|
|
64
|
+
if toknum in (NEWLINE, NL, ENDMARKER):
|
|
65
|
+
return True
|
|
66
|
+
if toknum in (NAME, OP, STRING):
|
|
67
|
+
return False
|
|
68
|
+
return True
|
|
69
|
+
|
|
70
|
+
@classmethod
|
|
71
|
+
def _transform_import(cls, stmt_tokens: list) -> list | None:
|
|
72
|
+
"""Transform collected import statement tokens into __import__() calls.
|
|
73
|
+
|
|
74
|
+
Returns None if the import should be left unchanged.
|
|
75
|
+
"""
|
|
76
|
+
body = [
|
|
77
|
+
(t, v)
|
|
78
|
+
for t, v in stmt_tokens
|
|
79
|
+
if t not in (NEWLINE, NL, ENDMARKER) and v not in ("(", ")")
|
|
80
|
+
]
|
|
81
|
+
trailing = [(t, v) for t, v in stmt_tokens if t in (NEWLINE, NL, ENDMARKER)]
|
|
82
|
+
|
|
83
|
+
if not body:
|
|
84
|
+
return None
|
|
85
|
+
|
|
86
|
+
first_val = body[0][1]
|
|
87
|
+
|
|
88
|
+
if first_val == "import":
|
|
89
|
+
return cls._transform_simple_import(body[1:]) + trailing
|
|
90
|
+
if first_val == "from":
|
|
91
|
+
t = cls._transform_from_import(body[1:])
|
|
92
|
+
if t is None:
|
|
93
|
+
return None
|
|
94
|
+
return t + trailing
|
|
95
|
+
return None
|
|
96
|
+
|
|
97
|
+
@classmethod
|
|
98
|
+
def _transform_simple_import(cls, tokens: list) -> list:
|
|
99
|
+
"""Transform: import X / import X as Y / import X, Y / import X.Y."""
|
|
100
|
+
groups = cls._split_by_comma(tokens)
|
|
101
|
+
result: list = []
|
|
102
|
+
|
|
103
|
+
for group in groups:
|
|
104
|
+
if not group:
|
|
105
|
+
continue
|
|
106
|
+
if result:
|
|
107
|
+
result.append((NEWLINE, "\n"))
|
|
108
|
+
|
|
109
|
+
names, alias = cls._extract_alias(group)
|
|
110
|
+
module_name = cls._join_dotted_name(names)
|
|
111
|
+
bind_name = alias or names[0][1]
|
|
112
|
+
|
|
113
|
+
result.extend(
|
|
114
|
+
[
|
|
115
|
+
(NAME, bind_name),
|
|
116
|
+
(OP, "="),
|
|
117
|
+
(NAME, "__import__"),
|
|
118
|
+
(OP, "("),
|
|
119
|
+
(STRING, repr(module_name)),
|
|
120
|
+
(OP, ")"),
|
|
121
|
+
],
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
return result
|
|
125
|
+
|
|
35
126
|
@classmethod
|
|
36
|
-
def
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
127
|
+
def _transform_from_import(cls, tokens: list) -> list | None:
|
|
128
|
+
"""Transform: from X import Y / from X import Y as Z / from X import Y, Z.
|
|
129
|
+
|
|
130
|
+
Returns None for relative imports or wildcard imports.
|
|
131
|
+
"""
|
|
132
|
+
import_idx = None
|
|
133
|
+
for idx, (_, v) in enumerate(tokens):
|
|
134
|
+
if v == "import":
|
|
135
|
+
import_idx = idx
|
|
136
|
+
break
|
|
137
|
+
|
|
138
|
+
if import_idx is None:
|
|
139
|
+
return None
|
|
140
|
+
|
|
141
|
+
module_tokens = tokens[:import_idx]
|
|
142
|
+
name_tokens = tokens[import_idx + 1 :]
|
|
143
|
+
|
|
144
|
+
if any(v == "." for _, v in module_tokens):
|
|
145
|
+
return None
|
|
146
|
+
|
|
147
|
+
if any(v == "*" for _, v in name_tokens):
|
|
148
|
+
return None
|
|
149
|
+
|
|
150
|
+
module_name = cls._join_dotted_name(module_tokens)
|
|
151
|
+
|
|
152
|
+
groups = cls._split_by_comma(name_tokens)
|
|
153
|
+
result: list = []
|
|
154
|
+
|
|
155
|
+
for group in groups:
|
|
156
|
+
if not group:
|
|
157
|
+
continue
|
|
158
|
+
if result:
|
|
159
|
+
result.append((NEWLINE, "\n"))
|
|
160
|
+
|
|
161
|
+
names, alias = cls._extract_alias(group)
|
|
162
|
+
imported_name = names[0][1]
|
|
163
|
+
bind_name = alias or imported_name
|
|
164
|
+
|
|
165
|
+
result.extend(
|
|
166
|
+
[
|
|
167
|
+
(NAME, bind_name),
|
|
168
|
+
(OP, "="),
|
|
169
|
+
(NAME, "__import__"),
|
|
170
|
+
(OP, "("),
|
|
171
|
+
(STRING, repr(module_name)),
|
|
172
|
+
(OP, ","),
|
|
173
|
+
(NAME, "fromlist"),
|
|
174
|
+
(OP, "="),
|
|
175
|
+
(OP, "["),
|
|
176
|
+
(STRING, repr(imported_name)),
|
|
177
|
+
(OP, "]"),
|
|
178
|
+
(OP, ")"),
|
|
179
|
+
(OP, "."),
|
|
180
|
+
(NAME, imported_name),
|
|
181
|
+
],
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
return result
|
|
185
|
+
|
|
186
|
+
@staticmethod
|
|
187
|
+
def _split_by_comma(tokens: list) -> list[list]:
|
|
188
|
+
"""Split a token list by comma operators."""
|
|
189
|
+
groups: list[list] = [[]]
|
|
190
|
+
for toknum, tokval in tokens:
|
|
191
|
+
if toknum == OP and tokval == ",":
|
|
192
|
+
groups.append([])
|
|
193
|
+
else:
|
|
194
|
+
groups[-1].append((toknum, tokval))
|
|
195
|
+
return groups
|
|
196
|
+
|
|
197
|
+
@staticmethod
|
|
198
|
+
def _extract_alias(tokens: list) -> tuple[list, str | None]:
|
|
199
|
+
"""Extract name tokens and alias from a token group.
|
|
200
|
+
|
|
201
|
+
Returns (name_tokens, alias_string_or_None).
|
|
202
|
+
"""
|
|
203
|
+
alias = None
|
|
204
|
+
name_tokens = []
|
|
205
|
+
saw_as = False
|
|
206
|
+
for toknum, tokval in tokens:
|
|
207
|
+
if tokval == "as":
|
|
208
|
+
saw_as = True
|
|
209
|
+
elif saw_as:
|
|
210
|
+
alias = tokval
|
|
211
|
+
saw_as = False
|
|
64
212
|
else:
|
|
65
|
-
|
|
66
|
-
|
|
213
|
+
name_tokens.append((toknum, tokval))
|
|
214
|
+
return name_tokens, alias
|
|
215
|
+
|
|
216
|
+
@staticmethod
|
|
217
|
+
def _join_dotted_name(tokens: list) -> str:
|
|
218
|
+
"""Join NAME and OP('.') tokens into a dotted module name string."""
|
|
219
|
+
return "".join(v for _, v in tokens)
|
|
220
|
+
|
|
221
|
+
@classmethod
|
|
222
|
+
def obfuscate_tokens(cls, tokens: list) -> list:
|
|
223
|
+
result: list = []
|
|
224
|
+
i = 0
|
|
225
|
+
while i < len(tokens):
|
|
226
|
+
toknum, tokval, *_ = tokens[i]
|
|
227
|
+
|
|
228
|
+
if (
|
|
229
|
+
toknum == NAME
|
|
230
|
+
and tokval in ("import", "from")
|
|
231
|
+
and cls._is_statement_start(result)
|
|
232
|
+
):
|
|
233
|
+
stmt_tokens = []
|
|
234
|
+
j = i
|
|
235
|
+
paren_depth = 0
|
|
236
|
+
while j < len(tokens):
|
|
237
|
+
st, sv, *_ = tokens[j]
|
|
238
|
+
stmt_tokens.append((st, sv))
|
|
239
|
+
if sv == "(":
|
|
240
|
+
paren_depth += 1
|
|
241
|
+
elif sv == ")":
|
|
242
|
+
paren_depth -= 1
|
|
243
|
+
if st in (NEWLINE, NL, ENDMARKER) and paren_depth <= 0:
|
|
244
|
+
break
|
|
245
|
+
j += 1
|
|
246
|
+
|
|
247
|
+
replacement = cls._transform_import(stmt_tokens)
|
|
248
|
+
if replacement is not None:
|
|
249
|
+
result.extend(replacement)
|
|
250
|
+
else:
|
|
251
|
+
result.extend(stmt_tokens)
|
|
252
|
+
i = j + 1
|
|
253
|
+
continue
|
|
67
254
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
prev_toknum = toknum
|
|
255
|
+
result.append((toknum, tokval))
|
|
256
|
+
i += 1
|
|
71
257
|
return result
|
pof/obfuscator/junk/dead_code.py
CHANGED
|
@@ -147,17 +147,37 @@ class DeadCodeObfuscator:
|
|
|
147
147
|
tokens.append((DEDENT, ""))
|
|
148
148
|
return tokens
|
|
149
149
|
|
|
150
|
-
|
|
150
|
+
@staticmethod
|
|
151
|
+
def _get_false_cond():
|
|
151
152
|
false_conds = [
|
|
152
153
|
[(NAME, "False")],
|
|
153
154
|
[(NUMBER, "0")],
|
|
154
155
|
[(STRING, '""')],
|
|
155
156
|
[(NAME, "None")],
|
|
157
|
+
[
|
|
158
|
+
(NUMBER, str(random.randint(1, 50))),
|
|
159
|
+
(OP, ">"),
|
|
160
|
+
(NUMBER, str(random.randint(51, 100))),
|
|
161
|
+
],
|
|
162
|
+
[
|
|
163
|
+
(NUMBER, str(random.randint(1, 50))),
|
|
164
|
+
(OP, "=="),
|
|
165
|
+
(NUMBER, str(random.randint(51, 100))),
|
|
166
|
+
],
|
|
167
|
+
[(NAME, "not"), (NAME, "True")],
|
|
168
|
+
[(NAME, "True"), (NAME, "and"), (NAME, "False")],
|
|
169
|
+
[(NUMBER, "0"), (OP, "*"), (NUMBER, str(random.randint(1, 999)))],
|
|
170
|
+
[(NAME, "len"), (OP, "("), (STRING, '""'), (OP, ")")],
|
|
171
|
+
[(NAME, "bool"), (OP, "("), (NUMBER, "0"), (OP, ")")],
|
|
172
|
+
[(OP, "("), (OP, ")"), (NAME, "and"), (NAME, "True")],
|
|
156
173
|
]
|
|
174
|
+
return random.choice(false_conds)
|
|
175
|
+
|
|
176
|
+
def _generate_dead_if_tokens(self, indent_level: int):
|
|
157
177
|
inner_indent = " " * (indent_level + 1)
|
|
158
178
|
|
|
159
179
|
tokens = [(NAME, "if")]
|
|
160
|
-
tokens.extend(
|
|
180
|
+
tokens.extend(self._get_false_cond())
|
|
161
181
|
tokens.extend(
|
|
162
182
|
[
|
|
163
183
|
(OP, ":"),
|
|
@@ -171,7 +191,7 @@ class DeadCodeObfuscator:
|
|
|
171
191
|
num_elif = random.randint(0, max(0, self.max_branches - 1))
|
|
172
192
|
for _ in range(num_elif):
|
|
173
193
|
tokens.append((NAME, "elif"))
|
|
174
|
-
tokens.extend(
|
|
194
|
+
tokens.extend(self._get_false_cond())
|
|
175
195
|
tokens.extend(
|
|
176
196
|
[
|
|
177
197
|
(OP, ":"),
|
|
@@ -191,6 +211,10 @@ class DeadCodeObfuscator:
|
|
|
191
211
|
empty_choices = [
|
|
192
212
|
[(OP, "["), (OP, "]")],
|
|
193
213
|
[(NAME, "range"), (OP, "("), (NUMBER, "0"), (OP, ")")],
|
|
214
|
+
[(OP, "("), (OP, ")")],
|
|
215
|
+
[(OP, "{"), (OP, "}")],
|
|
216
|
+
[(STRING, '""')],
|
|
217
|
+
[(NAME, "set"), (OP, "("), (OP, ")")],
|
|
194
218
|
]
|
|
195
219
|
|
|
196
220
|
tokens = [
|
|
@@ -212,13 +236,15 @@ class DeadCodeObfuscator:
|
|
|
212
236
|
|
|
213
237
|
def _generate_dead_while_tokens(self, indent_level: int):
|
|
214
238
|
inner_indent = " " * (indent_level + 1)
|
|
215
|
-
tokens = [
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
239
|
+
tokens = [(NAME, "while")]
|
|
240
|
+
tokens.extend(self._get_false_cond())
|
|
241
|
+
tokens.extend(
|
|
242
|
+
[
|
|
243
|
+
(OP, ":"),
|
|
244
|
+
(NEWLINE, "\n"),
|
|
245
|
+
(INDENT, inner_indent),
|
|
246
|
+
],
|
|
247
|
+
)
|
|
222
248
|
tokens.extend(self._body_tokens())
|
|
223
249
|
tokens.append((DEDENT, ""))
|
|
224
250
|
return tokens
|
|
@@ -301,7 +327,7 @@ class DeadCodeObfuscator:
|
|
|
301
327
|
|
|
302
328
|
if toknum == OP and tokval == "@":
|
|
303
329
|
in_decorator = True
|
|
304
|
-
if in_decorator and toknum ==
|
|
330
|
+
if in_decorator and toknum == NAME and tokval in ("def", "class"):
|
|
305
331
|
in_decorator = False
|
|
306
332
|
|
|
307
333
|
result.append((toknum, tokval))
|