
Reverse Engineering
- 193 installs
- 18.1k repo stars
- Updated August 4, 2026
- zhaoxuya520/reverse-skill
Bypass anti-debug, anti-VM, and integrity checks when analyzing Linux or Windows binaries in CTF or malware-style challenges.
About
Reverse Engineering is a reference-style agent skill aimed at builders and security hobbyists tackling CTF binaries and protected executables on Linux and Windows. It catalogs advanced anti-debugging, anti-VM, anti-DBI, and integrity-check mechanisms alongside practical bypass angles—ptrace and /proc tricks on Linux, PEB and NtQueryInformationProcess paths on Windows, TLS callbacks, hardware and software breakpoint detection, and sandbox fingerprinting via CPUID, timing, and environment artifacts. Solo indie developers rarely need this daily, but it fits anyone shipping native tools, analyzing crackmes, or learning how release binaries resist inspection. Treat it as procedural knowledge to pair with a debugger, disassembler, or dynamic instrumentation workflow rather than an automated scanner. Complexity is advanced: you should already be comfortable with gdb, WinDbg, or similar before applying the bypass recipes.
- Linux advanced anti-debug: ptrace, /proc checks, timing, signals, and syscall-level evasion patterns
- Windows advanced anti-debug: PEB, NtQueryInformationProcess, heap flags, TLS callbacks, breakpoint scans
- Anti-VM and anti-sandbox coverage: CPUID, MAC/hardware fingerprinting, timing, registry and resource artifacts
- Practical bypass-oriented reference structured for CTF workflows rather than generic RE theory
- Table-of-contents depth across multiple OS-specific technique families
Reverse Engineering by the numbers
- 193 all-time installs (skills.sh)
- +33 installs in the week ending Jul 28, 2026 (Skillselion tracking)
- Ranked #786 of 2,203 Security skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/zhaoxuya520/reverse-skill --skill reverse-engineeringAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 193 |
|---|---|
| repo stars | ★ 18.1k |
| Last updated | August 4, 2026 |
| Repository | zhaoxuya520/reverse-skill ↗ |
What it does
Bypass anti-debug, anti-VM, and integrity checks when analyzing Linux or Windows binaries in CTF or malware-style challenges.
Files
Reverse Engineering
Quick reference for RE challenges. For detailed techniques, see supporting files.
Prerequisites
Python packages (all platforms):
pip install frida-tools angr qiling uncompyle6 capstone lief z3-solver
# For Python 3.9+ bytecode: build pycdc from source
git clone https://github.com/zrax/pycdc && cd pycdc && cmake . && makeLinux (apt):
apt install gdb radare2 binutils strace ltrace apktool upxmacOS (Homebrew):
brew install gdb radare2 binutils apktool upx ghidraradare2 plugins:
r2pm -ci r2ghidra # Native Ghidra decompiler for radare2Manual install:
- pwndbg — Linux: GitHub, macOS:
brew install pwndbg/tap/pwndbg-gdb
Additional Resources
- tools.md - Static analysis tools (GDB, Ghidra, radare2, IDA, Binary Ninja, dogbolt.org, RISC-V with Capstone, Unicorn emulation, Python bytecode, WASM, Android APK, .NET, packed binaries)
- tools-dynamic.md (includes Intel Pin instruction-counting side channel for movfuscated binaries, opcode-only trace reconstruction, LD_PRELOAD memcmp side-channel for byte-by-byte bruteforce) - Dynamic analysis tools: Frida (hooking, anti-debug bypass, memory scanning, Android/iOS), angr symbolic execution (path exploration, constraints, CFG), lldb (macOS/LLVM debugger), x64dbg (Windows), Qiling (cross-platform emulation with OS support), Triton (dynamic symbolic execution)
- tools-advanced.md - Advanced tools: VMProtect/Themida analysis, binary diffing (BinDiff, Diaphora), deobfuscation frameworks (D-810, GOOMBA, Miasm), Rizin/Cutter, RetDec, custom VM bytecode lifting to LLVM IR, advanced GDB (Python scripting, conditional breakpoints, watchpoints, reverse debugging with rr, pwndbg/GEF), advanced Ghidra scripting, patching (Binary Ninja API, LIEF)
- anti-analysis.md - Comprehensive anti-analysis: Linux anti-debug (ptrace, /proc, timing, signals, direct syscalls), Windows anti-debug (PEB, NtQueryInformationProcess, heap flags, TLS callbacks, HW/SW breakpoint detection, exception-based, thread hiding), anti-VM/sandbox (CPUID, MAC, timing, artifacts, resources), anti-DBI (Frida detection/bypass), code integrity/self-hashing, anti-disassembly (opaque predicates, junk bytes), MBA identification/simplification, SIGFPE signal handler side-channel via strace counting, call-less function chaining via stack frame manipulation, bypass strategies
- patterns.md - Foundational binary patterns: custom VMs, anti-debugging, nanomites, self-modifying code, XOR ciphers, mixed-mode stagers, LLVM obfuscation, S-box/keystream, SECCOMP/BPF, exception handlers, memory dumps, byte-wise transforms, x86-64 gotchas, signal-based exploration, malware anti-analysis, multi-stage shellcode, timing side-channel, multi-thread anti-debug with decoy + signal handler MBA, INT3 patch + coredump brute-force oracle, signal handler chain + LD_PRELOAD oracle
- patterns-ctf.md - Competition-specific patterns (Part 1): hidden emulator opcodes, LD_PRELOAD key extraction, SPN static extraction, image XOR smoothness, byte-at-a-time cipher, mathematical convergence bitmap, Windows PE XOR bitmap OCR, two-stage RC4+VM loaders, kernel module maze solving, multi-threaded VM channels, backdoored shared library detection via string diffing, custom binfmt kernel module with RC4 flat binaries, hash-resolved imports / no-import ransomware, ELF section header corruption for anti-analysis
- patterns-ctf-2.md - Competition-specific patterns (Part 2): multi-layer self-decrypting brute-force, embedded ZIP+XOR license, stack string deobfuscation, prefix hash brute-force, CVP/LLL lattice for integer validation, decision tree function obfuscation, GF(2^8) Gaussian elimination, ROP chain obfuscation analysis (ROPfuscation)
- patterns-ctf-3.md - Competition-specific patterns (Part 3): Z3 single-line Python circuit, sliding window popcount, keyboard LED Morse code via ioctl, C++ destructor-hidden validation, syscall side-effect memory corruption, MFC dialog event handlers, VM sequential key-chain brute-force, Burrows-Wheeler transform inversion, OpenType font ligature exploitation, GLSL shader VM with self-modifying code, instruction counter as cryptographic state, batch crackme automation via objdump, fork+pipe+dead branch anti-analysis, TensorFlow DNN inversion via sigmoid layer inversion, BPF filter analysis via kernel JIT to x64 assembly
- languages.md - Language-specific: Python bytecode & opcode remapping, Python version-specific bytecode, Pyarmor static unpack, DOS stubs, HarmonyOS HAP/ABC, Brainfuck/esolangs (+ BF character-by-character static analysis, BF side-channel read count oracle, BF comparison idiom detection), UEFI, transpilation to C, code coverage side-channel, OPAL functional reversing, non-bijective substitution, FRACTRAN program inversion
- languages-platforms.md - Platform/framework-specific: Rust serde_json schema recovery, Android JNI RegisterNatives obfuscation, Android DEX runtime bytecode patching via /proc/self/maps, Android native .so loading bypass via new project, Frida Firebase Cloud Functions bypass, Verilog/hardware RE, prefix-by-prefix hash reversal, Ruby/Perl polyglot constraint satisfaction, Electron ASAR extraction + native binary analysis, Node.js npm runtime introspection
- languages-compiled.md - Go binary reversing (GoReSym, goroutines, memory layout, channel ops, embed.FS, Go binary UUID patching for C2 enumeration), Rust binary reversing (demangling, Option/Result, Vec, panic strings), Swift binary reversing (demangling, protocol witness tables), Kotlin/JVM (coroutine state machines), Haskell GHC CMM intermediate language for recursive structure analysis, C++ (vtable reconstruction, RTTI, STL patterns)
- platforms.md - Platform-specific RE: macOS/iOS (Mach-O, code signing, Objective-C runtime, Swift, dyld, jailbreak bypass), embedded/IoT firmware (binwalk, UART/JTAG/SPI extraction, ARM/MIPS, RTOS), kernel drivers (Linux .ko, eBPF, Windows .sys), automotive CAN bus
- platforms-hardware.md - Hardware and advanced architecture RE: HD44780 LCD controller GPIO reconstruction, RISC-V advanced (custom extensions, privileged modes, debugging), ARM64/AArch64 reversing and exploitation (calling convention, ROP gadgets, qemu-aarch64-static emulation)
- field-notes.md - Quick reference notes: binary types, anti-debugging bypass, specialized patterns, CTF case notes
---
When to Pivot
- If you already understand the binary and now need heap, ROP, or kernel exploitation, switch to
/ctf-pwn. - If the challenge is really about recovering deleted files, PCAP data, or disk artifacts, switch to
/ctf-forensics. - If the target is a web app and you are only reversing a small client-side helper script, switch to
/ctf-web. - If the binary implements a machine learning model and the challenge is about model attacks or adversarial inputs, switch to
/ctf-ai-ml. - If the reversed binary's core logic is a cryptographic algorithm or math problem, switch to
/ctf-crypto. - If the binary is a real malware sample with C2, packing, or evasion behavior, switch to
/ctf-malware. - If the challenge is a toy VM, encoding puzzle, or pyjail rather than a real binary, switch to
/ctf-misc.
Problem-Solving Workflow
1. Start with strings extraction - many easy challenges have plaintext flags 2. Try ltrace/strace - dynamic analysis often reveals flags without reversing 3. Try Frida hooking - hook strcmp/memcmp to capture expected values without reversing 4. Try angr - symbolic execution solves many flag-checkers automatically 5. Try Qiling - emulate foreign-arch binaries or bypass heavy anti-debug without artifacts 6. Map control flow before modifying execution 7. Automate manual processes via scripting (r2pipe, Frida, angr, Python) 8. Validate assumptions by comparing decompiler outputs (dogbolt.org for side-by-side)
Quick Wins (Try First!)
# Plaintext flag extraction
strings binary | grep -E "flag\{|CTF\{|pico"
strings binary | grep -iE "flag|secret|password"
rabin2 -z binary | grep -i "flag"
# Dynamic analysis - often captures flag directly
ltrace ./binary
strace -f -s 500 ./binary
# Hex dump search
xxd binary | grep -i flag
# Run with test inputs
./binary AAAA
echo "test" | ./binaryInitial Analysis
file binary # Type, architecture
checksec --file=binary # Security features (for pwn)
chmod +x binary # Make executableMemory Dumping Strategy
Key insight: Let the program compute the answer, then dump it. Break at final comparison (b *main+OFFSET), enter any input of correct length, then x/s $rsi to dump computed flag.
Decoy Flag Detection
Pattern: Multiple fake targets before real check. Look for multiple comparison targets in sequence with different success messages. Set breakpoint at FINAL comparison, not earlier ones.
GDB PIE Debugging
PIE binaries randomize base address. Use relative breakpoints:
gdb ./binary
start # Forces PIE base resolution
b *main+0xca # Relative to main
runComparison Direction (Critical!)
Two patterns: (1) transform(flag) == stored_target — reverse the transform. (2) transform(stored_target) == flag — flag IS the transformed data, just apply transform to stored target.
Common Encryption Patterns
- XOR with single byte - try all 256 values
- XOR with known plaintext (
flag{,CTF{) - RC4 with hardcoded key
- Custom permutation + XOR
- XOR with position index (
^ ior^ (i & 0xff)) layered with a repeating key
Quick Tool Reference
# Radare2
r2 -d ./binary # Debug mode
aaa # Analyze
afl # List functions
pdf @ main # Disassemble main
# Ghidra (headless)
analyzeHeadless project/ tmp -import binary -postScript script.py
# IDA
ida64 binary # Open in IDA64Deep-Dive Notes
Use field-notes.md after the first round of triage when you know what kind of target you have.
- Target formats: Python bytecode, WASM, Android, Flutter, .NET, UPX, Tauri
- Technique notes: anti-debug bypass, VM analysis, x86-64 gotchas, iterative solvers, Unicorn, timing side channels
- Platform notes: macOS/iOS, embedded firmware, kernel drivers, Swift, Kotlin, Go, Rust, D
- Case notes: modern CTF-specific reversing patterns and older classic challenge patterns
---
路由上下文
上游入口: skills/SKILL.md(总控)、routing.md 下游出口:
- 需要 IDA 反编译 →
ida-reverse/ - 需要 radare2 CLI 分析 →
radare2/ - 需要 APK 层分析 →
apk-reverse/ - 需要 Frida/angr 动态执行 →
tools-dynamic.md - 需要绕过反调试 →
anti-analysis.md - 遇到特定语言(Go/Rust/Python/WASM)→
languages*.md - 遇到 CTF 模式 →
patterns*.md
同级关联模块: apk-reverse/(APK 定位到 .so 时可切回本模块的 Frida/radare2 分支)
CTF Reverse - Anti-Analysis Techniques & Bypasses
Comprehensive reference for anti-debugging, anti-VM, anti-DBI, and integrity-check techniques encountered in CTF challenges, with practical bypasses.
Table of Contents
- Linux Anti-Debug (Advanced)
- ptrace-Based
- /proc Filesystem Checks
- Timing-Based Detection
- Signal-Based Anti-Debug
- Syscall-Level Evasion
- Windows Anti-Debug (Advanced)
- PEB (Process Environment Block) Checks
- NtQueryInformationProcess
- Heap Flags
- TLS Callbacks
- Hardware Breakpoint Detection
- Software Breakpoint Detection (INT3 Scanning)
- Exception-Based Anti-Debug
- NtSetInformationThread (Thread Hiding)
- Anti-VM / Anti-Sandbox
- CPUID Hypervisor Bit
- MAC Address / Hardware Fingerprinting
- Timing-Based VM Detection
- File / Registry Artifacts
- Resource Checks (CPU Count, RAM, Disk)
- Anti-DBI (Dynamic Binary Instrumentation)
- Frida Detection
- Pin/DynamoRIO Detection
- Code Integrity / Self-Hashing
- Anti-Disassembly Techniques
- Opaque Predicates
- Junk Bytes / Overlapping Instructions
- Jump-in-the-Middle
- Function Chunking / Scattered Code
- Control Flow Flattening (Advanced)
- Mixed Boolean-Arithmetic (MBA) Identification & Simplification
- SIGILL Handler for Execution Mode Switching (Hack.lu 2015)
- SIGFPE Signal Handler Side-Channel via strace Counting (PlaidCTF 2017)
- Instruction Trace Inversion with Keystone and Unicorn (MeePwn CTF 2017)
- Call-less Function Chaining via Stack Frame Manipulation (THC CTF 2018)
- Comprehensive Bypass Strategies
- Universal Bypass Checklist
- Layered Anti-Debug (Real-World Pattern)
- Quick Reference: Check to Bypass
---
Linux Anti-Debug (Advanced)
ptrace-Based
Self-ptrace (most common):
if (ptrace(PTRACE_TRACEME, 0, 0, 0) == -1) exit(1); // Already traced = debugger attachedBypasses:
# 1. LD_PRELOAD (see patterns.md for full hook)
LD_PRELOAD=./hook.so ./binary
# 2. Patch with pwntools
python3 -c "
from pwn import *
elf = ELF('./binary', checksec=False)
elf.asm(elf.symbols.ptrace, 'xor eax, eax; ret')
elf.save('patched')
"
# 3. GDB: catch the syscall
gdb ./binary
(gdb) catch syscall ptrace
(gdb) run
# When it stops at ptrace:
(gdb) set $rax = 0
(gdb) continue
# 4. Kernel config (requires root)
echo 0 > /proc/sys/kernel/yama/ptrace_scopeDouble-ptrace pattern:
// Fork child to ptrace parent — blocks all other debuggers
pid_t child = fork();
if (child == 0) {
ptrace(PTRACE_ATTACH, getppid(), 0, 0);
// Child sits in waitpid loop, keeping parent traced
} else {
// Parent continues with real logic
}Bypass: Kill the watchdog child process, then attach debugger.
/proc Filesystem Checks
// TracerPid check
FILE *f = fopen("/proc/self/status", "r");
// Looks for "TracerPid:\t0" — non-zero means debugger
// /proc/self/exe link check (some debuggers change this)
readlink("/proc/self/exe", buf, sizeof(buf));
// /proc/self/maps — check for debugger libraries
grep("frida", "/proc/self/maps");Bypasses:
# 1. LD_PRELOAD fopen/fread to fake /proc contents
# 2. Mount namespace isolation
unshare -m bash -c 'mount --bind /dev/null /proc/self/status && ./binary'
# 3. GDB: set breakpoint at fopen, change filename argument
(gdb) b fopen
(gdb) run
(gdb) set {char[20]} $rdi = "/dev/null"
(gdb) continueTiming-Based Detection
// rdtsc (CPU timestamp counter)
uint64_t start = __rdtsc();
// ... code ...
uint64_t delta = __rdtsc() - start;
if (delta > THRESHOLD) exit(1); // too slow = debugger
// clock_gettime
struct timespec ts1, ts2;
clock_gettime(CLOCK_MONOTONIC, &ts1);
// ... code ...
clock_gettime(CLOCK_MONOTONIC, &ts2);
// gettimeofday
struct timeval tv1, tv2;
gettimeofday(&tv1, NULL);Bypasses:
# 1. Frida hook (see tools-dynamic.md for clock_gettime hook)
# 2. GDB: skip rdtsc by patching with constant
(gdb) set {unsigned char[2]} 0x401234 = {0x90, 0x90} # NOP the rdtsc
# 3. Pin tool to fix TSC reads
# 4. faketime library
LD_PRELOAD=/usr/lib/faketime/libfaketime.so.1 FAKETIME="2024-01-01" ./binarySignal-Based Anti-Debug
// SIGTRAP handler — INT3 under debugger is caught by debugger, not handler
signal(SIGTRAP, handler);
__asm__("int3");
// If handler runs: no debugger. If debugger catches: debugged.
// SIGALRM timeout — kill self if analysis takes too long
signal(SIGALRM, kill_handler);
alarm(5);
// SIGSEGV handler that does real work (see patterns.md for MBA pattern)
signal(SIGSEGV, real_logic_handler);
*(int*)0 = 0; // deliberate crash → handler runs real codeBypasses:
# GDB: pass signals to program instead of handling them
(gdb) handle SIGTRAP nostop pass
(gdb) handle SIGALRM ignore
(gdb) handle SIGSEGV nostop pass
# For alarm-based: patch alarm() to return immediatelySyscall-Level Evasion
// Direct syscall instead of libc — bypasses LD_PRELOAD hooks
long ret;
asm volatile("syscall" : "=a"(ret) : "a"(101), "D"(0), "S"(0), "d"(0), "r"(0));
// Syscall 101 = ptrace on x86_64Bypass: Must patch the binary itself or use ptrace to intercept at syscall level.
# GDB: catch syscall
(gdb) catch syscall 101
(gdb) commands
> set $rax = 0
> continue
> end---
Windows Anti-Debug (Advanced)
PEB (Process Environment Block) Checks
// BeingDebugged flag (offset 0x2 in PEB)
bool debugged = NtCurrentPeb()->BeingDebugged;
// NtGlobalFlag (offset 0x68/0xBC in PEB)
// When debugger: FLG_HEAP_ENABLE_TAIL_CHECK | FLG_HEAP_ENABLE_FREE_CHECK | FLG_HEAP_VALIDATE_PARAMETERS = 0x70
DWORD flags = *(DWORD*)((BYTE*)NtCurrentPeb() + 0xBC); // 64-bit offset
if (flags & 0x70) exit(1);Bypass (x64dbg):
# ScyllaHide plugin auto-patches PEB fields
# Manual: dump PEB, zero BeingDebugged and NtGlobalFlagNtQueryInformationProcess
// ProcessDebugPort (0x7)
DWORD_PTR debugPort = 0;
NtQueryInformationProcess(GetCurrentProcess(), 7, &debugPort, sizeof(debugPort), NULL);
if (debugPort != 0) exit(1);
// ProcessDebugObjectHandle (0x1E)
HANDLE debugObj = NULL;
NTSTATUS status = NtQueryInformationProcess(GetCurrentProcess(), 0x1E, &debugObj, sizeof(debugObj), NULL);
if (status == 0) exit(1); // STATUS_SUCCESS means debugger present
// ProcessDebugFlags (0x1F) — returns inverse: 0 = debugger present
DWORD noDebug = 0;
NtQueryInformationProcess(GetCurrentProcess(), 0x1F, &noDebug, sizeof(noDebug), NULL);
if (noDebug == 0) exit(1);Bypass: Hook NtQueryInformationProcess to return fake values, or use ScyllaHide.
Heap Flags
// Process heap has debug flags when debugger attached
PHEAP heap = (PHEAP)GetProcessHeap();
// Flags at offset 0x70 (64-bit): should be HEAP_GROWABLE (0x2)
// ForceFlags at offset 0x74: should be 0
if (heap->Flags != 0x2 || heap->ForceFlags != 0) exit(1);TLS Callbacks
Key technique: TLS (Thread Local Storage) callbacks execute BEFORE main() / entry point.
// Registered in PE header's TLS directory
void NTAPI TlsCallback(PVOID DllHandle, DWORD Reason, PVOID Reserved) {
if (Reason == DLL_PROCESS_ATTACH) {
if (IsDebuggerPresent()) {
ExitProcess(1); // Kills process before main runs
}
}
}
#pragma comment(linker, "/INCLUDE:_tls_used")
#pragma data_seg(".CRT$XLB")
PIMAGE_TLS_CALLBACK callbacks[] = { TlsCallback, NULL };Detection in IDA/Ghidra: Check PE TLS Directory → AddressOfCallBacks. Functions listed there run before EP.
Bypass: Set breakpoint on TLS callback in x64dbg (Options → Events → TLS Callbacks), or patch the TLS directory entry.
Hardware Breakpoint Detection
// Read debug registers via GetThreadContext
CONTEXT ctx;
ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
GetThreadContext(GetCurrentThread(), &ctx);
if (ctx.Dr0 || ctx.Dr1 || ctx.Dr2 || ctx.Dr3) exit(1);
// Also via exception handler: deliberate exception, check DR regs in handlerBypass:
# x64dbg: use software breakpoints instead, or hook GetThreadContext
# Frida: hook GetThreadContext to zero DR registersSoftware Breakpoint Detection (INT3 Scanning)
// CRC / hash check over code section
unsigned char *code = (unsigned char*)function_addr;
uint32_t checksum = 0;
for (int i = 0; i < code_size; i++) {
checksum += code[i];
if (code[i] == 0xCC) exit(1); // INT3 = software breakpoint
}
if (checksum != EXPECTED_CHECKSUM) exit(1);Bypass: Use hardware breakpoints (DR0-DR3) instead of software breakpoints. Or hook the scanning function.
Exception-Based Anti-Debug
// UnhandledExceptionFilter — under debugger, filter is NOT called
SetUnhandledExceptionFilter(handler);
RaiseException(EXCEPTION_ACCESS_VIOLATION, 0, 0, NULL);
// If handler runs: no debugger
// If debugger catches: debugger present
// INT 2D — debugger single-step anomaly
__asm { int 2dh } // Debugger silently consumes the exception
// If execution continues: debugger presentNtSetInformationThread (Thread Hiding)
// Hide thread from debugger — stops all debug events
typedef NTSTATUS(NTAPI *pNtSIT)(HANDLE, ULONG, PVOID, ULONG);
pNtSIT NtSIT = (pNtSIT)GetProcAddress(GetModuleHandle("ntdll"), "NtSetInformationThread");
NtSIT(GetCurrentThread(), 0x11 /*ThreadHideFromDebugger*/, NULL, 0);
// After this, debugger won't see breakpoints or exceptions from this threadBypass: Hook NtSetInformationThread to ignore class 0x11, or patch the call.
---
Anti-VM / Anti-Sandbox
CPUID Hypervisor Bit
int regs[4];
__cpuid(regs, 1);
if (regs[2] & (1 << 31)) { // ECX bit 31 = hypervisor present
exit(1);
}
// Hypervisor brand string
__cpuid(regs, 0x40000000);
char brand[13] = {0};
memcpy(brand, ®s[1], 12);
// "VMwareVMware", "Microsoft Hv", "KVMKVMKVM", "XenVMMXenVMM"Bypass: Patch cpuid results or use LD_PRELOAD to hook wrapper functions.
MAC Address / Hardware Fingerprinting
Known VM MAC prefixes:
VMware: 00:0C:29, 00:50:56
VirtualBox: 08:00:27
Hyper-V: 00:15:5D
Parallels: 00:1C:42
QEMU: 52:54:00Timing-Based VM Detection
// VM exits on privileged instructions are measurably slower
uint64_t start = __rdtsc();
__cpuid(regs, 0); // Forces VM exit
uint64_t delta = __rdtsc() - start;
if (delta > 500) { /* likely VM */ }File / Registry Artifacts
Files: C:\Windows\System32\drivers\vm*.sys, vbox*.dll, VBoxService.exe
Registry: HKLM\SOFTWARE\VMware, Inc.\VMware Tools
Services: VMTools, VBoxService
Processes: vmtoolsd.exe, VBoxTray.exe, qemu-ga.exe
Linux: /sys/class/dmi/id/product_name contains "VirtualBox"|"VMware"
dmesg | grep -i "hypervisor detected"Resource Checks (CPU Count, RAM, Disk)
// Sandboxes typically have minimal resources
SYSTEM_INFO si;
GetSystemInfo(&si);
if (si.dwNumberOfProcessors < 2) exit(1);
MEMORYSTATUSEX ms;
ms.dwLength = sizeof(ms);
GlobalMemoryStatusEx(&ms);
if (ms.ullTotalPhys < 2ULL * 1024 * 1024 * 1024) exit(1); // < 2GB RAM
// Disk size check (< 60GB = sandbox)
GetDiskFreeSpaceEx("C:\\", NULL, &total, NULL);Bypass: Use a VM configured with adequate resources (4+ CPUs, 8GB+ RAM, 100GB+ disk).
---
Anti-DBI (Dynamic Binary Instrumentation)
Frida Detection
// 1. Check /proc/self/maps for frida-agent
FILE *f = fopen("/proc/self/maps", "r");
while (fgets(line, sizeof(line), f)) {
if (strstr(line, "frida") || strstr(line, "gadget")) exit(1);
}
// 2. Check for Frida's default port (27042)
int sock = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addr = {.sin_family=AF_INET, .sin_port=htons(27042), .sin_addr.s_addr=inet_addr("127.0.0.1")};
if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == 0) exit(1);
// 3. Check for inline hooks (function prologue modification)
// Compare first bytes of libc functions against expected values
unsigned char *strcmp_bytes = (unsigned char *)strcmp;
if (strcmp_bytes[0] == 0xE9 || strcmp_bytes[0] == 0xFF) exit(1); // JMP = hooked
// 4. Thread name check
// Frida creates threads with names like "gmain", "gdbus", "frida-*"
DIR *dir = opendir("/proc/self/task");
while ((entry = readdir(dir))) {
char comm_path[256];
snprintf(comm_path, sizeof(comm_path), "/proc/self/task/%s/comm", entry->d_name);
// Read comm and check for "gmain", "gdbus"
}
// 5. Named pipe detection (Windows)
// Frida creates \\.\pipe\frida-* named pipesFrida bypass of Frida detection:
// Hook the detection functions themselves
Interceptor.attach(Module.findExportByName(null, "strstr"), {
onEnter(args) {
this.haystack = Memory.readUtf8String(args[0]);
this.needle = Memory.readUtf8String(args[1]);
},
onLeave(retval) {
if (this.needle && (this.needle.includes("frida") || this.needle.includes("gadget"))) {
retval.replace(ptr(0)); // Not found
}
}
});
// Early Frida load (before anti-DBI runs)
// Use frida-gadget as early-init shared libraryPin/DynamoRIO Detection
// Check for instrumentation libraries in /proc/self/maps
// Pin: "pin-", "pinbin", "pinatrace"
// DynamoRIO: "dynamorio", "drcov", "drrun"
// Instruction count timing — DBI adds overhead
// Execute known instruction sequence, compare execution time---
Code Integrity / Self-Hashing
// CRC32 over .text section
uint32_t crc = compute_crc32(text_start, text_size);
if (crc != EXPECTED_CRC) exit(1); // Code was modified (breakpoints, patches)
// MD5/SHA256 of function bodies
unsigned char hash[32];
SHA256(function_addr, function_size, hash);
if (memcmp(hash, expected_hash, 32) != 0) exit(1);Bypasses: 1. Hardware breakpoints (don't modify code, DR0-DR3) 2. Patch the comparison to always succeed 3. Hook the hash function to return expected value 4. Emulate instead of debug (Unicorn/Qiling — no code modification) 5. Snapshot + restore: dump memory before and after, diff to find checks
Self-checksumming in loops:
// Continuous integrity check in separate thread
void *watchdog(void *arg) {
while (1) {
if (compute_crc32(text_start, text_end - text_start) != saved_crc) {
memset(flag_buffer, 0, flag_len); // Destroy flag
exit(1);
}
usleep(100000);
}
}Bypass: Kill the watchdog thread or patch its sleep to infinite.
---
Anti-Disassembly Techniques
Opaque Predicates
; Condition that always evaluates the same way but looks data-dependent
mov eax, [some_memory]
imul eax, eax ; x^2
and eax, 1 ; x^2 mod 2 is always 0 for any x
jnz fake_branch ; Never taken, but disassembler doesn't know
; real code hereIdentification: Z3/SMT can prove branch is always/never taken.
Junk Bytes / Overlapping Instructions
jmp real_code
db 0xE8 ; Looks like start of CALL to linear disassembler
real_code:
mov eax, 1 ; Real code — disassembler may misalign hereFix: Switch to graph-mode disassembly (Ghidra/IDA handle this well). Manual: undefine and re-analyze from correct offset.
Jump-in-the-Middle
; Jumps into the middle of a multi-byte instruction
eb 01 ; jmp +1 (skip next byte)
e8 ; fake CALL opcode — disassembler tries to decode as call
90 ; real: NOP (landed here from jmp)Function Chunking / Scattered Code
Functions split into non-contiguous chunks connected by unconditional jumps. Defeats linear function boundary detection.
Tool: IDA's "Append function tail" or Ghidra's "Create function" at each chunk.
Control Flow Flattening (Advanced)
Beyond basic switch-case (see patterns.md): modern OLLVM variants use:
- Bogus control flow: Fake branches with opaque predicates
- Instruction substitution:
a + b→a - (-b),a ^ b→(a | b) & ~(a & b) - String encryption: Strings decrypted at runtime, cleared after use
Deobfuscation tools:
- D-810 (IDA plugin): Pattern-based deobfuscation, MBA simplification
- GOOMBA (Ghidra): Automated deobfuscation for OLLVM
- Miasm: Symbolic execution for deobfuscation
- Arybo / SiMBA: MBA expression simplification
# D-810: install in IDA plugins directory, Edit → Plugins → D-810
# Simplifies MBA expressions: (a | b) & ~(a & b) → a ^ b
# Removes opaque predicates via pattern matchingMixed Boolean-Arithmetic (MBA) Identification & Simplification
# Common MBA patterns and their simplified forms:
# (x & y) + (x | y) == x + y
# (x ^ y) + 2*(x & y) == x + y
# (x | y) - (x & ~y) == y
# ~(~x & ~y) == x | y (De Morgan's)
# (x | y) & ~(x & y) == x ^ y
# SiMBA tool for automated simplification:
# pip install simba-simplifier
from simba import simplify_mba
expr = "(a | b) + (a & b) - (~a & b)"
print(simplify_mba(expr)) # → a---
SIGILL Handler for Execution Mode Switching (Hack.lu 2015)
Binaries may install SIGILL (illegal instruction) handlers to switch between x86 and x86-64 execution modes or implement custom opcode dispatch:
1. Signal registration: signal(SIGILL, handler) installs a callback for illegal instruction exceptions 2. Mode switching: The handler modifies the saved instruction pointer or segment registers to switch between 32-bit and 64-bit code 3. Custom opcodes: Invalid x86 instructions trigger the handler, which interprets operand bytes as custom VM opcodes
// Signal handler decodes "illegal" instructions as custom opcodes
void sigill_handler(int sig, siginfo_t *info, void *ucontext) {
ucontext_t *ctx = (ucontext_t *)ucontext;
unsigned char *pc = (unsigned char *)ctx->uc_mcontext.gregs[REG_RIP];
// Decode custom opcode from bytes at PC
// Advance PC past the custom instruction
ctx->uc_mcontext.gregs[REG_RIP] += opcode_length;
}Key insight: If a binary installs signal handlers for SIGILL/SIGSEGV/SIGTRAP early in execution, suspect custom instruction dispatch. Trace signal deliveries with strace -e signal or set GDB to not intercept: handle SIGILL nostop pass.
---
SIGFPE Signal Handler Side-Channel via strace Counting (PlaidCTF 2017)
Binary uses SIGFPE signal handlers for control flow, making static analysis unreliable. Brute-force by counting SIGFPE signals via strace — correct input characters produce more signals.
# Count SIGFPE signals per input character guess
for c in {a..z} {A..Z} {0..9}; do
count=$(echo -n "${c}AAAAAAA" | strace -e signal=SIGFPE ./binary 2>&1 | grep -c SIGFPE)
echo "$c: $count"
done
# Character producing the most SIGFPEs is correct
# Repeat for each position, extending the known prefixKey insight: Signal handlers (SIGFPE, SIGSEGV, SIGILL) create implicit control flow invisible to static analysis. The number of signals raised correlates with validation progress. Counting signals via strace -e signal=SIGFPE turns opaque signal-based validation into a measurable side-channel for character-by-character brute-force.
---
Instruction Trace Inversion with Keystone and Unicorn (MeePwn CTF 2017)
UPX-packed binary applies a sequence of arithmetic-only transforms (sub, add, xor, rol, ror) to the flag. No memory side-effects — purely register arithmetic. IDAPython traces non-jump instructions, the sequence is then inverted to recover the flag.
Inversion rules:
- Reverse the instruction sequence (last instruction first)
- Swap inverse pairs:
add ↔ sub,rol ↔ ror,xoris self-inverse
# IDAPython: collect non-jump instructions in the obfuscated routine
import idaapi, idc
def trace_transforms(start_ea, end_ea):
instructions = []
ea = start_ea
while ea < end_ea:
mnem = idc.print_insn_mnem(ea)
if mnem not in ('jmp', 'je', 'jne', 'call', 'ret'):
instructions.append((ea, mnem, idc.print_operands(ea)))
ea = idc.next_head(ea)
return instructions
transforms = trace_transforms(0x401000, 0x401200)
# Invert: reverse order, swap add/sub and rol/ror
inverse_map = {'add': 'sub', 'sub': 'add', 'rol': 'ror', 'ror': 'rol', 'xor': 'xor'}
inverted = [(mnem, op) for (_, mnem, op) in reversed(transforms)]
inverted = [(inverse_map.get(m, m), op) for m, op in inverted]# Assemble inverted instructions with Keystone, emulate with Unicorn
from keystone import *
from unicorn import *
from unicorn.x86_const import *
ks = Ks(KS_ARCH_X86, KS_MODE_64)
uc = Uc(UC_ARCH_X86, UC_MODE_64)
asm_src = '\n'.join(f'{mnem} {op}' for mnem, op in inverted)
encoding, _ = ks.asm(asm_src)
CODE_BASE = 0x400000
uc.mem_map(CODE_BASE, 0x10000)
uc.mem_write(CODE_BASE, bytes(encoding))
# Set initial register state to the observed output value
uc.reg_write(UC_X86_REG_RAX, known_output)
uc.emu_start(CODE_BASE, CODE_BASE + len(encoding))
flag_bytes = uc.reg_read(UC_X86_REG_RAX).to_bytes(8, 'little')PEB anti-debug note: If the binary reads PEB.BeingDebugged and uses it to select between two comparison target values, the traced instructions under IDAPython may use the debug-mode target. Patch BeingDebugged to 0 before tracing, or identify both branches and use the non-debug target value.
Key insight: Arithmetic-only obfuscation (no memory writes) is fully reversible by tracing, inverting the instruction sequence, and swapping inverse operations. PEB anti-debug can silently change comparison targets — always verify which branch is taken.
References: MeePwn CTF 2017
---
Call-less Function Chaining via Stack Frame Manipulation (THC CTF 2018)
Pattern: Binary hides function calls by building a linked list of function pointers on the stack, then modifying saved RBP and return addresses so leave; ret instructions chain through the list without any explicit CALL instructions. IDA fails to decompile because push/pop are unbalanced and function boundaries cannot be determined.
Each function in the chain: 1. Pushes operands and the next function's address onto the stack 2. Sets saved RBP to point to the next stack frame 3. Sets the return address to the next function 4. leave restores RSP from RBP (moving to next frame), ret jumps to the next function
# Reversed processing chain (each function applied via leave/ret):
def reverse_processing(byte):
res = byte | 0x80 # OR 0x80
res = res ^ 0xCA # XOR 0xCA
res = (res + 66) & 0xFF # ADD 66
res = res ^ 0xCA # XOR 0xCA (repeated)
res = (res + 66) & 0xFF
res = res ^ 0xCA
res = (res + 66) & 0xFF
res = res ^ 0xFE # XOR 0xFE (final)
return res
# Apply in reverse order, then reverse the character sequenceKey insight: By manipulating saved RBP to point to the next stack frame and saved RIP to the next function, leave; ret chains through functions without any call instructions. Disassemblers that track call/ret balance fail to identify function boundaries. Patch each function body individually for IDA to handle them.
Detection: Binary with many small code blocks ending in leave; ret but no corresponding call instructions. Stack contains interleaved function pointers and data. IDA shows "stack frame is too big" or fails to create functions.
References: THC CTF 2018
---
Comprehensive Bypass Strategies
Universal Bypass Checklist
1. Identify all anti-analysis checks — search for: ptrace, IsDebuggerPresent, rdtsc, cpuid, NtQuery, GetTickCount, CheckRemoteDebuggerPresent, /proc/self, SIGTRAP, alarm 2. Static patching — NOP/patch checks with pwntools or Ghidra before running 3. LD_PRELOAD (Linux) — hook libc functions returning fake values 4. ScyllaHide (Windows x64dbg) — patches PEB, hooks NT functions automatically 5. Emulation (Unicorn/Qiling) — no debugger artifacts to detect 6. Kernel-level bypass — modify /proc/sys/kernel/yama/ptrace_scope, use prctl
Layered Anti-Debug (Real-World Pattern)
Many CTF challenges stack multiple checks:
1. TLS callback → IsDebuggerPresent (before main)
2. main() → ptrace(TRACEME)
3. Watchdog thread → timing check + /proc scan
4. Code section → self-CRC32 integrity
5. Signal handler → real logic in SIGSEGV handlerApproach: Identify ALL checks before patching. Patch or hook each one systematically. Run under emulator if too many to patch individually.
Quick Reference: Check to Bypass
| Anti-Debug Check | Platform | Bypass |
|---|---|---|
ptrace(TRACEME) | Linux | LD_PRELOAD, patch to ret 0, catch syscall |
IsDebuggerPresent | Windows | ScyllaHide, Frida hook, PEB patch |
NtQueryInformationProcess | Windows | ScyllaHide, hook ntdll |
rdtsc timing | Both | NOP rdtsc, Frida time hook, Pin |
/proc/self/status | Linux | Mount namespace, hook fopen |
alarm(N) | Linux | handle SIGALRM ignore in GDB |
SIGTRAP handler | Linux | handle SIGTRAP nostop pass |
SIGFPE handler side-channel | Linux | strace -e signal=SIGFPE count per input |
| TLS callback | Windows | Break on TLS in x64dbg, patch |
| DR register scan | Windows | Use software BPs, hook GetThreadContext |
| INT3 scan / CRC | Both | Hardware BPs, patch CRC comparison |
| Frida detection | Both | Early-load gadget, hook strstr |
| CPUID hypervisor | Both | Patch CPUID result, bare metal |
| Thread hiding | Windows | Hook NtSetInformationThread |
逆向工程参考资源汇总
精选自多个 awesome 列表,按实用性排序。AI 在逆向分析时可参考这些资源获取方法论和工具指导。
---
综合资源库
| 项目 | Stars | 覆盖 | 链接 |
|---|---|---|---|
| awesome-reversing (tylerha97) | 3k+ | 逆向工具/书籍/课程/练习 | https://github.com/tylerha97/awesome-reversing |
| awesome-reverse-engineering (alphaSeclab) | 4k+ | 3500+ 工具 + 2300 文章,全平台 | https://github.com/alphaSeclab/awesome-reverse-engineering |
| Reverse-Engineering (mytechnotalent) | 10k+ | 免费教程:x86/x64/ARM/AVR/RISC-V | https://github.com/mytechnotalent/Reverse-Engineering |
| awesome-malware-analysis (rshipp) | 12k+ | 恶意软件分析工具/资源 | https://github.com/rshipp/awesome-malware-analysis |
| reversingBits | — | 逆向/二进制分析速查表合集 | https://github.com/mohitmishra786/reversingBits |
| awesome-arm-exploitation | — | ARM 利用资源(视频/文章/书籍) | https://github.com/HenryHoggard/awesome-arm-exploitation |
| Binary-Analysis-Automation | — | 自动化二进制分析(ML/脚本/静态/动态) | https://github.com/user1342/Awesome-Binary-Analysis-Automation |
---
ELF / Linux 逆向专项
| 资源 | 说明 | 链接 |
|---|---|---|
| libelfmaster | 安全 ELF 解析库(取证/恶意软件重建) | https://github.com/elfmaster/libelfmaster |
| ELF 规范 | 官方 ELF 格式文档 | https://refspecs.linuxfoundation.org/elf/elf.pdf |
| Linux Internals | /proc 文件系统、内存布局、syscall | https://0xax.gitbooks.io/linux-insides/ |
| Compiler Explorer | 在线看 C/C++/Rust/Go 编译成什么汇编 | https://godbolt.org/ |
---
ARM / AArch64 专项
| 资源 | 说明 | 链接 |
|---|---|---|
| ARM 官方架构手册 | 完整指令集参考 | https://developer.arm.com/documentation |
| Azeria Labs | ARM 汇编/利用教程(最佳入门) | https://azeria-labs.com/writing-arm-assembly-part-1/ |
| ARM64 syscall 表 | Linux AArch64 系统调用号 | https://arm64.syscall.sh/ |
| QEMU 用户态模拟 | 不需要真实设备分析 ARM 二进制 | qemu-aarch64 -strace ./binary |
---
恶意软件分析
| 资源 | 说明 | 链接 |
|---|---|---|
| YARA | 恶意软件特征匹配规则 | https://github.com/VirusTotal/yara |
| Volatility 3 | 内存取证框架 | https://github.com/volatilityfoundation/volatility3 |
| FLOSS | 自动提取混淆字符串 | https://github.com/mandiant/flare-floss |
| Detect It Easy (DiE) | 文件类型/壳/编译器识别 | https://github.com/horsicq/Detect-It-Easy |
| PE-bear | PE 文件分析器 | https://github.com/hasherezade/pe-bear |
| Capa | 自动识别二进制能力(网络/文件/加密等) | https://github.com/mandiant/capa |
| Unpacker | 通用脱壳框架 | https://github.com/malwaretech/UnpackerFramework |
---
动态分析 / 沙箱
| 资源 | 说明 | 链接 |
|---|---|---|
| Frida | 跨平台动态插桩 | https://frida.re/ |
| strace | Linux 系统调用跟踪 | 系统自带 |
| ltrace | 库函数调用跟踪 | 系统自带 |
| QEMU | 用户态/系统态模拟 | https://www.qemu.org/ |
| Unicorn | CPU 模拟框架(可编程) | https://www.unicorn-engine.org/ |
| Qiling | 高级二进制模拟框架 | https://qiling.io/ |
| angr | 符号执行 + 二进制分析 | https://angr.io/ |
| Triton | 动态二进制分析框架 | https://triton-library.github.io/ |
---
反混淆 / 脱壳
| 资源 | 说明 | 链接 |
|---|---|---|
| UPX | 最常见的壳,upx -d 脱壳 | https://upx.github.io/ |
| unipacker | 通用 PE 脱壳器 | https://github.com/unipacker/unipacker |
| de4dot | .NET 反混淆 | https://github.com/de4dot/de4dot |
| JADX | Android DEX 反混淆 | https://github.com/skylot/jadx |
| JEB | 商业 Android/ARM 反编译器 | https://www.pnfsoftware.com/ |
| Miasm | 逆向工程框架(IR/符号执行/反混淆) | https://github.com/cea-sec/miasm |
| OLLVM 反混淆 | 控制流平坦化/虚假控制流对抗 | 用 angr/Triton 符号执行恢复 |
---
在线分析平台
| 平台 | 说明 | 链接 |
|---|---|---|
| VirusTotal | 多引擎扫描 + 行为分析 | https://www.virustotal.com/ |
| Joe Sandbox | 自动化恶意软件分析 | https://www.joesandbox.com/ |
| ANY.RUN | 交互式在线沙箱 | https://any.run/ |
| Hybrid Analysis | 免费恶意软件分析 | https://www.hybrid-analysis.com/ |
| Compiler Explorer | 看编译器输出 | https://godbolt.org/ |
| Dogbolt | 多反编译器对比(IDA/Ghidra/Binary Ninja) | https://dogbolt.org/ |
---
学习路径
入门(0-3 个月)
1. Reverse Engineering for Beginners — 免费电子书 2. Azeria Labs ARM 教程 — ARM 汇编基础 3. Nightmare — CTF 逆向/Pwn 教程 4. crackmes.one — 逆向练习题
进阶(3-12 个月)
1. Practical Binary Analysis — 实战二进制分析 2. The IDA Pro Book — IDA 深度使用 3. Malware Unicorn RE101 — 恶意软件逆向 4. pwnable.kr / pwnable.tw — Pwn 练习
高级
1. Modern Binary Exploitation — RPI 课程 2. How to Hack Like a Ghost — 高级渗透 3. Windows Internals — Windows 内核 4. 实战:分析真实恶意软件样本(MalwareBazaar)
---
速查表
| 速查表 | 链接 |
|---|---|
| x86/x64 指令速查 | https://www.felixcloutier.com/x86/ |
| ARM64 指令速查 | https://developer.arm.com/documentation/ddi0602/latest |
| Linux syscall 表 (x64) | https://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/ |
| Linux syscall 表 (ARM64) | https://arm64.syscall.sh/ |
| GDB 速查 | https://darkdust.net/files/GDB%20Cheat%20Sheet.pdf |
| radare2 速查 | 本包 radare2/references/cheatsheet.md |
| IDA 快捷键 | https://hex-rays.com/products/ida/support/freefiles/IDA_Pro_Shortcuts.pdf |
| Ghidra 快捷键 | Ghidra 内置 Help → Keyboard Shortcuts |
加解密 / 编解码工具速查
逆向和 CTF 中经常遇到加密/编码/哈希数据。本文档按场景列出最实用的工具。
---
自动识别 + 解密(不知道用了什么加密时)
| 工具 | Stars | 用途 | 链接 |
|---|---|---|---|
| Ciphey | 18k+ | AI 自动识别并解密(支持 50+ 编码/加密/哈希) | https://github.com/Ciphey/Ciphey |
| CyberChef | 29k+ | 在线/离线编解码瑞士军刀(拖拽式操作) | https://github.com/gchq/CyberChef |
| dcode.fr | — | 在线 900+ 密码/编码/数学工具 | https://www.dcode.fr/ |
Ciphey 使用
pip install ciphey
# 自动检测并解密
ciphey -t "密文"
# 从文件读取
ciphey -f encrypted.txtCiphey 支持:Base64/32/16、Caesar、Vigenere、XOR、AES(弱密钥)、Morse、Binary、Hex、URL encoding、HTML entities、哈希识别等。
CyberChef 使用
在线版:https://gchq.github.io/CyberChef/
离线版:下载 GitHub Release 的 HTML 文件直接打开
常用 Recipe:
- From Base64 → 解 Base64
- XOR → 异或解密(可暴力尝试 key)
- AES Decrypt → AES 解密
- Magic → 自动检测编码类型---
哈希识别与破解
| 工具 | 用途 | 链接 |
|---|---|---|
| hashID | 识别哈希类型(MD5/SHA/bcrypt 等) | https://github.com/psypanda/hashID |
| hash-identifier | 同上,Python 版 | https://github.com/blackploit/hash-identifier |
| haiti | 现代哈希识别工具(更准确) | gem install haiti |
| Hashcat | GPU 哈希破解 | https://hashcat.net/ |
| John the Ripper | CPU 哈希破解 | https://www.openwall.com/john/ |
| hashes.com | 在线哈希查询(彩虹表) | https://hashes.com/ |
# 识别哈希类型
hashid '5f4dcc3b5aa765d61d8327deb882cf99'
# 输出: [+] MD5
# haiti(更准确)
haiti '5f4dcc3b5aa765d61d8327deb882cf99'
# Hashcat 破解
hashcat -m 0 hash.txt rockyou.txt # MD5
hashcat -m 1000 hash.txt rockyou.txt # NTLM---
RSA 攻击
| 工具 | 用途 | 链接 |
|---|---|---|
| RsaCtfTool | RSA 自动攻击(20+ 攻击方式) | https://github.com/Ganapati/RsaCtfTool |
| SageMath | 数学计算(大数分解/椭圆曲线) | https://www.sagemath.org/ |
| factordb.com | 在线大数分解查询 | http://factordb.com/ |
| yafu | 本地大数分解 | https://github.com/bbuhrow/yafu |
# RsaCtfTool 自动攻击
python RsaCtfTool.py --publickey pub.pem --private
python RsaCtfTool.py --publickey pub.pem --uncipherfile cipher.txt
# 支持的攻击:
# Wiener、Boneh-Durfee、Fermat、Pollard p-1、Williams p+1
# Common modulus、Small q、Hastads、Noveltyprimes 等---
XOR 分析
| 工具 | 用途 | 链接 |
|---|---|---|
| xortool | XOR 密钥长度猜测 + 已知明文攻击 | https://github.com/hellman/xortool |
| CyberChef XOR | 可视化 XOR 操作 | CyberChef 内置 |
# 猜测 XOR key 长度
xortool encrypted_file
# 用猜测的 key 长度解密
xortool -l 4 -c 00 encrypted_file
# 已知明文攻击(知道部分明文)
xortool-xor -f encrypted -s "known_plaintext"---
古典密码
| 密码类型 | 工具 | 说明 |
|---|---|---|
| Caesar | CyberChef / dcode.fr | 暴力 25 种偏移 |
| Vigenere | dcode.fr / Ciphey | 需要猜 key 长度 |
| Substitution | quipqiup.com | 频率分析自动求解 |
| Enigma | dcode.fr | 在线模拟器 |
| Rail Fence | dcode.fr / CyberChef | 栅栏密码 |
| Playfair | dcode.fr | 需要 key |
| Morse | CyberChef | 点划转文字 |
| Bacon | dcode.fr | 二进制隐写 |
| ROT13/47 | CyberChef / tr | 简单替换 |
---
编码识别与转换
| 编码 | 识别特征 | 解码方式 |
|---|---|---|
| Base64 | 末尾 = 或 ==,字符集 A-Za-z0-9+/ | base64 -d / CyberChef |
| Base32 | 大写字母 + 2-7,末尾 = | CyberChef |
| Base58 | 无 0/O/I/l,常见于 Bitcoin | CyberChef |
| Hex | 只有 0-9a-f,长度为偶数 | xxd -r -p / CyberChef |
| URL encoding | %XX 格式 | urldecode / CyberChef |
| HTML entities | &#XX; 或 & 格式 | CyberChef |
| Unicode escape | \uXXXX 格式 | Python decode('unicode_escape') |
| JWT | xxxxx.yyyyy.zzzzz(三段 Base64URL) | jwt.io / CyberChef |
| Brainfuck | 只有 ><+-.,[] 八个字符 | 在线解释器 |
| Ook! | 只有 Ook. Ook! Ook? | 在线解释器 |
---
逆向中的加密识别
通过常量识别算法
| 常量/特征 | 算法 |
|---|---|
0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476 | MD5 |
0x6A09E667, 0xBB67AE85, 0x3C6EF372 | SHA-256 |
0x63, 0x7C, 0x77, 0x7B (S-Box 开头) | AES |
0x243F6A88 (π 的十六进制) | Blowfish |
0xB7E15163, 0x9E3779B9 | RC5/RC6/TEA |
0x61707865 ("expa") | ChaCha20/Salsa20 |
0xC6EF3720 | XTEA |
通过行为识别
| 行为特征 | 可能的算法 |
|---|---|
| 256 字节查找表 + swap 操作 | RC4 |
| 16 字节块 + 多轮置换 | AES |
| Feistel 结构(左右交换) | DES/Blowfish/TEA |
| 大数乘法/模幂 | RSA |
| 椭圆曲线点运算 | ECDSA/ECDH |
| 固定 64 轮循环 | TEA/XTEA |
| 32 轮 + delta 常量 | XTEA |
---
自动化密码分析
| 工具 | 用途 | 链接 |
|---|---|---|
| FeatherDuster | 自动化密码分析框架 | https://github.com/nccgroup/featherduster |
| PkCrack | ZIP 已知明文攻击 | https://www.unix-ag.uni-kl.de/~conrad/krypto/pkcrack.html |
| bkcrack | ZIP 已知明文攻击(现代版) | https://github.com/kimci86/bkcrack |
| z3 | SMT 求解器(约束求解) | https://github.com/Z3Prover/z3 |
| angr | 符号执行(自动求解输入) | https://angr.io/ |
---
快速决策树
拿到一段未知数据:
1. 看长度和字符集
- 只有 hex 字符 → 可能是 hex 编码或哈希
- 末尾有 = → Base64
- 三段点分 → JWT
- 32/40/64 字符 hex → 哈希(MD5/SHA1/SHA256)
2. 用 Ciphey 自动尝试
ciphey -t "数据"
3. 如果 Ciphey 失败 → 用 CyberChef Magic 模式
4. 如果是哈希 → hashID 识别类型 → Hashcat/John 破解
5. 如果是 RSA → RsaCtfTool 自动攻击
6. 如果是 XOR → xortool 分析 key
7. 如果是自定义加密 → IDA/Ghidra 逆向算法 → 手写解密脚本---
在线资源
| 资源 | 链接 | 用途 |
|---|---|---|
| CyberChef | https://gchq.github.io/CyberChef/ | 万能编解码 |
| dcode.fr | https://www.dcode.fr/ | 900+ 密码工具 |
| quipqiup | https://quipqiup.com/ | 替换密码自动求解 |
| factordb | http://factordb.com/ | RSA 大数分解 |
| jwt.io | https://jwt.io/ | JWT 解码/验证 |
| hashes.com | https://hashes.com/ | 哈希反查 |
| crackstation | https://crackstation.net/ | 在线哈希破解 |
ELF 二进制深度分析参考
逆向 Linux/Android ELF 文件时的结构解析、反分析对抗识别和分析技巧。
---
ELF 结构速查
文件头 (ELF Header)
偏移 大小 字段 说明
0x00 4 e_ident[EI_MAG] Magic: 7f 45 4c 46 ("\x7fELF")
0x04 1 e_ident[EI_CLASS] 1=32bit, 2=64bit
0x05 1 e_ident[EI_DATA] 1=LE, 2=BE
0x10 2 e_type 2=EXEC, 3=DYN(PIE/SO), 4=CORE
0x12 2 e_machine 0x03=x86, 0x3E=x86_64, 0xB7=AArch64, 0x28=ARM
0x18 8 e_entry 入口点虚拟地址
0x20 8 e_phoff 程序头表偏移
0x28 8 e_shoff 节头表偏移(strip 后可能为 0)
0x38 2 e_phnum 程序头数量
0x3C 2 e_shnum 节头数量程序头 (Program Header)
类型值 名称 说明
0x01 PT_LOAD 可加载段(代码/数据)
0x02 PT_DYNAMIC 动态链接信息
0x03 PT_INTERP 解释器路径(/lib/ld-linux.so)
0x04 PT_NOTE 辅助信息
0x06 PT_PHDR 程序头表自身
0x6474e550 PT_GNU_EH_FRAME 异常处理
0x6474e551 PT_GNU_STACK 栈可执行标记
0x6474e552 PT_GNU_RELRO 只读重定位常见节 (Sections)
| 节名 | 说明 |
|---|---|
.text | 代码段 |
.rodata | 只读数据(字符串常量) |
.data | 已初始化全局变量 |
.bss | 未初始化全局变量 |
.plt / .got | 动态链接跳转表 |
.init_array | 构造函数指针数组 |
.fini_array | 析构函数指针数组 |
.dynamic | 动态链接信息 |
.symtab / .dynsym | 符号表 |
.strtab / .dynstr | 字符串表 |
---
反分析手法识别
常见 ELF 反分析技术
| 手法 | 特征 | 对抗方式 |
|---|---|---|
| 损坏程序头 | PHDR 填充垃圾数据(如 0x0a) | 手动修复或忽略损坏的 PHDR |
| 无 section header | e_shoff = 0, e_shnum = 0 | 只依赖程序头分析,不依赖 section |
| 去符号 (strip) | 无 .symtab,函数名全丢 | GoReSym(Go) / 签名匹配 / FLIRT |
| 静态链接 | 无 .dynamic,体积巨大 | 用 FLIRT/Lumina 识别库函数 |
| 伪装文件类型 | 后缀 .sh/.txt/.jpg | 用 file 命令 / magic bytes 判断 |
| UPX 加壳 | 包含 UPX! 标记 | upx -d 脱壳 |
| 自定义壳 | 入口点跳转到解压代码 | 动态运行到 OEP 后 dump |
| 反调试 | ptrace(TRACEME) | LD_PRELOAD hook / patch |
| 反虚拟机 | 检查 /proc/cpuinfo | 修改 cpuinfo 或 hook 读取 |
| 代码加密 | 运行时解密 .text | 断点在解密后 dump |
识别自解压/自修改代码
特征:
1. 入口点附近有 mmap(PROT_READ|PROT_WRITE|PROT_EXEC) 调用
2. 紧接着有 memcpy 或循环拷贝
3. 然后 mprotect 改权限
4. 最后 br/jmp 到新映射的地址
分析策略:
1. 找到 mmap 调用 → 记录返回的地址
2. 在 mprotect(PROT_EXEC) 后下断点
3. dump 解压后的内存区域
4. 作为新的二进制分析---
ARM64 (AArch64) 逆向速查
寄存器
| 寄存器 | 用途 |
|---|---|
| x0-x7 | 参数/返回值 |
| x8 | 间接结果(syscall 号) |
| x9-x15 | 临时寄存器 |
| x16-x17 | IP0/IP1(PLT 跳转) |
| x18 | 平台寄存器(Android: shadow call stack) |
| x19-x28 | 被调用者保存 |
| x29 (FP) | 帧指针 |
| x30 (LR) | 链接寄存器(返回地址) |
| SP | 栈指针 |
| PC | 程序计数器 |
常见指令模式
函数序言:
stp x29, x30, [sp, #-N]! # 保存 FP 和 LR
mov x29, sp # 设置帧指针
函数尾声:
ldp x29, x30, [sp], #N # 恢复 FP 和 LR
ret # 返回(br x30)
系统调用:
mov x8, #NR # syscall 号
svc #0 # 触发 syscall
条件分支:
cmp x0, #0
b.eq label # 等于跳转
b.ne label # 不等于跳转
cbz x0, label # x0 == 0 跳转
cbnz x0, label # x0 != 0 跳转
地址加载:
adrp x0, page # 加载页地址高位
add x0, x0, #offset # 加低 12 位偏移
ldr x0, [x1, #offset] # 从内存加载Linux ARM64 系统调用号
| 号码 | 名称 | 说明 |
|---|---|---|
| 56 | openat | 打开文件 |
| 63 | read | 读取 |
| 64 | write | 写入 |
| 57 | close | 关闭 |
| 222 | mmap | 内存映射 |
| 226 | mprotect | 修改内存权限 |
| 117 | ptrace | 进程跟踪 |
| 220 | clone | 创建进程/线程 |
| 221 | execve | 执行程序 |
| 93 | exit | 退出 |
| 94 | exit_group | 退出进程组 |
---
常见压缩/打包算法识别
| 算法 | 识别特征 | 解压方式 |
|---|---|---|
| LZSS | 位流 + 字面量/匹配标记 | 自定义解压器(如本报告) |
| ZLIB/Deflate | Magic: 78 01/78 9C/78 DA | zlib.decompress() |
| GZIP | Magic: 1F 8B | gzip -d / gunzip |
| LZ4 | Magic: 04 22 4D 18 | lz4 -d |
| LZMA/XZ | Magic: FD 37 7A 58 5A 00 (XZ) | xz -d / lzma -d |
| Brotli | 无固定 magic,看上下文 | brotli -d |
| Zstandard | Magic: 28 B5 2F FD | zstd -d |
| UPX | 字符串 UPX! | upx -d |
| 自定义 | 入口点有解压循环 | 逆向算法后写解压器 |
识别自定义压缩的线索
1. 入口点附近有循环 + 位操作(移位、AND、OR)
2. 有"滑动窗口"回拷(从输出缓冲区往回读)→ LZ 系列
3. 有频率表/霍夫曼树构建 → Deflate/Huffman
4. 有固定大小块处理 → 块压缩(LZ4/Snappy)
5. 有算术编码特征(区间缩小)→ LZMA/ANS---
Linux 进程注入技术
mmap + 代码注入
流程:
1. mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0)
2. 将 shellcode/payload 写入映射区域
3. mprotect(addr, size, PROT_READ|PROT_EXEC) # 改为可执行
4. 跳转到映射地址执行
特征:
- mmap 返回值被保存
- 紧接着有 memcpy 或循环写入
- 然后 mprotect 改权限
- 最后 br/blr 到该地址ptrace 注入
流程:
1. ptrace(PTRACE_ATTACH, target_pid)
2. waitpid(target_pid)
3. ptrace(PTRACE_GETREGS, target_pid, ®s)
4. 修改 regs.pc 指向注入代码
5. ptrace(PTRACE_SETREGS, target_pid, ®s)
6. ptrace(PTRACE_CONT, target_pid)
特征:
- 打开 /proc/<pid>/mem 或使用 ptrace
- 读取/修改目标进程寄存器
- 写入 shellcode 到目标进程空间/proc/self/mem 自修改
流程:
1. open("/proc/self/mem", O_RDWR)
2. lseek(fd, target_addr, SEEK_SET)
3. write(fd, new_code, size)
用途:
- 绕过 W^X 保护(mmap 的页不能同时 W+X)
- 修改自身代码段(.text 通常是只读的)
- 运行时 patch 指令---
分析大型 ELF 的策略
对于 5MB+ 的大型二进制:
1. 快速侦察(5 分钟)
- file / rabin2 -I → 架构、类型、保护
- strings | grep -i "error\|fail\|http\|/proc\|/dev" → 关键字符串
- rabin2 -i → 导入函数(如果有)
- rabin2 -E → 导出函数
2. 结构分析(10 分钟)
- readelf -l → 程序头(LOAD 段布局)
- 入口点附近代码 → 是否有解压/解密
- 找 .init_array → 构造函数(可能有反调试)
3. 定位关键逻辑
- 从字符串交叉引用入手
- 从系统调用(mmap/ptrace/open)入手
- 从网络函数(connect/send/recv)入手
4. 分而治之
- 如果是自解压 → 先解压,分析 payload
- 如果是多模块 → 按功能分块分析
- 用 binary-diff 对比不同版本---
工具命令速查
# 基本信息
file binary
readelf -h binary # ELF 头
readelf -l binary # 程序头
readelf -S binary # 节头(如果有)
rabin2 -I binary # 综合信息
# 字符串
strings -a binary | less
rabin2 -z binary # 数据段字符串
rabin2 -zz binary # 全文件字符串
# 反汇编
r2 -A binary # radare2 分析
objdump -d binary # GNU 反汇编
aarch64-linux-gnu-objdump -d binary # ARM64 交叉反汇编
# 动态分析
strace -f ./binary # 系统调用跟踪
ltrace -f ./binary # 库函数跟踪
qemu-aarch64 -strace ./binary # ARM64 模拟执行
# 内存 dump
gdb -p <pid> -ex "dump memory out.bin 0xADDR 0xADDR+SIZE" -ex quit
# 修复损坏的 ELF
# 手动修改 e_phnum 或 patch 损坏的 PHDR
python -c "
import struct
with open('binary', 'r+b') as f:
f.seek(0x38) # e_phnum offset (64-bit)
f.write(struct.pack('<H', 2)) # 修改为正确的 PHDR 数量
"Reverse Engineering Field Notes
Detailed quick notes that support `SKILL.md`. Read this file after triage, not before.
Table of Contents
- Binary Types
- Python .pyc
- WASM
- Android APK
- Flutter APK (Dart AOT)
- .NET
- Packed (UPX)
- Tauri Packed Desktop Apps
- Anti-Debugging Bypass
- Specialized Patterns
- S-Box / Keystream Patterns
- Custom VM Analysis
- Python Bytecode Reversing
- Signal-Based Binary Exploration
- Malware Anti-Analysis Bypass via Patching
- Expected Values Tables
- x86-64 Gotchas
- Iterative Solver Pattern
- Unicorn Emulation (Complex State)
- Multi-Stage Shellcode Loaders
- Timing Side-Channel Attack
- Unstripped Binary Information Leaks
- Custom Mangle Function Reversing
- Rust serde_json Schema Recovery
- Position-Based Transformation Reversing
- Hex-Encoded String Comparison
- CTF Case Notes
- Embedded ZIP + XOR License Decryption
- Stack String Deobfuscation (.rodata XOR Blob)
- Prefix Hash Brute-Force
- Mathematical Convergence Bitmap
- RISC-V Binary Analysis
- Kernel Module Maze Solving
- Multi-Threaded VM with Channels
- CVP/LLL Lattice for Constrained Integer Validation
- Decision Tree Function Obfuscation
- Android JNI RegisterNatives Obfuscation
- Multi-Layer Self-Decrypting Binary
- GLSL Shader VM with Self-Modifying Code
- GF(2^8) Gaussian Elimination for Flag Recovery
- Z3 for Single-Line Python Boolean Circuit
- Sliding Window Popcount Differential Propagation
- Ruby/Perl Polyglot Constraint Satisfaction
- Verilog/Hardware RE
- Custom binfmt Kernel Module with RC4 Flat Binaries
- Hash-Resolved Imports / No-Import Ransomware
- ELF Section Header Corruption for Anti-Analysis
- Brainfuck Character-by-Character Static Analysis
- Brainfuck Side-Channel via Read Count Oracle
- Brainfuck Comparison Idiom Detection
- Backdoored Shared Library Detection
- Go Binary Reversing
- Go Binary UUID Patching for C2 Enumeration
- D Language Binary Reversing
- Rust Binary Reversing
- Frida Dynamic Instrumentation
- Frida Firebase Cloud Functions Bypass
- angr Symbolic Execution
- Qiling Emulation
- VMProtect / Themida Analysis
- Binary Diffing
- Advanced GDB (pwndbg, rr)
- macOS / iOS Reversing
- Embedded / IoT Firmware RE
- Kernel Driver Reversing
- Swift / Kotlin Binary Reversing
- INT3 Patch + Coredump Brute-Force Oracle
- Signal Handler Chain + LD_PRELOAD Oracle
- Font Ligature Exploitation
- Instruction Counter as Cryptographic State
- Burrows-Wheeler Transform Inversion
- FRACTRAN Program Inversion
- Opcode-Only Trace Reconstruction
- Thread Race Signed Integer Overflow
- ESP32/Xtensa Firmware Reversing
- Custom VM Bytecode Lifting to LLVM IR
- SIGFPE Signal Handler Side-Channel
- Batch Crackme Automation via objdump
- Android DEX Runtime Bytecode Patching
- Fork + Pipe + Dead Branch Anti-Analysis
- Web/CTF Auth Bypass Case Notes
- Signed Cookie Key Reuse: access token to admin_session
- Web Phishing Infrastructure
- Phishing Panel: {domain_a} / {domain_b}
Binary Types
Python .pyc
Disassemble with marshal.load() + dis.dis(). Header: 8 bytes (2.x), 12 (3.0-3.6), 16 (3.7+). See languages.md.
WASM
wasm2c checker.wasm -o checker.c
gcc -O3 checker.c wasm-rt-impl.c -o checker
# WASM patching (challenge binaries):
wasm2wat main.wasm -o main.wat # Binary → text
# Edit WAT: flip comparisons, change constants
wat2wasm main.wat -o patched.wasm # Text → binaryAndroid APK
apktool d app.apk -o decoded/ for resources; jadx app.apk for Java decompilation. Check decoded/res/values/strings.xml for flags. See tools.md.
Flutter APK (Dart AOT)
If lib/arm64-v8a/libapp.so + libflutter.so present, use Blutter: python3 blutter.py path/to/app/lib/arm64-v8a out_dir. Outputs reconstructed Dart symbols + Frida script. See tools.md.
.NET
- dnSpy - debugging + decompilation
- ILSpy - decompiler
Packed (UPX)
upx -d packed -o unpackedIf unpacking fails, inspect UPX metadata first: verify UPX section names, header fields, and version markers are intact. If metadata looks tampered or uncertain, review UPX source on GitHub to identify likely modification points.
Tauri Packed Desktop Apps
Tauri embeds Brotli-compressed frontend assets in the executable. Find index.html xrefs to locate asset index table, dump blobs, Brotli decompress. Reference: tauri-codegen/src/embedded_assets.rs.
Anti-Debugging Bypass
Common checks:
IsDebuggerPresent()/ PEB.BeingDebugged / NtQueryInformationProcess (Windows)ptrace(PTRACE_TRACEME)//proc/self/statusTracerPid (Linux)- TLS callbacks (run before main — check PE TLS Directory)
- Timing checks (
rdtsc,clock_gettime,GetTickCount) - Hardware breakpoint detection (DR0-DR3 via GetThreadContext)
- INT3 scanning / code self-hashing (CRC over .text section)
- Signal-based: SIGTRAP handler, SIGALRM timeout, SIGSEGV for real logic
- Frida/DBI detection:
/proc/self/mapsscan, port 27042, inline hook checks
Bypass: Set breakpoint at check, modify register to bypass conditional. pwntools patch: elf.asm(elf.symbols.ptrace, 'ret') to replace function with immediate return. See patterns.md.
For comprehensive anti-analysis techniques and bypasses (30+ methods with code), see anti-analysis.md.
Specialized Patterns
S-Box / Keystream Patterns
Xorshift32: Shifts 13, 17, 5 Xorshift64: Shifts 12, 25, 27 Magic constants: 0x2545f4914f6cdd1d, 0x9e3779b97f4a7c15
Custom VM Analysis
1. Identify structure: registers, memory, IP 2. Reverse executeIns for opcode meanings 3. Write disassembler mapping opcodes to mnemonics 4. Often easier to bruteforce than fully reverse 5. Look for the bytecode file loaded via command-line arg
See patterns.md for VM workflow, opcode tables, and state machine BFS.
Sequential key-chain brute-force: When a VM validates input in small blocks (e.g., 3 bytes = 2^24 candidates) with each block's output key feeding the next, brute-force each block sequentially with OpenMP parallelization. Compile solver with gcc -O3 -march=native -fopenmp. See patterns-ctf-3.md.
Python Bytecode Reversing
XOR flag checkers with interleaved even/odd tables are common. See languages.md for bytecode analysis tips and reversing patterns.
Signal-Based Binary Exploration
Binary uses UNIX signals as binary tree navigation; hook sigaction via LD_PRELOAD, DFS by sending signals. See patterns.md.
Malware Anti-Analysis Bypass via Patching
Flip JNZ/JZ (0x75/0x74), change sleep values, patch environment checks in Ghidra (Ctrl+Shift+G). See patterns.md.
Expected Values Tables
Locate with objdump -s -j .rodata binary | less — look near comparison instructions, size matches flag length.
x86-64 Gotchas
Sign extension and 32-bit truncation pitfalls. See patterns.md for details and code examples.
Iterative Solver Pattern
Try each byte (0-255) per position, match against expected output. Uniform transform shortcut: if one input byte only changes one output byte, build 0..255 mapping then invert. See patterns.md for full implementation.
Unicorn Emulation (Complex State)
from unicorn import * -- map segments, set up stack, hook to trace. Mixed-mode pitfall: 64-bit stub jumping to 32-bit via retf requires switching to UC_MODE_32 and copying GPRs + EFLAGS + XMM regs. See tools.md.
Multi-Stage Shellcode Loaders
Nested shellcode with XOR decode loops; break at call rax, bypass ptrace with set $rax=0, extract flag from mov instructions. See patterns.md.
Timing Side-Channel Attack
Validation time varies per correct character; measure elapsed time per candidate to recover flag byte-by-byte. See patterns.md.
Unstripped Binary Information Leaks
Pattern: Debug info and file paths leak author identity. Quick checks: strings binary | grep "/home/" (home dirs), file binary (stripped?), readelf -S binary | grep debug (debug sections).
Custom Mangle Function Reversing
Binary mangles input 2 bytes at a time with running state; extract target from .rodata, write inverse function. See patterns.md.
Rust serde_json Schema Recovery
Disassemble serde Visitor implementations to recover expected JSON schema; field names in order reveal flag. See languages-platforms.md.
Position-Based Transformation Reversing
Binary adds/subtracts position index; reverse by undoing per-index offset. See patterns.md.
Hex-Encoded String Comparison
Input converted to hex, compared against constant. Decode with xxd -r -p. See patterns.md.
CTF Case Notes
Embedded ZIP + XOR License Decryption
Binary with named symbols (EMBEDDED_ZIP, ENCRYPTED_MESSAGE) in .rodata → extract ZIP containing license, XOR encrypted message with license bytes to recover flag. No execution needed. See patterns-ctf-2.md.
Stack String Deobfuscation (.rodata XOR Blob)
Binary mmaps .rodata blob, XOR-deobfuscates, uses it to validate input. Reimplement verification loop with pyelftools to extract blob. Look for 0x9E3779B9, 0x85EBCA6B constants and rol32(). See patterns-ctf-2.md.
Prefix Hash Brute-Force
Binary hashes every prefix independently. Recover one character at a time by matching prefix hashes. See patterns-ctf-2.md.
Mathematical Convergence Bitmap
Pattern: Binary classifies coordinate pairs by Newton's method convergence (e.g., z^3-1=0). Grid of pass/fail results renders ASCII art flag. Key: the binary is a classifier, not a checker — reverse the math and visualize. See patterns-ctf.md.
RISC-V Binary Analysis
Statically linked, stripped RISC-V ELF. Use Capstone with CS_MODE_RISCVC | CS_MODE_RISCV64 for mixed compressed instructions. Emulate with qemu-riscv64. Watch for fake flags and XOR decryption with incremental keys. See tools.md.
Kernel Module Maze Solving
Rust kernel module implements maze via device ioctls. Enumerate commands dynamically, build DFS solver with decoy avoidance, deploy as minimal static binary (raw syscalls, no libc). See patterns-ctf.md.
Multi-Threaded VM with Channels
Custom VM with 16+ threads communicating via futex channels. Trace data flow across thread boundaries, extract constants from GDB, watch for inverted validity logic, solve via BFS state space search. See patterns-ctf.md.
CVP/LLL Lattice for Constrained Integer Validation
Binary validates flag via matrix multiplication with 64-bit coefficients; solutions must be printable ASCII. Use LLL reduction + CVP in SageMath to find nearest lattice point in the constrained range. Two-phase pattern: Phase 1 recovers AES key, Phase 2 decrypts custom VM bytecode with another linear system (mod 2^32). See patterns-ctf-2.md.
Decision Tree Function Obfuscation
~200+ auto-generated functions routing input through polynomial comparisons. Script extraction via Ghidra headless rather than reversing each function manually. Constraint propagation from known output format cascades through arithmetic constraints. See patterns-ctf-2.md.
Android JNI RegisterNatives Obfuscation
RegisterNatives in JNI_OnLoad hides which C++ function handles each Java native method (no standard Java_com_pkg_Class_method symbol). Find the real handler by tracing JNI_OnLoad → RegisterNatives → fnPtr. Use x86_64 .so from APK for best Ghidra decompilation. See languages-platforms.md.
Multi-Layer Self-Decrypting Binary
N-layer binary where each layer decrypts the next using user-provided key bytes + SHA-NI. Use oracle (correct key → valid code with expected pattern). JIT execution with fork-per-candidate COW isolation for speed. See patterns-ctf-2.md.
GLSL Shader VM with Self-Modifying Code
Pattern: WebGL2 fragment shader implements Turing-complete VM on a 256x256 RGBA texture (program memory + VRAM). Self-modifying code (STORE opcode) patches drawing instructions. GPU parallelism causes write conflicts — emulate sequentially in Python to recover full output. See patterns-ctf-3.md.
GF(2^8) Gaussian Elimination for Flag Recovery
Pattern: Binary performs Gaussian elimination over GF(2^8) with the AES polynomial (0x11b). Matrix + augmentation vector in .rodata; solution vector is the flag. Look for constant 0x1b in disassembly. Addition is XOR, multiplication uses polynomial reduction. See patterns-ctf-2.md.
Z3 for Single-Line Python Boolean Circuit
Pattern: Single-line Python (2000+ semicolons) with walrus operator chains validates flag as big-endian integer via boolean circuit. Obfuscated XOR (a | b) & ~(a & b). Split on semicolons, translate to Z3 symbolically, solve in under a second. See patterns-ctf-3.md.
Sliding Window Popcount Differential Propagation
Pattern: Binary validates input via expected popcount for each position of a 16-bit sliding window. Popcount differences create a recurrence: bit[i+16] = bit[i] + (data[i+1] - data[i]). Brute-force ~4000-8000 valid initial 16-bit windows; each determines the entire bit sequence. See patterns-ctf-3.md.
Ruby/Perl Polyglot Constraint Satisfaction
Pattern: Single file valid in both Ruby and Perl, each imposing different constraints on a key. Exploits =begin/=end (Ruby block comment) vs =begin/=cut (Perl POD) to run different code per interpreter. Intersect constraints from both languages to recover the unique key. See languages-platforms.md.
Verilog/Hardware RE
Pattern: Verilog HDL source for state machines with hidden conditions gated on shift register history. Analyze always @(posedge clk) blocks and case statements to find correct input sequences. See languages-platforms.md.
Custom binfmt Kernel Module with RC4 Flat Binaries
Pattern: Kernel module registers binfmt handler for encrypted flat binaries. Reverse the .ko to find RC4 key (in movabs immediates), decrypt the flat binary, import at the fixed virtual address from the module's vm_mmap call. See patterns-ctf.md.
Hash-Resolved Imports / No-Import Ransomware
Pattern: Binary with zero visible imports resolves APIs via symbol name hashing at runtime. Skip the hash reversing — hook OpenSSL functions via LD_PRELOAD in Docker to capture AES keys directly. See patterns-ctf.md.
ELF Section Header Corruption for Anti-Analysis
Pattern: Corrupted section headers crash analysis tools but program headers are intact so binary runs normally. Patch e_shoff to zero or use readelf -l (program headers only). Flag hidden after corrupted sections with magic marker + XOR. See patterns-ctf.md.
Brainfuck Character-by-Character Static Analysis
Pattern: BF programs validating input have , (read char) followed by + operations whose count = expected ASCII value. Extract increment counts per input position to recover expected input without execution. See languages.md.
Brainfuck Side-Channel via Read Count Oracle
Pattern: BF input validators read more bytes when a character is correct. Count , operations per candidate — highest read count = correct byte. Character-by-character recovery. See languages.md.
Brainfuck Comparison Idiom Detection
Pattern: Compiled BF uses fixed idioms for equality checks (<[-<->] +<[>-<[-]]>[-<+>]). Instrument interpreter to detect patterns and extract comparison operands (expected flag bytes). See languages.md.
Backdoored Shared Library Detection
Binary works in GDB but fails when run normally (suid)? Check ldd for non-standard libc paths, then strings | diff the suspicious vs. system library to find injected code/passwords. See patterns-ctf.md.
Go Binary Reversing
Large static binary with go.buildid? Use GoReSym to recover function names (works even on stripped binaries). Go strings are {ptr, len} pairs — not null-terminated. Look for main.main, runtime.gopanic, channel ops (runtime.chansend1/chanrecv1). Use Ghidra golang-loader plugin for best results. See languages-compiled.md.
Go Binary UUID Patching for C2 Enumeration
Pattern: Go C2 client with UUID from -ldflags -X. Binary-patch UUID bytes (same length), register with C2, enumerate clients/files via API. See languages-compiled.md.
D Language Binary Reversing
D language binaries have unique symbol mangling (not C++ style). Template-heavy, many function variants. Look for _D prefix in symbols. See languages-compiled.md.
Rust Binary Reversing
Binary with core::panicking strings and _ZN mangled symbols? Use rustfilt for demangling. Panic messages contain source paths and line numbers — strings binary | grep "panicked" is the fastest approach. Option/Result enums use discriminant byte (0=None/Err, 1=Some/Ok). See languages-compiled.md.
Frida Dynamic Instrumentation
Hook runtime functions without modifying binary. frida -f ./binary -l hook.js to spawn with instrumentation. Hook strcmp/memcmp to capture expected values, bypass anti-debug by replacing ptrace return value, scan memory for flag patterns, replace validation functions. See tools-dynamic.md.
Frida Firebase Cloud Functions Bypass
Pattern: Android app validates via Firebase Cloud Functions. Post-login Frida hook constructs valid payload (UID + value + timestamp) and calls Cloud Function directly, bypassing QR/payment validation. See languages-platforms.md.
angr Symbolic Execution
Automatic path exploration to find inputs satisfying constraints. Load binary with angr.Project, set find/avoid addresses, call simgr.explore(). Constrain input to printable ASCII and known prefix for faster solving. Hook expensive functions (crypto, I/O) to prevent path explosion. See tools-dynamic.md.
Qiling Emulation
Cross-platform binary emulation with OS-level support (syscalls, filesystem). Emulate Linux/Windows/ARM/MIPS binaries on any host. No debugger artifacts — bypasses all anti-debug by default. Hook syscalls and addresses with Python API. See tools-dynamic.md.
VMProtect / Themida Analysis
VMProtect virtualizes code into custom bytecode. Identify VM entry (pushad-like), find handler table (large indirect jump), trace handlers dynamically. For CTF, focus on tracing operations on input rather than full devirtualization. Themida: dump at OEP with ScyllaHide + Scylla. See tools-advanced.md.
Binary Diffing
BinDiff and Diaphora compare two binaries to highlight changes. Essential when challenge provides patched/original versions. Export from IDA/Ghidra, diff to find vulnerability or hidden functionality. See tools-advanced.md.
Advanced GDB (pwndbg, rr)
pwndbg: context, vmmap, search -s "flag{", telescope $rsp. GEF alternative. Reverse debugging with rr record/rr replay — step backward through execution. Python scripting for brute-force and automated tracing. See tools-advanced.md.
macOS / iOS Reversing
Mach-O binaries: otool -l for load commands, class-dump for Objective-C headers. Swift: swift demangle for symbols. iOS apps: decrypt FairPlay DRM with frida-ios-dump, bypass jailbreak detection with Frida hooks. Re-sign patched binaries with codesign -f -s -. See platforms.md.
Embedded / IoT Firmware RE
binwalk -Me firmware.bin for recursive extraction. Hardware: UART/JTAG/SPI flash for firmware dumps. Filesystems: SquashFS (unsquashfs), JFFS2, UBI. Emulate with QEMU: qemu-arm -L /usr/arm-linux-gnueabihf/ ./binary. See platforms.md.
Kernel Driver Reversing
Linux .ko: find ioctl handler via file_operations struct, trace copy_from_user/copy_to_user. Debug with QEMU+GDB (-s -S). eBPF: bpftool prog dump xlated. Windows .sys: find DriverEntry → IoCreateDevice → IRP handlers. See platforms.md.
Swift / Kotlin Binary Reversing
Swift: swift demangle symbols, protocol witness tables for dispatch, __swift5_* sections. Kotlin/JVM: coroutines compile to state machines in invokeSuspend, jadx with Kotlin mode for best decompilation. Kotlin/Native: LLVM backend, looks like C++ in disassembly. See languages-compiled.md.
INT3 Patch + Coredump Brute-Force Oracle
Patch 0xCC (INT3) after transform output, enable core dumps, brute-force each input character by extracting computed state from coredump via strings. Avoids full reverse of transformation. See patterns.md.
Signal Handler Chain + LD_PRELOAD Oracle
Binary uses signal handler chains for per-character password validation. Hook signal() via LD_PRELOAD -- the call to install the next handler confirms the current character is correct. See patterns.md.
Font Ligature Exploitation
Custom OpenType font maps multi-character ligature sequences to single glyphs; reverse the GSUB table to decode hidden messages. See patterns-ctf-3.md.
Instruction Counter as Cryptographic State
Pattern: Hand-written assembly uses a dedicated register (e.g., r12) as an instruction counter incremented after nearly every instruction. The counter feeds into XOR/ROL/multiply transformations on input bytes, making transformation path-dependent. Byte-by-byte brute force with Unicorn emulation recovers the flag. See patterns-ctf-3.md.
Burrows-Wheeler Transform Inversion
Invert BWT without terminator character by trying all possible row indices. Standard bwtool or manual column-sorting reconstruction. See patterns-ctf-3.md.
FRACTRAN Program Inversion
Esoteric language using iterated fraction multiplication. Invert by swapping numerator/denominator in fraction table, run output backward. I/O encoded as prime factorization exponents. See languages.md.
Opcode-Only Trace Reconstruction
Execution traces with only opcodes (no data) still leak info through branch decisions. Sorting algorithm comparisons reveal element ordering. Reconstruct by deduplicating trace, splitting into basic blocks. See tools-dynamic.md.
Thread Race Signed Integer Overflow
Combat-simulation binary with thread-unsafe skill lock. Race between skill selection and damage calculation; cdqe sign-extends 0xFFFFFFFF to -1 (signed), causing HP overflow on subtraction. See patterns-ctf-3.md.
ESP32/Xtensa Firmware Reversing
No IDA support — use radare2 + ESP-IDF ROM linker script (esp32.rom.ld) for symbol resolution. Cross-reference with public ESP-IDF HTTP server examples to identify app logic. See patterns-ctf-3.md.
Custom VM Bytecode Lifting to LLVM IR
Transpile custom VM bytecode to LLVM IR, then use opt -O3 to simplify (inlining, constant folding, dead code elimination). Reduces 1300 lines to ~150 lines, revealing the underlying algorithm. See tools-advanced.md.
SIGFPE Signal Handler Side-Channel
SIGFPE signal handlers create implicit control flow invisible to static analysis. Count SIGFPE signals via strace -e signal=SIGFPE per candidate character -- correct characters produce more signals. See anti-analysis.md.
Batch Crackme Automation via objdump
Mass crackme challenges (100s of binaries) with identical structure: script objdump to extract CMP immediates and add/sub arithmetic sequences, then reverse-compute keys algebraically without execution. See patterns-ctf-3.md.
Android DEX Runtime Bytecode Patching
Native JNI library patches Dalvik bytecode in memory via /proc/self/maps + mprotect + XOR. Static APK analysis alone is insufficient -- extract XOR key and offsets from the native .so to reconstruct the runtime DEX. See languages-platforms.md.
Fork + Pipe + Dead Branch Anti-Analysis
Fork/pipe IPC where parent writes data and exits, child reads and continues. Real validation hidden in a dead branch (always-false comparison). strace reveals the fork/pipe pattern; patch the comparison constant to reach hidden code. See patterns-ctf-3.md.
Web/CTF Auth Bypass Case Notes
Signed Cookie Key Reuse: access token to admin_session
Case: class.pangbaoba.me CTF homework system. Public /access/<token> route set a signed student_gate; the same access token also worked as the HMAC key for admin_session, allowing direct admin API access by forging the exact session payload shape.
Core pattern: A visible invite/access token is reused as a server-side signing secret. If one signed cookie can be validated offline, test whether sibling auth cookies use the same signing scheme and key.
Triage workflow: 1. Capture Set-Cookie from the gated entry route, especially cookies shaped like <base64url-json>.<base64url-signature>. 2. Decode the first segment; identify compact JSON payloads such as {"access":"student"}. 3. Recompute HMAC-SHA256(payload_b64, candidate_key) using visible route tokens, invite codes, reset tokens, or frontend constants as candidate keys. 4. If the signature matches, enumerate payload shape, not passwords: try likely authorization claims on the correct cookie name (admin_session, session, auth, etc.). 5. Verify with read-only endpoints first (/api/admin/me, settings/status/list routes) before any write action.
Important lesson: The first obvious payload may fail. In this case {"access":"admin"}, {"role":"admin"}, and {"access":"student","isAdmin":true} failed, while the backend actually checked:
{"admin":true}Minimal PoC shape:
import base64, hashlib, hmac, json
def b64u(data: bytes) -> str:
return base64.urlsafe_b64encode(data).decode().rstrip("=")
access_token = "<token from /access/<token>>"
payload_b64 = b64u(json.dumps({"admin": True}, separators=(",", ":")).encode())
sig_b64 = b64u(hmac.new(access_token.encode(), payload_b64.encode(), hashlib.sha256).digest())
print(f"admin_session={payload_b64}.{sig_b64}")Validation signals:
GET /api/admin/mechanges from401 {"error":"unauthorized"}to200 {"admin":true}.- Other read-only admin endpoints return real data with the forged cookie.
- A JSON-cookie value like
admin_session=j:{}causing500suggests Express/cookie-parser type confusion and confirms fragile cookie parsing; it is not required for the bypass but helps identify the stack and parsing assumptions.
What to avoid: Do not brute-force admin passwords or enumerate unrelated user IDs when a signed-cookie structure is visible. Work offline on signatures and use low-frequency read-only verification.
Fix guidance: Never use public route/access tokens as HMAC secrets. Use server-only cookie signing secrets, separate student/admin secrets, server-side sessions for admin identity, strict cookie type checks, and return 401 on parse/verify failure instead of 500.
Web Phishing Infrastructure
Phishing Panel: {target_domain_a} / {target_domain_b}
完整分析: phishing-case-study.md
Two-server phishing infrastructure impersonating a government agency. Full victim control system with server-driven status code redirection.
Architecture:
{target_domain_a}— Presentation layer (phishing pages, JS polling client){target_domain_b}— Data layer (PHP+MySQL backend, admin panel)- Both behind NAT ({internal_ip} internal), nginx, SSL-only
- Web root:
/www/wwwroot/{target_domain_b}/
Victim Flow: Landing page (fake subsidy quotas) → 1.html (ID/bank card form → submit.php) → 4.html (PIN → get-ayment.php) → server-controlled staged pages (9-16) via 1-second status_check.php polling.
Key Findings:
- Admin panel at
register.php→qichuang.php(login form),list.php(dashboard template) - Auth via PHP session (
PHPSESSID);login.phpandcheck_login_ajax.phpremoved (404) - Data leak:
db.phpreturns victim name list without auth (49+ records, no bank details — only id/username/note/description fields) - No-auth write:
save_note.phpaccepts data without authentication backend.phpgives SQL error suggesting admin registration endpoint (broken)- Rate limiting on
submit.php(multi-factor), no SQLi or session bypass found - Status code system: admin sets 1-16, victim browser auto-redirects to
N.html
Infrastructure:
| Domain | Public IP | Role |
|---|---|---|
| {target_domain_1} | {target_ip_1} | Backend + Admin |
| {target_domain_2} | {target_ip_2} | Frontend (phishing pages) |
---
分析前预判:文件伪装与名字欺骗
文件后缀不可信
核心原则:永远用 `file` 命令或 magic bytes 判断文件类型,不要相信后缀名。
常见伪装手法:
| 伪装后缀 | 实际类型 | 目的 |
|---|---|---|
.sh | ELF 二进制 | 让人以为是脚本,降低警惕 |
.txt | PE/ELF | 绕过简单的文件类型过滤 |
.jpg/.png | 可执行文件或压缩包 | 隐藏在图片中 |
.dll | 实际是 .NET assembly | 混淆分析方向 |
.so | 实际是加密 payload | 需要先解密 |
| 无后缀 | 任何类型 | Linux 下常见 |
# 正确做法:用 file 命令
file suspicious_file.sh
# 输出: ELF 64-bit LSB executable, ARM aarch64...
# 用 xxd 看 magic bytes
xxd suspicious_file.sh | head -1
# 7f454c46 = ELF magic文件名不可信
"DriverLoader" 不一定加载驱动,"Updater" 不一定更新。
常见名字欺骗:
| 文件名暗示 | 实际行为 |
|---|---|
DriverLoader | 可能是 ptrace 注入器 / 进程 hook |
SystemService | 可能是后门 / C2 agent |
Updater / Update | 可能是 dropper / 下载器 |
Helper / Assistant | 可能是提权工具 |
lib*.so | 可能是注入 payload |
分析时应该:
- 忽略文件名暗示,按实际代码行为判断
- 关注
mmap、ptrace、/proc/self/mem等系统调用 - 如果看到"加载驱动"但没有
insmod/init_module调用,说明名不副实
静态分析不够时的动态补充
纯静态分析只能看到代码骨架。以下场景必须配合动态分析:
| 场景 | 推荐动态方法 |
|---|---|
| 代码有解密/解压逻辑 | 在解密后下断点,dump 明文 |
| 大量间接调用(函数指针表) | strace/ltrace 跟踪实际调用 |
| 疑似反调试 | 先 strace 看 ptrace 调用 |
| 内嵌 shellcode/payload | QEMU 用户态模拟执行 |
| 网络通信协议未知 | tcpdump/Wireshark 抓包 |
# strace 跟踪系统调用(重点关注)
strace -f -e trace=open,mmap,ptrace,execve,connect ./binary
# ltrace 跟踪库函数调用
ltrace -f ./binary
# QEMU 用户态模拟(不需要真实设备)
qemu-aarch64 -strace ./binary_arm64
# 检查反调试:看是否 ptrace 自追踪
strace ./binary 2>&1 | grep ptrace
# 如果看到 ptrace(PTRACE_TRACEME, ...) 说明有反调试进程注入/保护壳类样本的常见模式
这类样本(如 LinYuDriverLoader)通常:
1. 不是真正加载内核驱动(需要 root 权限,大多数场景没有) 2. 实际行为是进程注入:
ptraceattach 到目标进程- 通过
/proc/<pid>/mem读写目标内存 mmap映射 shellcode 到目标进程空间
3. 内嵌加密 payload:
- 运行时解密一段 shellcode
- 解密后的 payload 才是真正的 hook 代码
4. 反调试保护:
ptrace(PTRACE_TRACEME)自追踪- 时间检测(
clock_gettime前后对比) /proc/self/status检查 TracerPid
分析策略:
1. file 命令确认真实类型
2. strings 看有没有明显的路径/库名/错误信息
3. rabin2 -I 看架构/编译器/保护
4. 静态找 mmap/ptrace/open 调用
5. 如果有解密逻辑 → 动态跑到解密后 dump
6. 如果有反调试 → 先 patch 掉或用 LD_PRELOAD 绕过Go 二进制逆向指南
Go 编译的二进制有独特的挑战:静态链接导致体积巨大、函数数量上万、字符串格式特殊、符号 strip 后恢复困难。
本文档覆盖工具链、恢复技巧和实战工作流。
---
Go 二进制的特征识别
快速判断一个二进制是否是 Go 编译的:
# 字符串特征
strings binary | grep -E "runtime\.|go\.buildid|GOROOT"
# rabin2 侦察
rabin2 -z binary | grep -i "runtime"
# 文件大小异常大(静态链接 runtime)
# 典型 Hello World: C ~20KB, Go ~2MB常见特征:
- 包含
runtime.前缀的大量函数 - 包含
go.buildidsection - 包含
GOROOT、GOPATH路径字符串 - 函数数量 5000-50000+(包含整个 runtime 和标准库)
---
核心工具链
符号恢复
| 工具 | 用途 | 链接 |
|---|---|---|
| GoReSym | Mandiant 出品,解析 Go 符号信息(pclntab/moduledata) | https://github.com/mandiant/GoReSym |
| GoResolver | Volexity 出品,用 CFG 相似度自动去混淆 Garble 二进制 | https://github.com/volexity/GoResolver |
| redress | 分析 stripped Go 二进制,恢复类型/接口/包结构 | https://github.com/goretk/redress |
| GoStringUngarbler | Google 出品,专门恢复 Garble 混淆的字符串 | https://github.com/mandiant/GoStringUngarbler |
IDA 插件
| 工具 | 用途 | 链接 |
|---|---|---|
| go_parser | IDA 插件,解析 moduledata/pclntab/类型信息 | https://github.com/0xjiayu/go_parser |
| IDAGolangHelper | IDA 脚本集,解析 Go 类型信息 | https://github.com/sibears/IDAGolangHelper |
| AlphaGolang | SentinelLabs 的 IDAPython 脚本集 | https://github.com/SentineLabs/AlphaGolang |
| IDA 9.2+ 原生支持 | Hex-Rays 官方 Go 反编译改进 | https://hex-rays.com/blog/stop-guessing-and-start-going |
Ghidra 插件
| 工具 | 用途 | 链接 |
|---|---|---|
| Ghidra + GoReSym 输出 | 用 GoReSym 导出符号后导入 Ghidra | 配合使用 |
| golang_loader_assist | Ghidra Go 加载辅助 | 社区脚本 |
独立分析工具
| 工具 | 用途 | 链接 |
|---|---|---|
| gore | Go 逆向工程库(redress 的底层) | https://github.com/goretk/gore |
| garble | Go 混淆工具(了解它才能对抗它) | https://github.com/burrowers/garble |
---
Go 二进制的关键结构
pclntab (PC Line Table)
Go 二进制中最重要的结构,包含:
- 所有函数名和地址映射
- 源文件路径
- 行号信息
- 栈帧大小
即使 strip 了符号,pclntab 通常仍然存在(Go runtime 依赖它)。
定位方法:
1. 搜索 magic bytes: 0xFFFFFFF0 (Go 1.16+) 或 0xFFFFFFFB (Go 1.18+)
2. 用 GoReSym 自动定位
3. 用 go_parser IDA 插件自动解析moduledata
包含:
- pclntab 指针
- 类型信息表
- itab(接口表)
- 全局变量信息
字符串格式
Go 字符串不是 C 风格的 null-terminated,而是 (pointer, length) 结构:
C 字符串: "hello\0"
Go 字符串: struct { ptr *byte; len int } → ptr 指向 "hello"(无 \0)这导致 IDA/Ghidra 默认的字符串识别会漏掉大量 Go 字符串。
解决方案:
- 用
go_parser自动识别 Go 字符串 - 用 GoReSym 导出字符串列表
- 手动:找到
runtime.stringtable或通过交叉引用定位
---
实战工作流
场景 1:未 strip 的 Go 二进制
1. GoReSym -t -d -p binary > symbols.json
→ 导出所有函数名、类型、源文件路径
2. 加载到 IDA/Ghidra
3. 导入 GoReSym 的符号信息
4. 过滤掉 runtime.* 和标准库函数,聚焦用户代码
5. 从 main.main 开始分析场景 2:strip 后的 Go 二进制
1. GoReSym -t -d -p binary > symbols.json
→ 即使 strip 了,pclntab 通常还在
2. 如果 GoReSym 失败 → 用 redress
redress -src binary # 恢复源文件路径
redress -pkg binary # 恢复包结构
redress -type binary # 恢复类型信息
3. 加载到 IDA + go_parser 插件
4. 运行 go_parser 自动恢复
5. 从恢复的 main.main 开始场景 3:Garble 混淆的 Go 二进制
Garble 会:
- 随机化函数名(main.main → main.a3f2b1c)
- 加密字符串
- 移除文件路径信息
- 混淆包名
对抗方法:
1. GoResolver(CFG 签名匹配)
→ 通过控制流图相似度恢复标准库函数名
2. GoStringUngarbler(字符串解密)
→ 自动识别 Garble 的字符串加密模式并解密
3. 动态分析(Frida/dlv)
→ Hook runtime 函数观察实际行为
4. 对比分析
→ 编译同版本 Go 的 Hello World,用 binary-diff 对比 runtime 部分场景 4:CGo 混合编译
1. 识别 CGo 边界(_cgo_* 函数)
2. Go 部分用 go_parser 恢复
3. C 部分用常规 IDA 分析
4. 关注 _cgo_topofstack、crosscall2 等桥接函数---
常用命令速查
# GoReSym:导出符号
GoReSym -t -d -p binary > symbols.json
GoReSym -t -d -p binary -o ida_script.py # 生成 IDA 脚本
# redress:分析 stripped 二进制
redress -src binary # 源文件路径
redress -pkg binary # 包结构
redress -type binary # 类型信息
redress -interface binary # 接口信息
redress -filepath binary # 完整文件路径
# GoResolver:去混淆 Garble
GoResolver -binary binary -output resolved.json
# GoStringUngarbler:解密 Garble 字符串
GoStringUngarbler -i binary -o deobfuscated_binary
# 快速判断 Go 版本
strings binary | grep "go1\."
GoReSym -p binary | grep "Version"---
IDA 中的 Go 分析流程
1. 加载二进制(选择正确的架构)
2. 等待自动分析完成
3. 运行 go_parser 插件:
- File → Script File → go_parser.py
- 或 Edit → Plugins → Go Parser
4. 插件会自动:
- 解析 pclntab
- 恢复函数名
- 标记 Go 字符串
- 解析类型信息
5. 过滤视图:
- 隐藏 runtime.* 函数
- 聚焦 main.* 和第三方包
6. 从 main.main 开始逆向---
常见陷阱
| 陷阱 | 说明 | 解决 |
|---|---|---|
| 函数太多看不过来 | Go 静态链接导致 5000-50000 函数 | 用包名过滤,只看 main.* 和业务包 |
| 字符串识别不全 | Go 字符串不是 null-terminated | 用 go_parser 或 GoReSym 恢复 |
| 反编译结果难读 | Go 的 defer/goroutine/interface 让伪代码复杂 | IDA 9.2+ 有改进,或用动态分析辅助 |
| Garble 混淆 | 函数名/字符串全部随机化 | GoResolver + GoStringUngarbler |
| 版本差异 | 不同 Go 版本的 pclntab 格式不同 | GoReSym 支持 Go 1.2-1.23+ |
| CGo 边界 | Go 和 C 代码混合 | 识别 _cgo_* 函数作为分界线 |
---
与其他 skill 的配合
| 需求 | 用什么 |
|---|---|
| IDA 深度分析 Go 二进制 | ida-reverse/ + go_parser 插件 |
| Ghidra 分析(免费) | Ghidra + GoReSym 符号导入 |
| 快速侦察 | radare2/ — rabin2 -z 看字符串 |
| 动态 Hook | Frida(Hook runtime 函数)或 dlv(Go 原生调试器) |
| 跨版本对比 | binary-diff/ — 旧版有符号迁移到新版 |
| Garble 去混淆 | GoResolver + GoStringUngarbler |
内核驱动逆向参考
覆盖 Windows/Linux 内核驱动逆向、Rootkit 分析、C/C++ 二进制模式识别。
---
Windows 驱动逆向
驱动类型
| 类型 | 特征 | 分析重点 |
|---|---|---|
| WDM (Windows Driver Model) | 老式驱动,手动管理 IRP | DriverEntry → 设备创建 → Dispatch 例程 |
| KMDF (Kernel Mode Driver Framework) | 现代框架,事件驱动 | EvtDriverDeviceAdd → Queue → I/O 回调 |
| WDF (Windows Driver Foundation) | KMDF + UMDF 统称 | 看 WdfDriverCreate 调用 |
| Minifilter | 文件系统过滤驱动 | FltRegisterFilter → Pre/Post 回调 |
WDM 驱动分析流程
1. 找 DriverEntry(入口点)
- IDA 自动识别,或搜索 IoCreateDevice / IoCreateSymbolicLink
2. 找设备名和符号链接
- IoCreateDevice → DeviceName(如 \Device\MyDriver)
- IoCreateSymbolicLink → SymLink(如 \DosDevices\MyDriver)
3. 找 Dispatch 例程
- DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchIoctl
- 这是用户态通过 DeviceIoControl 调用的入口
4. 分析 IOCTL 处理
- switch(IoControlCode) 分发不同功能
- IOCTL 编码:CTL_CODE(DeviceType, Function, Method, Access)
- Method: METHOD_BUFFERED / METHOD_IN_DIRECT / METHOD_OUT_DIRECT / METHOD_NEITHER
5. 找漏洞
- 用户可控缓冲区未验证长度 → 溢出
- METHOD_NEITHER 直接使用用户指针 → 任意读写
- 未检查 IOCTL 权限 → 非特权用户可调用IOCTL 编码解析
# 解析 IOCTL code
def decode_ioctl(code):
device_type = (code >> 16) & 0xFFFF
access = (code >> 14) & 0x3
function = (code >> 2) & 0xFFF
method = code & 0x3
methods = {0: "BUFFERED", 1: "IN_DIRECT", 2: "OUT_DIRECT", 3: "NEITHER"}
access_types = {0: "ANY", 1: "READ", 2: "WRITE", 3: "READ|WRITE"}
return f"DevType=0x{device_type:X} Func=0x{function:X} Method={methods[method]} Access={access_types[access]}"
# 示例
decode_ioctl(0x80002034)
# DevType=0x8000 Func=0x80D Method=BUFFERED Access=ANYIDA 插件
| 插件 | 用途 | 链接 |
|---|---|---|
| Driver Buddy Reloaded | 自动识别 IOCTL、Dispatch、设备名 | https://github.com/VoidSec/DriverBuddyReloaded |
| WinDbg + IDA | 内核调试 + 静态分析配合 | 内置 |
| FLIRT/Lumina | 识别 WDK 库函数 | IDA 内置 |
参考文章
- Windows Drivers RE Methodology (VoidSec) — 最完整的 WDM 驱动逆向方法论
- Driver Reversing 101 — WDM vs KMDF 对比
- Methodology of Reversing Vulnerable Killer Drivers — 漏洞驱动分析
---
Linux 内核模块逆向
LKM (Loadable Kernel Module) 结构
关键函数:
- init_module / module_init → 模块加载时执行
- cleanup_module / module_exit → 模块卸载时执行
关键结构:
- struct file_operations → 字符设备的 open/read/write/ioctl
- struct net_device_ops → 网络设备操作
- struct block_device_operations → 块设备操作分析流程
1. 确认是内核模块
file module.ko → "ELF 64-bit ... relocatable"(注意是 relocatable 不是 executable)
2. 找 init/exit 函数
readelf -s module.ko | grep -E "init_module|cleanup_module"
或在 .modinfo section 找模块信息
3. 找 file_operations 结构
搜索 register_chrdev / cdev_add / misc_register
→ 找到 fops 结构体 → 定位 ioctl/read/write 处理函数
4. 分析 ioctl 处理
unlocked_ioctl / compat_ioctl 函数
→ switch(cmd) 分发
5. 找 Rootkit 行为
- 修改 sys_call_table → syscall hook
- 修改 /proc 文件系统 → 隐藏进程/文件
- 注册 netfilter hook → 隐藏网络连接
- 修改 VFS 层 → 隐藏文件Rootkit 常见技术
| 技术 | 特征 | 检测方法 |
|---|---|---|
| syscall table hook | 修改 sys_call_table 条目 | 对比内存中的表与磁盘上的 vmlinux |
| VFS hook | 修改 file_operations 函数指针 | 检查 fops 指针是否指向内核代码段外 |
| Netfilter hook | nf_register_net_hook | 遍历 netfilter hook 链表 |
| kprobe/ftrace hook | 注册 kprobe 或 ftrace 回调 | 检查 ftrace 注册列表 |
| eBPF rootkit | 加载恶意 BPF 程序 | bpftool prog list |
| DKOM | 直接修改内核对象(进程链表) | 遍历 task_struct 链表对比 /proc |
工具
| 工具 | 用途 |
|---|---|
crash | 内核 dump 分析 |
volatility3 | 内存取证(Linux profile) |
dmesg / journalctl | 内核日志 |
lsmod / /proc/modules | 已加载模块列表 |
modinfo | 模块元信息 |
strace | 系统调用跟踪(用户态视角) |
---
C/C++ 逆向模式识别
C 语言常见模式
| 源码模式 | 反汇编特征 |
|---|---|
if-else | cmp + jcc(条件跳转) |
switch-case | 跳转表(jmp [rax*8 + table])或连续 cmp |
for 循环 | cmp + jl/jle + 循环体 + inc/add + jmp 回跳 |
while 循环 | 条件判断在循环顶部 |
do-while | 条件判断在循环底部 |
| 函数指针调用 | call rax 或 call [reg+offset] |
struct 访问 | [reg+固定偏移](如 [rdi+0x10]) |
malloc + 使用 | call malloc → 返回值存入寄存器 → 后续用该寄存器+偏移访问 |
| 字符串比较 | call strcmp 或 repe cmpsb |
C++ 特有模式
| 源码模式 | 反汇编特征 |
|---|---|
| 虚函数调用 | mov rax, [rcx](取 vtable)→ call [rax+offset](调用虚函数) |
| 构造函数 | 分配内存 → 写入 vtable 指针 → 初始化成员 |
| 析构函数 | 清理成员 → 可能调用 operator delete |
| this 指针 | 第一个参数(rcx/rdi)是对象指针 |
| 继承 | vtable 中包含父类虚函数 + 子类覆盖 |
| 多重继承 | 对象内有多个 vtable 指针(偏移不同) |
| RTTI | vtable 前面有 type_info 指针 |
| 异常处理 | __cxa_throw / _CxxThrowException |
| STL 容器 | std::vector: {begin, end, capacity} 三指针结构 |
| std::string | 小字符串优化(SSO):短串内联,长串堆分配 |
vtable 逆向方法
1. 找 vtable
- 搜索连续的函数指针数组(在 .rodata 或 .rdata 段)
- 构造函数中 `mov [rcx], offset vtable` 写入 vtable 指针
2. 确定类层次
- vtable 前 -8 偏移处通常是 RTTI 指针(如果未 strip)
- 多个 vtable 共享前几个条目 → 继承关系
3. 标注虚函数
- vtable[0] 通常是析构函数(或 deleting destructor)
- 后续按偏移标注:vtable[1] = func1, vtable[2] = func2...
4. IDA 中操作
- 在 vtable 地址创建 struct(每个字段是函数指针)
- 对 `call [rax+offset]` 添加注释标明调用的虚函数结构体恢复
方法 1:从访问模式推断
mov eax, [rdi+0x00] → field_0: int/ptr (4/8 bytes)
mov ecx, [rdi+0x08] → field_8: int/ptr
movss xmm0, [rdi+0x10] → field_10: float
方法 2:从 sizeof 推断
call malloc(0x30) → 结构体大小 0x30 (48 bytes)
方法 3:从构造函数推断
构造函数会初始化所有字段 → 字段类型和偏移一目了然
方法 4:用 IDA 的 "Create struct" 功能
选中访问模式 → Edit → Struct → Create struct from selection---
常见编译器特征
| 编译器 | 识别特征 |
|---|---|
| MSVC | _security_cookie 检查、__fastcall 调用约定、Rich Header |
| GCC | __stack_chk_fail、-fstack-protector、.note.GNU-stack |
| Clang/LLVM | 类似 GCC 但优化模式不同、__asan_*(如果开了 sanitizer) |
| MinGW | GCC 特征 + Windows API 调用 |
| AOSP Clang | Android 特有的 __android_log_print、PGO 标记 |
优化级别识别
| 优化级别 | 特征 |
|---|---|
| -O0 | 大量冗余 mov、每个变量都在栈上、函数不内联 |
| -O1 | 基本优化、部分变量在寄存器 |
| -O2 | 循环展开、函数内联、尾调用优化 |
| -O3 / -Os | 激进内联、向量化(SIMD)、代码难读 |
| PGO | 热路径优化、冷代码分离到 .text.cold |
| LTO | 跨模块内联、全局死代码消除 |
---
内核调试环境
Windows
调试器:WinDbg Preview
连接方式:网络调试(推荐)或串口
被调试机设置:
bcdedit /debug on
bcdedit /dbgsettings net hostip:192.168.x.x port:50000
调试机连接:
WinDbg → File → Attach to Kernel → Net → Port:50000 Key:xxx
常用命令:
!analyze -v # 自动分析崩溃
lm # 列出已加载模块
!drvobj \Driver\xxx # 查看驱动对象
dt nt!_DRIVER_OBJECT # 显示结构体
bp module!function # 下断点Linux
调试器:GDB + QEMU 或 kgdb
QEMU 内核调试:
qemu-system-x86_64 -kernel bzImage -s -S ...
gdb vmlinux -ex "target remote :1234"
常用命令:
info threads # 内核线程
lx-symbols # 加载内核符号(需要 scripts/gdb/)
p init_task # 查看 init 进程
lx-dmesg # 内核日志---
参考资源
| 资源 | 说明 | 链接 |
|---|---|---|
| VoidSec 驱动逆向方法论 | Windows WDM 驱动完整分析流程 | https://voidsec.com/windows-drivers-reverse-engineering-methodology/ |
| Elastic Rootkit 系列 | Linux Rootkit 分类+检测 | https://security-labs.elastic.co/security-labs/linux-rootkits-1-hooked-on-linux |
| Driver Buddy Reloaded | IDA 驱动分析插件 | https://github.com/VoidSec/DriverBuddyReloaded |
| LOLDrivers | 已知漏洞驱动列表 | https://www.loldrivers.io/ |
| Windows Driver Samples | 微软官方驱动示例 | https://github.com/microsoft/Windows-driver-samples |
| Linux Kernel Module Programming | 内核模块开发教程 | https://sysprog21.github.io/lkmpg/ |
| Trail of Bits - Devirtualizing C++ | vtable 逆向方法 | https://blog.trailofbits.com/2017/02/13/devirtualizing-c-with-binary-ninja/ |
CTF Reverse - Hardware and Advanced Architecture Reversing
HD44780 LCD GPIO reconstruction, RISC-V advanced extensions and debugging, ARM64/AArch64 reversing and exploitation.
Table of Contents
- HD44780 LCD Controller GPIO Reconstruction (32C3 2015)
- RISC-V (Advanced)
- Custom Extensions
- Privileged Modes
- RISC-V Debugging
- ARM64/AArch64 Reversing and Exploitation
- MIPS64 Cavium OCTEON Coprocessor 2 Crypto (SEC-T CTF 2017)
- EFM32 ARM Microcontroller MMIO AES (SEC-T CTF 2017)
- MBR/Bootloader Reversing with QEMU + GDB (Square CTF 2017)
---
HD44780 LCD Controller GPIO Reconstruction (32C3 2015)
Recover text displayed on an HD44780 LCD from raw Raspberry Pi GPIO recordings:
1. Identify signal lines: Map GPIO pins to HD44780 signals (RS, CLK, D4-D7 for 4-bit mode) 2. Clock edge detection: Sample data lines on falling clock edges (1->0 transition) 3. Nibble assembly: Combine two 4-bit samples into one 8-bit command/data byte 4. DRAM address mapping: HD44780 uses non-contiguous addressing for multi-line displays:
- Line 0: 0x00-0x27
- Line 1: 0x40-0x67
- Line 2: 0x14-0x3B
- Line 3: 0x54-0x7B
display = [' '] * 80 # 4 lines x 20 chars
cursor = 0
for timestamp, gpio_state in sorted(gpio_log):
if falling_edge(gpio_state, CLK_PIN):
nibble = extract_data_bits(gpio_state)
byte = assemble_nibble(nibble) # Two nibbles per byte
if rs_high(gpio_state): # RS=1: data write
display[dram_to_position(cursor)] = chr(byte)
cursor += 1
else: # RS=0: command (set cursor, clear, etc.)
cursor = parse_command(byte)Key insight: GPIO pin-to-signal mapping is rarely documented; identify CLK by finding the pin with most transitions, RS by correlation with data patterns (alternating command/data phases).
---
RISC-V (Advanced)
Beyond basic disassembly (see tools.md):
Custom Extensions
Bitmanip extensions (Zbb, Zbc, Zbs):
clz, ctz, cpop -> count leading/trailing zeros, popcount
orc.b, rev8 -> byte-level bit manipulation
andn, orn, xnor -> negated logic operations
clmul, clmulh, clmulr -> carry-less multiplication (crypto)
bset, bclr, binv, bext -> single-bit operations
Crypto extensions (Zk*):
aes32esi, aes32dsmi -> AES round operations
sha256sig0, sha512sum0 -> SHA hash acceleration
sm3p0, sm4ed -> Chinese crypto standardsPrivileged Modes
Machine mode (M): Highest privilege, firmware/bootloader
Supervisor mode (S): OS kernel
User mode (U): Applications
CSR registers to watch:
mstatus/sstatus -> privilege level, interrupt enable
mtvec/stvec -> trap handler address
mepc/sepc -> exception return address
mcause/scause -> trap cause
satp -> page table root (virtual memory)RISC-V Debugging
# OpenOCD + GDB for hardware debugging
openocd -f interface/jlink.cfg -f target/riscv.cfg
# GDB for RISC-V
riscv64-unknown-elf-gdb binary
(gdb) target remote :3333
# QEMU with GDB server
qemu-riscv64 -g 1234 -L /usr/riscv64-linux-gnu/ ./binary
riscv64-linux-gnu-gdb -ex 'target remote :1234' ./binary---
ARM64/AArch64 Reversing and Exploitation
AArch64 (ARM 64-bit) appears in mobile apps, cloud servers (AWS Graviton), Apple Silicon, and CTF challenges. Key differences from x86-64 affect both reversing and exploitation.
Setup and emulation:
# Install cross-toolchain and emulator
apt install gcc-aarch64-linux-gnu gdb-multiarch qemu-user-static
# Run AArch64 binary on x86 host
qemu-aarch64-static -L /usr/aarch64-linux-gnu/ ./arm64_binary
# Debug with GDB
qemu-aarch64-static -g 12345 -L /usr/aarch64-linux-gnu/ ./arm64_binary &
gdb-multiarch -ex 'set arch aarch64' -ex 'target remote :1234' ./arm64_binary
# With library preloading (for challenges that ship libc)
qemu-aarch64-static -g 12345 -E LD_PRELOAD=./libc.so.6 -L ./lib ./arm64_binaryAArch64 calling convention (key differences from x86-64):
Registers:
x0-x7 -- function arguments AND return values (x0 = first arg / return)
x8 -- indirect result location (struct returns)
x9-x15 -- caller-saved temporaries
x19-x28 -- callee-saved (preserved across calls)
x29 (fp) -- frame pointer
x30 (lr) -- link register (return address, NOT on stack by default)
sp -- stack pointer (must be 16-byte aligned)
xzr -- zero register (reads as 0, writes discarded)
Key exploitation differences:
- Return address in LR (x30), not on stack -- pushed only if function calls others
- No RIP-relative addressing like x86 -- uses ADRP+ADD pairs for PC-relative loads
- Fixed 4-byte instruction width -- no variable-length gadget tricks
- NOP = 0xD503201F (not 0x90)
- BLR x8 / BR x30 -- indirect calls/jumps use register operandsCommon AArch64 patterns in Ghidra/IDA:
# PC-relative address loading (equivalent to x86 LEA):
ADRP x0, #0x411000 ; Load page address (4KB aligned)
ADD x0, x0, #0x8 ; Add page offset -> x0 = 0x411008
# Function prologue:
STP x29, x30, [sp, #-0x30]! ; Push fp + lr, decrement sp
MOV x29, sp ; Set frame pointer
# Function epilogue:
LDP x29, x30, [sp], #0x30 ; Pop fp + lr, increment sp
RET ; Branch to x30 (lr)
# Switch/jump table:
ADR x1, jump_table
LDRB w2, [x1, x0] ; Load offset byte
ADD x1, x1, w2, SXTB ; Sign-extend and add
BR x1 ; Indirect branchROP on AArch64:
from pwn import *
# AArch64 gadgets differ from x86:
# - "pop {x0}; ret" equivalent: LDP x0, x1, [sp], #0x10; RET
# - Prologue gadgets: LDP x29, x30, [sp, #0x20]; ... RET
# - system() call: x0 = pointer to "/bin/sh", BLR to system
context.arch = 'aarch64'
elf = ELF('./arm64_binary')
# Common gadget pattern in AArch64 libc:
# LDP X19, X20, [SP,#var_s10]
# LDP X29, X30, [SP+var_s0],#0x20
# RET
# Controls x19, x20, x29, x30 and advances sp by 0x20Key insight: AArch64's fixed instruction width and register-based return address (lr/x30) make ROP gadgets more constrained than x86. Look for LDP (load pair) gadgets that pop multiple registers from the stack. The STP/LDP instruction pairs that save/restore callee-saved registers in function prologues/epilogues are the primary gadget source.
When to recognize: file shows "ELF 64-bit LSB ... ARM aarch64". Ghidra auto-detects but may need manual processor selection for raw binaries. Use qemu-aarch64-static for emulation on x86 hosts.
Tools: radare2 (r2 -AA -a arm -b 64), Ghidra (auto-detect), aarch64-linux-gnu-objdump -d, Unicorn Engine (UC_ARCH_ARM64)
References: Google CTF 2016 "Forced Puns", Insomni'hack 2018 "onecall"
---
MIPS64 Cavium OCTEON Coprocessor 2 Crypto (SEC-T CTF 2017)
Cavium OCTEON network processors implement hardware AES and SHA256 via MIPS Coprocessor 2 (CP2) using dmtc2 (move to CP2) and dmfc2 (move from CP2) instructions. These look like ordinary register moves to a disassembler but drive the hardware crypto engine.
Key CP2 register layout (OCTEON):
AES key registers:
0x0104 – AES key quadword 0
0x0105 – AES key quadword 1
0x0106 – AES key quadword 2
0x0107 – AES key quadword 3
SHA256 hash registers:
0x400E–0x4012 – SHA256 intermediate hash words
0x404F – SHA256 control/result
dmtc2 rN, 0x0104 ; load 64 bits of AES key into CP2 register 0x104
dmtc2 rN, 0x0105 ; ...next quadwordApproach: 1. Disassemble in IDA/Ghidra — dmtc2/dmfc2 with selector in 0x100-0x40FF range indicates OCTEON CP2 2. Cross-reference the Cavium OCTEON Hardware Reference Manual for register semantics 3. Trace the key loading sequence to recover the AES or HMAC key material
Key insight: Hardware crypto accelerators on MIPS appear as CP2 register writes (dmtc2/dmfc2). Identify the base register address and cross-reference vendor documentation.
References: SEC-T CTF 2017
---
EFM32 ARM Microcontroller MMIO AES (SEC-T CTF 2017)
Silicon Labs EFM32 Cortex-M binary — a flat binary loaded at 0x1000 in Thumb mode.
IDA setup:
Processor: ARM Little-endian (ARMv7-M)
Load address: 0x1000
Set T register = 1 (force Thumb mode decoding)AES accelerator MMIO layout (EFM32 AES peripheral at 0x400E0000):
0x400E0000 + 0x000 CTRL – enable, decrypt mode
0x400E0000 + 0x004 CMD – start/stop
0x400E0000 + 0x010 KEYLA – key low word 0
0x400E0000 + 0x014 KEYLB – key low word 1
0x400E0000 + 0x018 KEYLC – key low word 2
0x400E0000 + 0x01C KEYLD – key low word 3The binary loads two separate values, XORs them together, then writes the result as the AES key. Decrypt the embedded ciphertext block with the composed key in ECB mode.
from Crypto.Cipher import AES
key_part_a = bytes.fromhex("...") # extracted from IDA .data section
key_part_b = bytes.fromhex("...") # second value
key = bytes(a ^ b for a, b in zip(key_part_a, key_part_b))
cipher = AES.new(key, AES.MODE_ECB)
plaintext = cipher.decrypt(ciphertext)Key insight: Hardware AES accelerators on microcontrollers appear as MMIO register writes at a specific base address — cross-reference the vendor reference manual (EFM32 Reference Manual for Silicon Labs peripherals).
References: SEC-T CTF 2017
---
MBR/Bootloader Reversing with QEMU + GDB (Square CTF 2017)
Boot a floppy/disk image in QEMU with the GDB stub enabled, then attach GDB for full source-level debugging of 16-bit real mode or 32-bit protected mode bootloader code.
# Boot with GDB stub on port 1234; -S pauses execution at start
qemu-system-x86_64 -fda disk.img -s -S
# In another terminal, attach GDB
gdb -ex "set architecture i8086" \
-ex "target remote :1234" \
-ex "break *0x7c00" \
-ex "continue"
# Common MBR entry point is 0x7c00 (BIOS loads MBR here)
# Step through bootloader, inspect registers and memory:
(gdb) x/20i $pc
(gdb) info registers
(gdb) x/16xb 0x7c00To bypass a password check: identify the conditional jump after the comparison and NOP it out in the image file, or patch the comparison to always succeed.
# Find the comparison offset in the image and patch it
python3 -c "
data = open('disk.img', 'rb').read()
# Replace JNZ (0x75) with JMP-short-always or NOP
data = data[:offset] + b'\x90\x90' + data[offset+2:]
open('disk_patched.img', 'wb').write(data)
"Key insight: QEMU's -s flag exposes a GDB stub on port 1234 for full debugging of MBR/bootloader code — workflow identical to userland debugging.
References: Square CTF 2017
---