1818
1919import io
2020import 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
4123from pof .logger import logger
4224from pof .obfuscator import (
4325 AddCommentsObfuscator ,
26+ AddTypeHintsObfuscator ,
4427 BooleanObfuscator ,
4528 BuiltinsObfuscator ,
4629 CommentsObfuscator ,
4730 ConstantsObfuscator ,
31+ DeadCodeObfuscator ,
4832 DocstringObfuscator ,
4933 ExceptionObfuscator ,
5034 GlobalsObfuscator ,
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
6243from pof .utils .extract_names import NameExtract
6344from pof .utils .generator import AdvancedGenerator , BaseGenerator , BasicGenerator
6445from pof .utils .tokens import untokenize
@@ -82,53 +63,84 @@ def _untokenize(tokens):
8263
8364
8465class 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