1+ import random
2+ import string
3+ import ast
4+ import re
5+ import argparse
6+
7+ NO_OBFUSCATE = set ([ #this is here because I couldn't fix these
8+ 'os' , 'sys' , 'random' , 'string' , 'ast' , 're' , 'open' , 'print' , 'exit' , #you could probably get rid of some of them
9+ 'getattr' , 'setattr' , 'delattr' , 'hasattr' , 'int' , 'str' , 'list' , 'dict' ,
10+ 'tuple' , 'range' , 'len' , 'type' , 'isinstance' , 'dir' , 'id' , 'input' ,
11+ 'sum' , 'min' , 'max' , 'map' , 'filter' , 'sorted' , 'enumerate' , 'zip' , '_' ,
12+ 'path' , '__init__' , '__main__'
13+ ])
14+
15+ def add_module_functions (module_name , no_obfuscate ):
16+ try :
17+ module = __import__ (module_name )
18+ for name in dir (module ):
19+ no_obfuscate .add (name )
20+ if '.' in name :
21+ submodule = name .split ('.' )[0 ]
22+ no_obfuscate .add (submodule )
23+ except ImportError :
24+ pass
25+
26+ def collect_imported_modules (filename ):
27+ with open (filename , 'r' ) as f :
28+ source_code = f .read ()
29+
30+ tree = ast .parse (source_code )
31+ imported_modules = set ()
32+
33+ for node in ast .walk (tree ):
34+ if isinstance (node , (ast .Import , ast .ImportFrom )):
35+ for alias in node .names :
36+ imported_modules .add (alias .name if isinstance (node , ast .Import ) else node .module )
37+ if isinstance (node , ast .ImportFrom ):
38+ imported_modules .add (alias .name )
39+
40+ return imported_modules
41+
42+ def generate_random_name (length ):
43+ return '' .join (random .choices (string .ascii_lowercase , k = length ))
44+
45+ def collect_names (node , name_map , imported_modules , imported_functions_classes , no_obfuscate , length ):
46+ builtins = dir (__builtins__ )
47+
48+ if isinstance (node , ast .FunctionDef ):
49+ if node .name not in builtins and node .name not in imported_functions_classes and node .name not in no_obfuscate :
50+ name_map [node .name ] = generate_random_name (length )
51+
52+ elif isinstance (node , ast .Name ):
53+ if node .id not in builtins and node .id not in imported_modules and node .id not in imported_functions_classes and node .id not in no_obfuscate :
54+ if node .id not in name_map :
55+ name_map [node .id ] = generate_random_name (length )
56+
57+ for child in ast .iter_child_nodes (node ):
58+ collect_names (child , name_map , imported_modules , imported_functions_classes , no_obfuscate , length )
59+
60+ def obfuscate_and_replace (file_path , length , output_file ):
61+ name_map = {}
62+ imported_modules = collect_imported_modules (file_path )
63+
64+ for module in imported_modules :
65+ add_module_functions (module , NO_OBFUSCATE )
66+
67+ with open (file_path , 'r' ) as f :
68+ source_code = f .read ()
69+
70+ tree = ast .parse (source_code )
71+
72+ imported_functions_classes = {alias .name for node in ast .walk (tree ) if isinstance (node , ast .ImportFrom ) for alias in node .names }
73+
74+ collect_names (tree , name_map , imported_modules , imported_functions_classes , NO_OBFUSCATE , length )
75+
76+ def replace_match (match ):
77+ name = match .group (0 )
78+ return name_map .get (name , name )
79+
80+ pattern = r'\b(?:' + '|' .join (re .escape (k ) for k in name_map .keys ()) + r')\b'
81+ updated_code = re .sub (pattern , replace_match , source_code )
82+
83+ with open (output_file , 'w' ) as file :
84+ file .write (updated_code )
85+ print (f"File has been obfuscated successfully" )
86+
87+ def main ():
88+ parser = argparse .ArgumentParser (description = "Python Code Obfuscator" , epilog = "by stigsec" )
89+ parser .add_argument ('-i' , '--input' , required = True , help = "Input Python code" )
90+ parser .add_argument ('-l' , '--length' , type = int , default = 16 , help = "Length of obfuscated names" )
91+ parser .add_argument ('-o' , '--output' , required = True , help = "Output file" )
92+
93+ args = parser .parse_args ()
94+
95+ if args .length <= 3 : #if less than 3, weird problems occur
96+ print ("Error: Length must be greater than 3." )
97+ return
98+
99+ obfuscate_and_replace (args .input , args .length , args .output )
100+
101+ if __name__ == "__main__" :
102+ main ()
0 commit comments