Skip to content

Commit 5e5a992

Browse files
committed
add: tests and fix obfuscators
1 parent 5d1cc4a commit 5e5a992

30 files changed

Lines changed: 400 additions & 95 deletions

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ celerybeat.pid
121121

122122
# Environments
123123
.env
124-
.venv
124+
.venv/
125125
env/
126126
venv/
127127
ENV/

pof/obfuscator/builtins.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,18 @@
1818
# TODO (deoktr): add `__name__.__class__.__class__.__base__.__subclasses__()` variant
1919

2020
import random
21-
from tokenize import LPAR, LSQB, NAME, NUMBER, OP, RPAR, RSQB, STRING
21+
from tokenize import (
22+
FSTRING_END,
23+
FSTRING_START,
24+
LPAR,
25+
LSQB,
26+
NAME,
27+
NUMBER,
28+
OP,
29+
RPAR,
30+
RSQB,
31+
STRING,
32+
)
2233
from typing import ClassVar
2334

2435
from pof.logger import logger
@@ -470,6 +481,7 @@ def obfuscate_builtins(self, tokval):
470481
def obfuscate_tokens(self, tokens):
471482
result = []
472483
parenthesis_depth = 0 # parenthesis depth
484+
fstring_depth = 0
473485
prev_tokval = None
474486
for index, (toknum, tokval, *_) in enumerate(tokens):
475487
new_tokens = [(toknum, tokval)]
@@ -482,14 +494,20 @@ def obfuscate_tokens(self, tokens):
482494
elif toknum == OP and tokval == ")":
483495
parenthesis_depth -= 1
484496

497+
if toknum == FSTRING_START:
498+
fstring_depth += 1
499+
elif toknum == FSTRING_END:
500+
fstring_depth -= 1
501+
485502
if (
486503
toknum == NAME
487504
and tokval in self.BUILTINS
488-
and prev_tokval != "." # avoid changing class/imports functions
505+
and prev_tokval not in (".", "def", "class")
489506
and (
490507
parenthesis_depth == 0
491508
or (parenthesis_depth > 0 and next_tokval != "=")
492509
)
510+
and fstring_depth == 0
493511
):
494512
new_tokens = self.obfuscate_builtins(tokval)
495513

pof/obfuscator/constants.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,18 @@
3838
#
3939

4040
import random
41-
from tokenize import DEDENT, ENCODING, INDENT, NAME, NEWLINE, NUMBER, OP, STRING
41+
from tokenize import (
42+
DEDENT,
43+
ENCODING,
44+
FSTRING_END,
45+
FSTRING_START,
46+
INDENT,
47+
NAME,
48+
NEWLINE,
49+
NUMBER,
50+
OP,
51+
STRING,
52+
)
4253

4354
from pof.utils.generator import BasicGenerator
4455
from pof.utils.tokens import merge_implicit_strings
@@ -238,6 +249,7 @@ def obfuscate_tokens(self, tokens):
238249
variables = {}
239250
result = []
240251
parenthesis_depth = 0 # parenthesis depth
252+
fstring_depth = 0
241253
prev_tokval = None
242254
prev_toknum = None
243255
for index, (toknum, tokval, *_) in enumerate(tokens):
@@ -252,12 +264,17 @@ def obfuscate_tokens(self, tokens):
252264
elif toknum == OP and tokval == ")":
253265
parenthesis_depth -= 1
254266

267+
if toknum == FSTRING_START:
268+
fstring_depth += 1
269+
elif toknum == FSTRING_END:
270+
fstring_depth -= 1
271+
255272
# obfuscation
256-
if (
273+
if fstring_depth == 0 and (
257274
(
258275
toknum == NAME
259276
and tokval in self.BUILTINS
260-
and prev_tokval != "." # avoid changing class/imports functions
277+
and prev_tokval not in (".", "def", "class")
261278
and (
262279
parenthesis_depth == 0
263280
or (parenthesis_depth > 0 and next_tokval != "=")

pof/obfuscator/controlflow/control_flow_flatten.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ def _should_skip_function(body: list[ast.stmt]) -> bool:
4040
ast.AsyncFor,
4141
ast.AsyncWith,
4242
ast.Await,
43+
ast.Nonlocal,
44+
ast.Global,
4345
),
4446
):
4547
return True

pof/obfuscator/esoteric/call.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
# along with this program. If not, see <https://www.gnu.org/licenses/>.
1616

1717
import keyword
18-
from tokenize import NAME, OP
18+
from tokenize import FSTRING_END, FSTRING_START, NAME, OP
1919

2020

2121
# TODO (deoktr): add frequency
@@ -39,18 +39,25 @@ class CallObfuscator:
3939
def obfuscate_tokens(cls, tokens):
4040
result = [] # obfuscated tokens
4141
prev_tokval = None
42+
fstring_depth = 0
4243
for index, (toknum, tokval, *_) in enumerate(tokens):
4344
new_tokens = [(toknum, tokval)]
4445
next_tokval = None
4546
if len(tokens) > index + 1:
4647
_, next_tokval, *__ = tokens[index + 1]
4748

49+
if toknum == FSTRING_START:
50+
fstring_depth += 1
51+
elif toknum == FSTRING_END:
52+
fstring_depth -= 1
53+
4854
if (
4955
# ensure it's not a definition
5056
(prev_tokval is None or prev_tokval not in ["def", "class"])
5157
and toknum == NAME
5258
and tokval not in cls.RESERVED
5359
and next_tokval == "("
60+
and fstring_depth == 0
5461
):
5562
new_tokens.extend(
5663
[

pof/obfuscator/extract_variables.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,19 @@
1515
# along with this program. If not, see <https://www.gnu.org/licenses/>.
1616

1717
import keyword
18-
from tokenize import DEDENT, ENCODING, INDENT, NAME, NEWLINE, NL, NUMBER, OP, STRING
18+
from tokenize import (
19+
DEDENT,
20+
ENCODING,
21+
FSTRING_END,
22+
FSTRING_START,
23+
INDENT,
24+
NAME,
25+
NEWLINE,
26+
NL,
27+
NUMBER,
28+
OP,
29+
STRING,
30+
)
1931

2032
from pof.utils.generator import BasicGenerator
2133
from pof.utils.tokens import merge_implicit_strings
@@ -207,12 +219,13 @@ def __init__(self, generator=None) -> None:
207219
def generate_new_name(self):
208220
return next(self.generator)
209221

210-
def obfuscate_tokens(self, tokens): # noqa: C901
222+
def obfuscate_tokens(self, tokens): # noqa: C901, PLR0912
211223
tokens = merge_implicit_strings(tokens)
212224
result = []
213225
new_line_buffer = []
214226
line_buffer = []
215227
parenthesis_depth = 0
228+
fstring_depth = 0
216229
prev_toknum = None
217230
in_decorator = False
218231

@@ -224,6 +237,11 @@ def obfuscate_tokens(self, tokens): # noqa: C901
224237
elif toknum == OP and tokval == ")":
225238
parenthesis_depth -= 1
226239

240+
if toknum == FSTRING_START:
241+
fstring_depth += 1
242+
elif toknum == FSTRING_END:
243+
fstring_depth -= 1
244+
227245
# track decorator context, suppress flushing between @ and def/class
228246
if toknum == OP and tokval == "@":
229247
in_decorator = True
@@ -244,8 +262,10 @@ def obfuscate_tokens(self, tokens): # noqa: C901
244262
on_continuation_line = first_name_in_line in self.CONTINUATION_KEYWORDS
245263

246264
if (
247-
(toknum == STRING and not is_docstring) or toknum == NUMBER
248-
) and not on_continuation_line:
265+
((toknum == STRING and not is_docstring) or toknum == NUMBER)
266+
and not on_continuation_line
267+
and fstring_depth == 0
268+
):
249269
random_name = self.generate_new_name()
250270
new_line_buffer.extend(
251271
[

pof/obfuscator/junk/add_newlines.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
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-
# TODO (deoktr): add parameter to disable spacing imports
1817
import random
1918
from tokenize import DEDENT, INDENT, NEWLINE
2019

@@ -35,7 +34,6 @@ def obfuscate_tokens(self, tokens):
3534
new_tokens.extend([(NEWLINE, "\n")])
3635

3736
else:
38-
# FIXME (deoktr): add newlines BEFORE class/function decorators
3937
next_non_indent_tokval = None
4038
for i in range(index, len(tokens)):
4139
tn, tv, *__ = tokens[i]

pof/obfuscator/numbers.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,19 @@
1616

1717
import random
1818
from enum import Enum
19-
from tokenize import COMMA, LPAR, NAME, NUMBER, OP, PLUS, RPAR, STRING, untokenize
19+
from tokenize import (
20+
COMMA,
21+
FSTRING_END,
22+
FSTRING_START,
23+
LPAR,
24+
NAME,
25+
NUMBER,
26+
OP,
27+
PLUS,
28+
RPAR,
29+
STRING,
30+
untokenize,
31+
)
2032

2133
from pof.errors import PofError
2234
from pof.logger import logger
@@ -274,10 +286,16 @@ def obfuscate_number(self, toknum, tokval): # noqa: C901 PLR0912
274286

275287
def obfuscate_tokens(self, tokens):
276288
result = []
289+
fstring_depth = 0
277290
for toknum, tokval, *_ in tokens:
278291
new_tokens = [(toknum, tokval)]
279292

280-
if toknum == NUMBER:
293+
if toknum == FSTRING_START:
294+
fstring_depth += 1
295+
elif toknum == FSTRING_END:
296+
fstring_depth -= 1
297+
298+
if toknum == NUMBER and fstring_depth == 0:
281299
new_tokens = self.obfuscate_number(toknum, tokval)
282300

283301
if new_tokens:

pof/obfuscator/other/tokens.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,17 @@
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 tokenize import LPAR, NAME, NEWLINE, NUMBER, OP, RPAR, STRING
17+
from tokenize import (
18+
FSTRING_END,
19+
FSTRING_START,
20+
LPAR,
21+
NAME,
22+
NEWLINE,
23+
NUMBER,
24+
OP,
25+
RPAR,
26+
STRING,
27+
)
1828

1929

2030
class TokensObfuscator:
@@ -33,7 +43,33 @@ def import_tokens():
3343
def generate_tokens_list(tokens):
3444
tokens_list = []
3545
tokens_list.append((OP, "["))
46+
fstring_depth = 0
47+
fstring_parts: list[str] = []
3648
for toknum, tokval, *_ in tokens:
49+
if toknum == FSTRING_START:
50+
fstring_depth += 1
51+
fstring_parts = [tokval]
52+
continue
53+
if fstring_depth > 0:
54+
if toknum == FSTRING_END:
55+
fstring_depth -= 1
56+
if fstring_depth == 0:
57+
fstring_parts.append(tokval)
58+
merged = "".join(fstring_parts)
59+
tokens_list.extend(
60+
[
61+
(LPAR, "("),
62+
(NUMBER, str(STRING)),
63+
(OP, ","),
64+
(STRING, repr(merged)),
65+
(RPAR, ")"),
66+
(OP, ","),
67+
],
68+
)
69+
fstring_parts = []
70+
continue
71+
fstring_parts.append(tokval)
72+
continue
3773
tokens_list.extend(
3874
[
3975
(LPAR, "("),

pof/obfuscator/remove/print.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,12 @@ def obfuscate_tokens(tokens):
4646
for toknum, tokval, *_ in tokens:
4747
new_tokens = [(toknum, tokval)]
4848

49-
if not inside_print and toknum == NAME and tokval == "print":
49+
if (
50+
not inside_print
51+
and toknum == NAME
52+
and tokval == "print"
53+
and prev_tokval not in ("def", ".")
54+
):
5055
new_tokens = None
5156
inside_print = True
5257
print_par_depth = parenthesis_depth

0 commit comments

Comments
 (0)