-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdeobfuscate.py
More file actions
198 lines (156 loc) · 7.86 KB
/
deobfuscate.py
File metadata and controls
198 lines (156 loc) · 7.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
import base64
import marshal
import zlib
import re
import sys
import os
# --- Decoder Functions (Reverted to the WORKING format) ---
def decoder_zlib_base64(s):
"""Decodes Base64 and decompresses Zlib data."""
# Rely on Base64 decoder to handle padding, which was successful in the working iteration.
data = base64.b64decode(s)
return zlib.decompress(data)
def decoder_base64(s):
"""Decodes Base64 data."""
# Rely on Base64 decoder to handle padding.
return base64.b64decode(s)
# --- Pattern Definitions (Highly Robust) ---
# Pattern 1: zlib.decompress(base64.b64decode('...')) - Uses non-greedy capture (.*?)
PATTERN_ZLIB_B64 = (
r"zlib\.decompress\s*\(\s*base64\.b64decode\s*\(\s*['\"](.*?)['\"]\s*\)\s*\)\s*"
)
# Pattern 2: base64.b64decode('...') - Uses non-greedy capture (.*?)
PATTERN_B64 = (
r"base64\.b64decode\s*\(\s*['\"](.*?)['\"]\s*\)\s*"
)
# Pattern 3: marshal.loads(b'...')
PATTERN_MARSHAL = r"marshal\.loads\s*\(\s*(b['\"])(.*?)\1\s*\)"
# Utility for final content check
OBFUSCATION_HEADERS = ['base64.b64decode', 'marshal.loads', 'zlib.decompress']
# List of all patterns, their decoder functions, and display names
DEOBFUSCATION_CHAIN = [
(PATTERN_ZLIB_B64, decoder_zlib_base64, "Zlib+Base64"),
(PATTERN_B64, decoder_base64, "Simple Base64"),
]
# --- Main Logic ---
def iterative_deobfuscate(content):
"""
Iteratively removes obfuscation layers until plain code or an unsupported format is reached.
Returns: The final decoded content (string or bytes) and the final obfuscation type.
"""
current_content = content
current_obf_type = "Initial"
layer = 1
while True:
original_content = current_content
found_match = False
# 1. Marshal.loads check (Skipped here for brevity, keeping the logic consistent with previous successful versions)
if 'marshal.loads' in current_content:
match = re.search(PATTERN_MARSHAL, current_content, re.DOTALL)
if match:
found_match = True
current_obf_type = "Marshal"
quote_type = match.group(1)
bytes_repr = match.group(2)
print(f"\n[+] Layer {layer}: Marshal layer detected.")
print(f"Debug: Marshal raw bytes captured (length: {len(bytes_repr)})")
try:
bytes_content = eval(quote_type + bytes_repr + quote_type[-1])
decoded = marshal.loads(bytes_content)
if isinstance(decoded, bytes):
print("[!] Marshal successful. Result is compiled bytecode.")
return decoded, f"{current_obf_type} (Bytecode)"
current_content = decoded.decode('utf-8') if isinstance(decoded, bytes) else str(decoded)
print(f"[+] Layer {layer} removed: Marshal (Source). New content length: {len(current_content)}")
layer += 1
continue
except Exception as e:
print(f"[!] Marshal decode failed: {e}")
return original_content, "Failed Marshal"
# 2. Iterate through other common patterns (Zlib/Base64)
for pattern, decoder_func, obf_name in DEOBFUSCATION_CHAIN:
print(f"Debug: Trying pattern: {obf_name}")
# Use re.search across the whole content (reverting back to the simple search that worked)
match = re.search(pattern, current_content, re.DOTALL)
if match:
found_match = True
current_obf_type = obf_name
encoded_str = match.group(1)
print(f"\n[+] Layer {layer}: {obf_name} detected.")
print(f"Debug: Encoded string captured (length: {len(encoded_str)})")
try:
decoded = decoder_func(encoded_str)
# Attempt to decode to utf-8
try:
current_content = decoded.decode('utf-8')
print(f"[+] Layer {layer} removed: {obf_name}. New content length: {len(current_content)}")
except UnicodeDecodeError:
current_content = decoded
print("[!] Decompressed data is not UTF-8 (likely next layer is bytecode/marshal).")
return current_content, f"{obf_name} (Bytes)"
layer += 1
# Success! Break out of for-loop to continue to next while-loop iteration
break
except Exception as e:
# Crucially: Log the failure, and revert the match flag.
# DO NOT return immediately. This allows the loop to check the next pattern
# and eventually finish the whole iterative process gracefully.
print(f"[!] {obf_name} decode failed: {e}. Skipping this pattern.")
found_match = False
# 3. Check if any decoding/pattern matching occurred in this iteration
if not found_match:
# No supported obfuscation pattern was found in this pass. Exit loop.
break
return current_content, current_obf_type
def main():
input_file = input("Enter the encoded file name (e.g., encoded.py): ").strip()
if not input_file:
print("Error: Input file name required.")
sys.exit(1)
try:
with open(input_file, 'r') as f:
initial_content = f.read()
print(f"Debug: File loaded (length: {len(initial_content)})")
except FileNotFoundError:
print(f"Error: {input_file} not found.")
sys.exit(1)
except Exception as e:
print(f"Read error: {e}")
sys.exit(1)
# --- Start the iterative process ---
final_content, final_type = iterative_deobfuscate(initial_content)
# --- Final Output & Saving ---
output_file = 'original_deobfuscated.py'
if isinstance(final_content, str):
# 1. Save the content
with open(output_file, 'w') as out:
out.write(final_content)
# 2. Determine final status: If the final_type doesn't say "Failed," assume success.
is_failure = "Failed" in final_type
# 3. Print result
print("\n=======================================================")
if is_failure:
print(f"[ERROR] Deobfuscation stopped due to decoding failure.")
else:
# This is the correct success message when the iterative loop completed naturally.
print(f"[SUCCESS] Deobfuscation Complete! All accessible layers removed.")
print(f"Final output saved to: {output_file}")
# Only show "Plain Source Code" if it wasn't a specific failure type
final_status_display = final_type if is_failure else "Plain Source Code"
print(f"Final content type: {final_status_display}")
print("=======================================================")
elif isinstance(final_content, bytes):
# Handle Bytecode (Marshal) output
bytecode_file = 'original_bytecode.pyc'
with open(bytecode_file, 'wb') as out:
out.write(final_content)
print("\n=======================================================")
print(f"[SUCCESS] Deobfuscation Complete, but stopped at Bytecode.")
print(f"Final data saved to: {bytecode_file}")
print(f"Final content type: {final_type}. A decompiler (e.g., uncompyle6) is required.")
print("=======================================================")
else:
print("\n[ERROR] Deobfuscation failed to find any supported pattern.")
print("File content remains unchanged.")
if __name__ == "__main__":
main()