Reverse Engineering
Purpose
Guide agents through reverse engineering binaries: Ghidra project setup and decompilation, radare2 analysis workflow, Binary Ninja scripting, initial triage with strings/file/xxd, identifying C++ patterns (vtables, RAII), analyzing stripped binaries, and diffing with Diaphora or BinDiff.
When to Use
- Analyzing an unknown binary without source code
- Recovering algorithm logic from compiled executables
- Comparing two firmware versions for vulnerability patches
- Understanding malware or CTF challenge binaries
- Recovering symbols from stripped ELF/PE files
- Automating analysis with Ghidra or r2 scripts
Workflow
1. Initial triage
file suspicious_binary
strings -n 8 suspicious_binary | head -50
strings -el suspicious_binary # UTF-16 LE
xxd suspicious_binary | head -20
readelf -h suspicious_binary # ELF
objdump -d -M intel suspicious_binary | head -40
# Check protections
checksec --file=suspicious_binary
| Command | Reveals |
|---|---|
file |
Architecture, static/dynamic, stripped |
strings |
URLs, paths, error messages, keys |
readelf -s |
Symbol table (if not stripped) |
nm -D |
Dynamic symbols |
checksec |
RELRO, NX, PIE, canary |
2. Ghidra workflow
# Headless analysis
analyzeHeadless /tmp/ghidra_projects MyProject \
-import suspicious_binary \
-postScript ExportDecompile.java
# GUI: File → New Project → Import File → Analyze (Yes)
Key steps:
- Auto-analysis — let Ghidra complete disassembly
- Define functions —
Fat entry points if missed - Decompiler — Window → Decompiler (C-like output)
- Rename —
Lon variables/functions for clarity - Cross-references —
Ctrl+Shift+Fon function/data
// Ghidra script (Java) — list functions > 100 bytes
import ghidra.program.model.listing.*;
FunctionManager fm = currentProgram.getFunctionManager();
for (Function f : fm.getFunctions(true)) {
if (f.getBody().getNumAddresses() > 100)
println(f.getName() + " @ " + f.getEntryPoint());
}
# Ghidra Python (Jython)
from ghidra.program.model.listing import FunctionManager
fm = currentProgram.getFunctionManager()
for f in fm.getFunctions(True):
print(f.getName(), f.getEntryPoint())
3. radare2 workflow
r2 suspicious_binary
[0x00001000]> aaa # analyze all
[0x00001000]> afl # list functions
[0x00001000]> pdf @ main # disassemble function
[0x00001000]> VV # visual graph mode
[0x00001000]> iz # strings in data sections
[0x00001000]> s sym.main; pdf
Patching:
[0x00001000]> wx 9090 @ 0x401234 # write NOPs
[0x00001000]> wci 0x401234 # insert instruction
[0x00001000]> wt modified_binary
# r2 scripting
r2 -qc 'aaa; afl' suspicious_binary
r2 -i analysis.r2 suspicious_binary
4. Binary Ninja scripting
# BN Python API
import binaryninja as bn
bv = bn.load("suspicious_binary")
for func in bv.functions:
if func.name.startswith("sub_"):
hlil = func.hlil
for block in hlil:
print(block)
5. C++ pattern recognition
// Vtable pattern in disassembly
// mov rax, [rdi] ; load vtable pointer
// call [rax+0x10] ; virtual call at offset
// Constructor pattern
// mov [obj], offset vtable
| Pattern | Indicator |
|---|---|
| Vtable | .data.rel.ro section, array of function pointers |
| RAII | paired ctor/dtor calls, exception landing pads |
| Templates | Mangled names _Z..., duplicate logic per type |
| std::string | SSO buffer inline or heap pointer at offset 0 |
# Demangle C++ symbols
c++filt _ZN4Math3addEii
6. Stripped binary recovery
# Find main via __libc_start_main
readelf -s binary | grep -E 'main|start'
# Or r2: afl~entry
# FLIRT signatures (Ghidra/BN) — match libc patterns
# Stack string analysis in Ghidra decompiler
Strategies:
- Identify
mainvia libc init or entry point - Find syscalls (
syscallinsn on Linux) - String xref to locate error handlers
- Entropy analysis for encrypted sections
7. Binary diffing
# Diaphora (Ghidra/IDA plugin)
# Export from both binaries, run diff
# BinDiff (commercial, IDA/Ghidra)
bindiff old.i64 new.i64
# Simple hash diff
sha256sum firmware_v1 firmware_v2
diff <(objdump -d v1) <(objdump -d v2) | head
Use diffing to find patched vulnerability functions after updates.
8. RE decision tree
Binary type?
├── ELF/Linux → Ghidra + r2 + readelf
├── PE/Windows → Ghidra + PE-bear + x64dbg reference
├── Firmware → binwalk extract → Ghidra on architecture
└── Obfuscated → dynamic analysis (gdb/ltrace) first
Common Problems
| Symptom | Cause | Fix |
|---|---|---|
| Ghidra decompiler fails | Indirect jumps, bad types | Fix function signature; define struct |
| r2 analysis incomplete | Large binary | aaa then aac ; increase analysis depth |
| Wrong architecture | ARM vs Thumb, MIPS | Set -a arm or correct Ghidra language |
| Anti-debug trap | ptrace check | Patch or use -gdb in QEMU |
| Packed binary | UPX/etc. | upx -d or manual unpack |
| No xrefs to string | PIE/RELRO | Follow GOT; runtime analysis |
Related Skills
skills/binaries/elf-inspection— ELF structure analysisskills/debuggers/gdb— dynamic analysis complementskills/runtimes/binary-hardening— understanding mitigations being bypassedskills/security/kernel-security— kernel RE and CVE analysisskills/low-level-programming/assembly-x86— reading disassemblyskills/low-level-programming/assembly-arm— ARM binary analysis