reverse-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
484
reverse-engineering-obfuscation/SKILL.md
Normal file
484
reverse-engineering-obfuscation/SKILL.md
Normal file
@@ -0,0 +1,484 @@
|
||||
---
|
||||
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 <string>, 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 — обход защит`
|
||||
Reference in New Issue
Block a user