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