--- name: reverse-engineering-obfuscation description: Use for deobfuscation, unpacking, anti-analysis bypass, code virtualization reversing, control flow flattening recovery, string deobfuscation, and any task involving removal or analysis of code protection mechanisms. version: 1.0.0 platforms: - linux - macos - windows triggers: - reverse - engineering - obfuscation - deobfuscation - unpacking - anti - analysis - bypass - code - virtualization - reversing - control depends_on: [] metadata: tags: - reverse - engineering - obfuscation --- # Reverse Engineering — Obfuscation & Anti-Analysis ## Trigger Conditions - Deobfuscating obfuscated code (JavaScript, Python, Java, .NET, native) - Unpacking protected binaries (UPX, custom packers, protectors) - Bypassing anti-analysis techniques (anti-debug, anti-VM, anti-sandbox) - Reversing code virtualization (VMProtect, Themida, Oreans) - Recovering control flow from flattened code - String deobfuscation and decryption - Analyzing polymorphic/metamorphic code - Bypassing integrity checks and self-modification - Deobfuscating malware or protected commercial software - Analyzing and bypassing code tampering protection - .NET deobfuscation (de4dot, dnlib) - JavaScript deobfuscation (eval chains, string arrays, control flow) ## Core Methodology ### 1. Packer Identification **Common packers:** ``` UPX — Universal Packer for Executables Signature: "UPX!" in section names Unpack: upx -d file.exe ASPack — Advanced SCS Packer Signature: ".aspack", ".adata" sections Unpack: manual dump at OEP Themida/WinLicense — Oreans Technologies Signature: heavy code virtualization, API redirection Unpack: extremely difficult, requires VM analysis VMProtect — VMP Signature: "VMProtect" strings, virtualized code Unpack: requires VM handler analysis Armadillo — Silicon Realms Signature: Nanomite debugging, code morphing Unpack: debug child process PECompact, PELock, ExeStealth Various signatures, custom unpacking needed ``` **Detection:** ```bash # Detect It Easy (DIE) — comprehensive packer detection diec file.exe # PEiD — legacy but useful peid file.exe # Exeinfo PE — another detector exeinfope file.exe # Manual: check section characteristics # Packed binaries have: # - Single executable section (no .rdata, .data separation) # - High entropy (>7.0) in code section # - Unusual section names # - Small import table ``` ### 2. Unpacking Techniques **Manual unpacking (generic):** ``` 1. Find OEP (Original Entry Point) - ESP law: set hardware breakpoint on ESP, run, step until return - Section breakpoint: set BP on first instruction of unpacked section - Memory breakpoint: set BP on write to original code section - API breakpoint: set BP on GetProcAddress/LoadLibrary 2. Dump process memory - Use Scylla, Process Dump, or custom dumper - Dump at OEP, after unpacker decompressed everything 3. Fix IAT (Import Address Table) - Scylla can rebuild IAT automatically - Manual: trace IAT reconstruction in unpacker - Identify IAT thunk addresses and original API names 4. Rebuild PE headers - Fix entry point, section table, import directory - Test with ImpREC or Scylla ``` **UPX unpacking:** ```bash # Automatic upx -d packed.exe -o unpacked.exe # Manual (if modified UPX): # 1. Find UPX1 section end (jmp to OEP) # 2. Set breakpoint on the jump # 3. Dump after jump executes # 4. Fix IAT with Scylla ``` **Custom packer unpacking:** ```python # Memory dumper — dump after unpacker runs import psutil import pefile def dump_process(pid, base_address, size, output_path): """Dump memory region from running process.""" proc = psutil.Process(pid) # Use platform-specific memory reading # Windows: ReadProcessMemory via ctypes # Linux: /proc/pid/mem with open(output_path, 'wb') as f: f.read(...) # IAT reconstruction def reconstruct_iat(dump_path, iat_start, iat_end): """Rebuild import table from dumped binary.""" pe = pefile.PE(dump_path) # Scan IAT range for valid function pointers # Resolve to module + function name # Rebuild import directory ``` ### 3. Anti-Debug Bypass **Detection → Bypass mapping:** ``` Detection Method | Bypass ──────────────────────────────|──────────────────────────── IsDebuggerPresent | Patch to return 0 CheckRemoteDebuggerPresent | Hook to return FALSE NtQueryInfoProcess(DebugPort) | Hook NtQueryInformationProcess GetTickCount timing | Hook GetTickCount, return realistic values RDTSC timing | Patch RDTSC or hook CPUID SEH debugger detection | Patch exception handler Hardware breakpoint detection | Clear DR0-DR7 registers Parent process check | Spoof parent PID Debug object handle | Close debug object Window class check | Rename debugger window ``` **Frida — comprehensive anti-debug bypass:** ```javascript // Anti-debug bypass suite function bypassAntiDebug() { // IsDebuggerPresent var isDebuggerPresent = Module.findExportByName('kernel32.dll', 'IsDebuggerPresent'); if (isDebuggerPresent) { Interceptor.replace(isDebuggerPresent, new NativeCallback(function() { return 0; }, 'int', [])); } // CheckRemoteDebuggerPresent var checkRemote = Module.findExportByName('kernel32.dll', 'CheckRemoteDebuggerPresent'); if (checkRemote) { Interceptor.attach(checkRemote, { onEnter: function(args) { this.hdbg = args[1]; }, onLeave: function(retval) { this.hdbg.writeU8(0); // not being debugged retval.replace(1); // function succeeded } }); } // NtQueryInformationProcess var ntQuery = Module.findExportByName('ntdll.dll', 'NtQueryInformationProcess'); if (ntQuery) { Interceptor.attach(ntQuery, { onEnter: function(args) { this.processInfoClass = args[1].toInt32(); this.processInfo = args[2]; }, onLeave: function(retval) { // ProcessDebugPort = 7 if (this.processInfoClass === 7) { this.processInfo.writePointer(ptr(0)); } // ProcessDebugFlags = 0x1E if (this.processInfoClass === 0x1E) { this.processInfo.writeU8(1); } // ProcessDebugObjectHandle = 0x1E if (this.processInfoClass === 30) { this.processInfo.writePointer(ptr(0)); retval.replace(0xC0000353); // STATUS_PORT_NOT_SET } } }); } } ``` ### 4. String Deobfuscation **Common string obfuscation patterns:** **XOR encrypted strings:** ```python def xor_decrypt(data, key): if isinstance(key, int): return bytes([b ^ key for b in data]) return bytes([b ^ key[i % len(key)] for i, b in enumerate(data)]) # Find XOR key by known plaintext known = b"GetProcAddress" cipher = bytes_from_binary(offset, len(known)) key = bytes([p ^ c for p, c in zip(known, cipher)]) ``` **String array + index (JavaScript):** ```javascript // Obfuscated: var _0x1234 = ['hello', 'world', 'test']; function _0x5678(i) { return _0x1234[i]; } console.log(_0x5678(0) + ' ' + _0x5678(1)); // Deobfuscation: replace calls with actual strings // AST-based approach: // 1. Parse JS to AST // 2. Find array declaration // 3. Find accessor function // 4. Replace all calls with literal values ``` **.NET string decryption:** ```csharp // Many .NET obfuscators encrypt strings and decrypt at runtime // Find the decryption method, emulate it using dnlib.DotNet; using dnlib.DotNet.Emit; // Load assembly var mod = ModuleDefMD.Load("obfuscated.exe"); // Find string decryption method // Usually called before string usage // Pattern: ldtoken , call decrypt_method // Emulate or hook the decryption // de4dot handles many common obfuscators automatically ``` ### 5. Control Flow Flattening Recovery **What it looks like:** ```c // Original: if (condition) { block_a(); } else { block_b(); } block_c(); // Flattened: state = INITIAL; while (state != EXIT) { switch (state) { case INITIAL: if (condition) state = STATE_A; else state = STATE_B; break; case STATE_A: block_a(); state = STATE_C; break; case STATE_B: block_b(); state = STATE_C; break; case STATE_C: block_c(); state = EXIT; break; } } ``` **Recovery approaches:** ``` 1. Symbolic execution (Angr, Triton) - Track state variable - Build CFG from state transitions - Reconstruct original flow 2. Dynamic tracing - Log state values during execution - Build execution trace - Reconstruct CFG from trace 3. Static analysis - Identify dispatcher (switch statement) - Map state values to basic blocks - Rebuild CFG by analyzing state assignments 4. IDA plugin — deflat - Automates dispatcher identification - Rebuilds CFG - Patches binary to remove flattening ``` **Angr — symbolic deobfuscation:** ```python import angr # Load binary proj = angr.Project('obfuscated_binary', auto_load_libs=False) # Find the dispatcher # Set up symbolic execution state = proj.factory.entry_state() simgr = proj.factory.simulation_manager(state) # Explore to find all reachable states simgr.explore(find=lambda s: s.solver.is_true(s.regs.rip == exit_addr)) # Analyze paths to reconstruct CFG for path in simgr.found: print(path.history.bbl_addrs) ``` ### 6. Code Virtualization Reversing **VMProtect/Themida analysis:** ``` Virtualized code structure: 1. VM entry point (transition from native to VM) 2. VM bytecode (custom instruction set) 3. VM handler (interprets bytecode) 4. VM exit (return to native code) Analysis approach: 1. Identify VM entry/exit points 2. Dump VM bytecode 3. Analyze VM handler to understand instruction set 4. Write VM bytecode disassembler 5. Reconstruct original logic from VM bytecode ``` **VM handler analysis:** ```c // Typical VM dispatcher: void vm_dispatcher(vm_context* ctx) { while (ctx->ip < ctx->code_end) { uint8_t opcode = *ctx->ip++; switch (opcode) { case 0x01: vm_push(ctx); break; case 0x02: vm_pop(ctx); break; case 0x03: vm_add(ctx); break; // ... 200+ handlers } } } // Extract handler table: // Find the switch statement in IDA // Each case → handler function // Document handler behavior // Build bytecode disassembler ``` ### 7. Integrity Check Bypass **Common integrity checks:** ``` 1. Checksum of code section - Compute CRC32/MD5 of .text section - Compare with stored value - Bypass: patch comparison or stored checksum 2. Self-debugging - Process debugs itself to detect tampering - Bypass: hook debugging APIs 3. Code signing verification - Verify Authenticode signature - Bypass: patch verification function 4. Size/alignment checks - Check section sizes, alignment - Bypass: maintain original sizes 5. API hook detection - Check if API functions are hooked - Bypass: use direct syscalls ``` ### 8. Automation Tools **Generic deobfuscation framework:** ```python class Deobfuscator: def __init__(self, binary_path): self.binary = open(binary_path, 'rb').read() self.pe = pefile.PE(binary_path) def find_string_references(self, target_string): """Find all references to a string in the binary.""" offsets = [] pos = 0 while True: pos = self.binary.find(target_string.encode(), pos) if pos == -1: break offsets.append(pos) pos += 1 return offsets def extract_xor_strings(self, key): """Extract XOR-encrypted strings with known key.""" results = [] # Scan data sections for XOR patterns for section in self.pe.sections: data = section.get_data() for i in range(len(data)): decrypted = xor_decrypt(data[i:i+32], key) if decrypted.isascii() and len(decrypted) > 4: results.append((section.Name, i, decrypted)) return results ``` ## Implementation Standards - All deobfuscation produces working, readable output - Provide both automated and manual approaches - Include scripts for repetitive deobfuscation tasks - Document obfuscation techniques identified - No partial deobfuscation — complete or nothing ## Output Format For obfuscation RE tasks: 1. **Protection Identification** — packer, protector, obfuscator type 2. **Obfuscation Techniques** — what methods are used 3. **Deobfuscation Process** — step-by-step removal 4. **Recovered Code** — deobfuscated, readable output 5. **Bypass Methods** — working patches, scripts, tools 6. **Automation** — scripts for future deobfuscation Всё, брат. ## Related - `skill reverse-engineering-binary — бинарный RE` - `skill reverse-engineering-full — полный RE` - `skill binary-exploitation — обход защит`