Skip to content

Commit ff2e08a

Browse files
committed
update: change default obfuscation preset
1 parent 261fc7e commit ff2e08a

5 files changed

Lines changed: 104 additions & 214 deletions

File tree

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,15 @@ cat in.py | docker run --rm -i ghcr.io/deoktr/python-obfuscation-framework:lates
220220

221221
## Usage
222222

223+
You can select the obfuscation level with the `-f` flag, here are the levels:
224+
225+
- basic
226+
- moderate
227+
- advanced
228+
- extreme
229+
230+
Or you can specify and use a single obfuscator.
231+
223232
```bash
224233
# pipe input and output to stdout
225234
echo "print('Hello, world')" | pof
@@ -233,6 +242,12 @@ pof in.py > out.py
233242
# pipe to python to run it
234243
pof in.py | python
235244

245+
# obfuscator preset, choose the level of obfuscation needed
246+
pof in.py -o out.py -f basic
247+
pof in.py -o out.py -f moderate
248+
pof in.py -o out.py -f advanced
249+
pof in.py -o out.py -f extreme
250+
236251
# obfuscator
237252
pof in.py -o out.py -f obfuscator -k BuiltinsObfuscator
238253

pof/cli.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,8 @@ def _cli() -> int:
176176
parser.add_argument(
177177
"-f",
178178
"--function",
179-
help="obfuscation function",
180-
default="obfuscate",
179+
help="obfuscation function, can be a specific function or preset: basic/moderate/advanced/extreme",
180+
default="moderate",
181181
)
182182
parser.add_argument(
183183
"--format",

pof/main.py

Lines changed: 79 additions & 208 deletions
Original file line numberDiff line numberDiff line change
@@ -18,33 +18,17 @@
1818

1919
import io
2020
import random
21-
from datetime import datetime, timedelta
22-
from tokenize import COMMENT, NEWLINE, generate_tokens
21+
from tokenize import generate_tokens
2322

24-
from pof.evasion import (
25-
CPUCountEvasion,
26-
DebuggerEvasion,
27-
DomainEvasion,
28-
ExecPathEvasion,
29-
ExpireEvasion,
30-
FileExistEvasion,
31-
FileListExistEvasion,
32-
FileListMissingEvasion,
33-
FileMissingEvasion,
34-
HostnameEvasion,
35-
LinuxRAMCountEvasion,
36-
LinuxUIDEvasion,
37-
TracemallocEvasion,
38-
UsernameEvasion,
39-
)
40-
from pof.evasion.utils import FILE_SYSTEM
4123
from pof.logger import logger
4224
from pof.obfuscator import (
4325
AddCommentsObfuscator,
26+
AddTypeHintsObfuscator,
4427
BooleanObfuscator,
4528
BuiltinsObfuscator,
4629
CommentsObfuscator,
4730
ConstantsObfuscator,
31+
DeadCodeObfuscator,
4832
DocstringObfuscator,
4933
ExceptionObfuscator,
5034
GlobalsObfuscator,
@@ -53,12 +37,9 @@
5337
NamesObfuscator,
5438
NewlineObfuscator,
5539
NumberObfuscator,
56-
PrintObfuscator,
5740
StringsObfuscator,
58-
XORObfuscator,
41+
TypeHintsObfuscator,
5942
)
60-
from pof.stager import ImageStager, RC4Stager
61-
from pof.utils.cipher import RC4Cipher
6243
from pof.utils.extract_names import NameExtract
6344
from pof.utils.generator import AdvancedGenerator, BaseGenerator, BasicGenerator
6445
from pof.utils.tokens import untokenize
@@ -82,53 +63,84 @@ def _untokenize(tokens):
8263

8364

8465
class Obfuscator(BaseObfuscator):
85-
def obfuscate(
86-
self,
87-
source,
88-
*,
89-
remove_logs: bool = False,
90-
remove_prints: bool = False,
91-
remove_exceptions: bool = False,
92-
):
93-
"""Complete chained obfuscation."""
66+
def basic(self, source):
67+
"""Just the bare minimum obfuscation."""
68+
tokens = self._get_tokens(source)
69+
tokens = CommentsObfuscator().obfuscate_tokens(tokens)
70+
generator = BasicGenerator.alphabet_generator()
71+
tokens = NamesObfuscator(generator=generator).obfuscate_tokens(tokens)
72+
return self._untokenize(tokens)
73+
74+
def moderate(self, source):
75+
tokens = self._get_tokens(source)
76+
tokens = CommentsObfuscator().obfuscate_tokens(tokens)
77+
tokens = TypeHintsObfuscator().obfuscate_tokens(tokens)
78+
generator = BasicGenerator.alphabet_generator()
79+
tokens = NamesObfuscator(generator=generator).obfuscate_tokens(tokens)
80+
tokens = NumberObfuscator().obfuscate_tokens(tokens)
81+
tokens = BooleanObfuscator().obfuscate_tokens(tokens)
82+
tokens = StringsObfuscator().obfuscate_tokens(tokens)
83+
tokens = IndentsObfuscator().obfuscate_tokens(tokens)
84+
tokens = NewlineObfuscator().obfuscate_tokens(tokens)
85+
return self._untokenize(tokens)
86+
87+
def advanced(self, source):
9488
tokens = self._get_tokens(source)
9589

96-
# get all the names and add them to the RESERVED_WORDS for the
97-
# generators
90+
# do not generate any names that was present in source
9891
reserved_words_add = NameExtract.get_names(tokens)
9992
BaseGenerator.extend_reserved(reserved_words_add)
10093

101-
msg = f"reserved {len(tokens)} names"
102-
logger.info(msg)
103-
104-
# clean input
10594
tokens = CommentsObfuscator().obfuscate_tokens(tokens)
106-
if remove_logs:
107-
# doesn't work yet !
108-
tokens = LoggingObfuscator().obfuscate_tokens(tokens)
95+
tokens = TypeHintsObfuscator().obfuscate_tokens(tokens)
96+
97+
generator = AdvancedGenerator.multi_generator(
98+
{
99+
86: AdvancedGenerator.realistic_generator(),
100+
10: BasicGenerator.alphabet_generator(),
101+
4: BasicGenerator.number_name_generator(length=random.randint(2, 5)),
102+
},
103+
)
109104

110-
if remove_prints:
111-
# not 100% safe !
112-
tokens = PrintObfuscator().obfuscate_tokens(tokens)
105+
tokens = DeadCodeObfuscator(generator=generator).obfuscate_tokens(tokens)
106+
tokens = AddTypeHintsObfuscator().obfuscate_tokens(tokens)
107+
tokens = NamesObfuscator(generator=generator).obfuscate_tokens(tokens)
108+
tokens = NumberObfuscator().obfuscate_tokens(tokens)
109+
tokens = BooleanObfuscator().obfuscate_tokens(tokens)
110+
tokens = StringsObfuscator().obfuscate_tokens(tokens)
111+
tokens = IndentsObfuscator().obfuscate_tokens(tokens)
112+
tokens = NewlineObfuscator().obfuscate_tokens(tokens)
113+
return self._untokenize(tokens)
114+
115+
def extreme(self, source):
116+
"""Complete chained obfuscation."""
117+
tokens = self._get_tokens(source)
118+
119+
# do not generate any names that was present in source
120+
reserved_words_add = NameExtract.get_names(tokens)
121+
BaseGenerator.extend_reserved(reserved_words_add)
113122

114-
if remove_exceptions:
115-
# not fully tested
116-
ex_generator = (
117-
BasicGenerator.number_name_generator()
118-
) # TODO (deoktr): have multiple generator !!!
119-
tokens = ExceptionObfuscator(
120-
add_codes=True,
121-
generator=ex_generator,
122-
).obfuscate_tokens(tokens)
123+
# clean input
124+
tokens = CommentsObfuscator().obfuscate_tokens(tokens)
125+
tokens = TypeHintsObfuscator().obfuscate_tokens(tokens)
123126

124127
# configure generator
125-
# generator = alphabet_generator()
126-
gen_dict = {
127-
86: AdvancedGenerator.realistic_generator(),
128-
10: BasicGenerator.alphabet_generator(),
129-
4: BasicGenerator.number_name_generator(length=random.randint(2, 5)),
130-
}
131-
generator = AdvancedGenerator.multi_generator(gen_dict)
128+
generator = AdvancedGenerator.multi_generator(
129+
{
130+
86: AdvancedGenerator.realistic_generator(),
131+
10: BasicGenerator.alphabet_generator(),
132+
4: BasicGenerator.number_name_generator(length=random.randint(2, 5)),
133+
},
134+
)
135+
136+
# add trash
137+
tokens = DeadCodeObfuscator(
138+
max_function_depth=3,
139+
max_branches=5,
140+
generate_classes=True,
141+
generator=generator,
142+
).obfuscate_tokens(tokens)
143+
tokens = AddTypeHintsObfuscator().obfuscate_tokens(tokens)
132144

133145
# core obfuscation
134146
tokens = ConstantsObfuscator(
@@ -160,106 +172,15 @@ def obfuscate(
160172

161173
for _ in range(2):
162174
tokens = NumberObfuscator().obfuscate_tokens(tokens)
163-
164-
tokens = BuiltinsObfuscator().obfuscate_tokens(tokens)
165-
166-
for _ in range(2):
167175
tokens = string_obfuscator.obfuscate_tokens(tokens)
168-
169-
# TODO (deoktr): enable once fully tested
170-
# for _ in range(2):
171-
# tokens = BooleanObfuscator().obfuscate_tokens(tokens)
176+
tokens = BooleanObfuscator().obfuscate_tokens(tokens)
172177

173178
tokens = AddCommentsObfuscator().obfuscate_tokens(tokens)
174179

175180
# clean output
176181
tokens = IndentsObfuscator().obfuscate_tokens(tokens)
177182
tokens = NewlineObfuscator().obfuscate_tokens(tokens)
178183

179-
xor_payload = False
180-
if xor_payload:
181-
tokens = XORObfuscator().obfuscate_tokens(tokens)
182-
generator = BasicGenerator.alphabet_generator()
183-
tokens = NamesObfuscator(generator=generator).obfuscate_tokens(tokens)
184-
185-
docstring_payload = False
186-
if docstring_payload:
187-
tokens = DocstringObfuscator().obfuscate_tokensj(tokens)
188-
generator = BasicGenerator.alphabet_generator()
189-
tokens = NamesObfuscator(generator=generator).obfuscate_tokens(tokens)
190-
tokens = BuiltinsObfuscator().obfuscate_tokens(tokens)
191-
tokens = StringsObfuscator(
192-
import_b64decode=True,
193-
import_b85decode=True,
194-
b64decode_name=b64decode_name,
195-
b85decode_name=b85decode_name,
196-
).obfuscate_tokens(tokens)
197-
198-
return self._untokenize(tokens)
199-
200-
def basic(self, source):
201-
"""Just the bare minimum obfuscation."""
202-
tokens = self._get_tokens(source)
203-
tokens = CommentsObfuscator().obfuscate_tokens(tokens)
204-
generator = BasicGenerator.alphabet_generator()
205-
tokens = NamesObfuscator(generator=generator).obfuscate_tokens(tokens)
206-
tokens = IndentsObfuscator().obfuscate_tokens(tokens)
207-
tokens = NewlineObfuscator().obfuscate_tokens(tokens)
208-
return self._untokenize(tokens)
209-
210-
def full_evasion( # noqa: C901, PLR0913, PLR0912
211-
self,
212-
source,
213-
hostname: str | None = None,
214-
username: str | None = None,
215-
uid=None,
216-
domain: str | None = None,
217-
file_exist=None,
218-
file_missing=None,
219-
min_cpu_count: int | None = None,
220-
min_ram: int | None = None,
221-
file_list_exist=None,
222-
file_list_missing=None,
223-
*,
224-
expire: bool = False,
225-
check_tracemalloc: bool = False,
226-
check_debugger: bool = False,
227-
check_executable_path: bool = False,
228-
):
229-
tokens = self._get_tokens(source)
230-
231-
if file_missing:
232-
tokens = FileMissingEvasion(file=file_missing).add_evasion(tokens)
233-
if min_cpu_count:
234-
tokens = CPUCountEvasion(min_cpu_count=min_cpu_count).add_evasion(tokens)
235-
if min_ram:
236-
tokens = LinuxRAMCountEvasion(min_ram=min_ram).add_evasion(tokens)
237-
if check_tracemalloc:
238-
tokens = TracemallocEvasion().add_evasion(tokens)
239-
if file_list_exist:
240-
tokens = FileListMissingEvasion(file_list=FILE_SYSTEM).add_evasion(tokens)
241-
if file_exist:
242-
tokens = FileExistEvasion(file=file_exist).add_evasion(tokens)
243-
if file_list_missing:
244-
# TODO (deoktr): remove
245-
idk = ["/tmp/a", "/tmp/b"] # noqa: S108
246-
tokens = FileListExistEvasion(file_list=idk).add_evasion(tokens)
247-
if domain:
248-
tokens = DomainEvasion(domain=domain).add_evasion(tokens)
249-
if hostname:
250-
tokens = HostnameEvasion(hostname=hostname).add_evasion(tokens)
251-
if uid:
252-
tokens = LinuxUIDEvasion(uid=uid).add_evasion(tokens)
253-
if username:
254-
tokens = UsernameEvasion(username=username).add_evasion(tokens)
255-
if expire:
256-
under_datetime = datetime.utcnow() + timedelta(seconds=5) # noqa: DTZ003
257-
tokens = ExpireEvasion(under_datetime).add_evasion(tokens)
258-
if check_executable_path:
259-
tokens = ExecPathEvasion().add_evasion(tokens)
260-
if check_debugger:
261-
tokens = DebuggerEvasion().add_evasion(tokens)
262-
263184
return self._untokenize(tokens)
264185

265186
def circles(self, source):
@@ -277,66 +198,16 @@ def circles(self, source):
277198
).obfuscate_tokens(tokens)
278199
tokens = IndentsObfuscator().obfuscate_tokens(tokens)
279200
tokens = NewlineObfuscator().obfuscate_tokens(tokens)
280-
281-
tokens = [(COMMENT, "# I love circles <3"), (NEWLINE, "\n"), *tokens]
282-
283-
return self._untokenize(tokens)
284-
285-
def image(self, source):
286-
"""Encrypt and store the payload code inside an image."""
287-
tokens = self._get_tokens(source)
288-
img_in = "pof/wip/stegano/i.png"
289-
encoding_class = RC4Cipher()
290-
tokens = ImageStager(encoding_class=encoding_class).generate_stager(
291-
tokens,
292-
img_in,
293-
)
294201
return self._untokenize(tokens)
295202

296-
def advanced_image(self, source):
297-
"""Obfuscate, encrypt, and store the payload code inside an image."""
203+
def docstring(self, source):
298204
tokens = self._get_tokens(source)
205+
tokens = DocstringObfuscator().obfuscate_tokens(tokens)
299206
generator = BasicGenerator.alphabet_generator()
300-
301-
# obfuscate source
302207
tokens = NamesObfuscator(generator=generator).obfuscate_tokens(tokens)
303-
tokens = IndentsObfuscator().obfuscate_tokens(tokens)
304-
305-
# stage inside image
306-
img_in = "pof/wip/stegano/i.png"
307-
encoding_class = RC4Cipher()
308-
tokens = ImageStager(encoding_class=encoding_class).generate_stager(
309-
tokens,
310-
img_in,
311-
)
312-
313-
# FIXME (deoktr): break the image stager
314-
# obfuscate stager
315-
# tokens = NamesObfuscator(generator=generator).obfuscate_tokens(tokens)
316-
# tokens = IndentsObfuscator().obfuscate_tokens(tokens)
317-
318-
# encrypt stager
319-
tokens = RC4Stager().generate_stager(tokens)
320-
321-
# FIXME (deoktr): break the decryption part because set 256 to a variable
322-
# obfuscate decryption stager
323-
# tokens = NamesObfuscator(generator=generator).obfuscate_tokens(tokens)
324-
# tokens = IndentsObfuscator().obfuscate_tokens(tokens)
325-
326-
return self._untokenize(tokens)
327-
328-
def test(self, source):
329-
tokens = self._get_tokens(source)
330-
331-
# generator = AdvancedGenerator.fixed_length_generator()
332-
# tokens = ConstantsObfuscator(generator=generator).obfuscate_tokens(tokens)
333-
334-
tokens = CommentsObfuscator().obfuscate_tokens(tokens)
335-
tokens = NamesObfuscator(
336-
generator=AdvancedGenerator.fixed_length_generator(),
208+
tokens = BuiltinsObfuscator().obfuscate_tokens(tokens)
209+
tokens = StringsObfuscator(
210+
import_b64decode=True,
211+
import_b85decode=True,
337212
).obfuscate_tokens(tokens)
338-
tokens = BooleanObfuscator().obfuscate_tokens(tokens)
339-
tokens = IndentsObfuscator().obfuscate_tokens(tokens)
340-
tokens = NewlineObfuscator().obfuscate_tokens(tokens)
341-
342213
return self._untokenize(tokens)

0 commit comments

Comments
 (0)