# CTF Reverse - Tools Reference ## Table of Contents - [GDB](#gdb) - [Basic Commands](#basic-commands) - [PIE Binary Debugging](#pie-binary-debugging) - [One-liner Automation](#one-liner-automation) - [Memory Examination](#memory-examination) - [Radare2](#radare2) - [Basic Session](#basic-session) - [r2pipe Automation](#r2pipe-automation) - [Ghidra](#ghidra) - [Headless Analysis](#headless-analysis) - [Emulator for Decryption](#emulator-for-decryption) - [MCP Commands](#mcp-commands) - [Unicorn Emulation](#unicorn-emulation) - [Basic Setup](#basic-setup) - [Mixed-Mode (64→32) Switch](#mixed-mode-6432-switch) - [Register Tracing Hook](#register-tracing-hook) - [Track Register Changes](#track-register-changes) - [Python Bytecode](#python-bytecode) - [Disassembly](#disassembly) - [Extract Constants](#extract-constants) - [WASM Analysis](#wasm-analysis) - [Decompile to C](#decompile-to-c) - [Common Patterns](#common-patterns) - [Android APK](#android-apk) - [Extraction](#extraction) - [Key Locations](#key-locations) - [Search](#search) - [.NET Analysis](#net-analysis) - [Tools](#tools) - [NativeAOT](#nativeaot) - [Packed Binaries](#packed-binaries) - [UPX](#upx) - [Custom Packers](#custom-packers) - [PyInstaller](#pyinstaller) - [LLVM IR](#llvm-ir) - [Convert to Assembly](#convert-to-assembly) - [RISC-V Binary Analysis (EHAX 2026)](#risc-v-binary-analysis-ehax-2026) - [Binary Ninja](#binary-ninja) - [Decompiler Comparison with dogbolt.org](#decompiler-comparison-with-dogboltorg) - [Useful Commands](#useful-commands) --- ## GDB ### Basic Commands ```bash gdb ./binary run # Run program start # Run to main b *0x401234 # Breakpoint at address b *main+0x100 # Relative breakpoint c # Continue si # Step instruction ni # Next instruction (skip calls) x/s $rsi # Examine string x/20x $rsp # Examine stack info registers # Show registers set $eax=0 # Modify register ``` ### PIE Binary Debugging ```bash gdb ./binary start # Forces PIE base resolution b *main+0xca # Relative to main b *main+0x198 run ``` ### One-liner Automation ```bash gdb -ex 'start' -ex 'b *main+0x198' -ex 'run' ./binary ``` ### Memory Examination ```bash x/s $rsi # String at RSI x/38c $rsi # 38 characters x/20x $rsp # 20 hex words from stack x/10i $rip # 10 instructions from RIP ``` --- ## Radare2 ### Basic Session ```bash r2 -d ./binary # Open in debug mode aaa # Analyze all afl # List functions pdf @ main # Disassemble main db 0x401234 # Set breakpoint dc # Continue ood # Restart debugging dr # Show registers dr eax=0 # Modify register ``` ### r2pipe Automation ```python import r2pipe r2 = r2pipe.open('./binary', flags=['-d']) r2.cmd('aaa') r2.cmd('db 0x401234') for char in range(256): r2.cmd('ood') # Restart r2.cmd(f'dr eax={char}') output = r2.cmd('dc') if 'correct' in output: print(f"Found: {chr(char)}") ``` --- ## Ghidra ### Headless Analysis ```bash analyzeHeadless /path/to/project tmp -import binary -postScript script.py ``` ### Emulator for Decryption ```java EmulatorHelper emu = new EmulatorHelper(currentProgram); emu.writeRegister("RSP", 0x2fff0000); emu.writeRegister("RBP", 0x2fff0000); // Write encrypted data emu.writeMemory(dataAddress, encryptedBytes); // Set function arguments emu.writeRegister("RDI", arg1); // Run until return emu.setBreakpoint(returnAddress); emu.run(functionEntryAddress); // Read result byte[] decrypted = emu.readMemory(outputAddress, length); ``` ### MCP Commands - Recon: `list_functions`, `list_imports`, `list_strings` - Analysis: `decompile_function`, `get_xrefs_to` - Annotation: `rename_function`, `rename_variable` --- ## Unicorn Emulation ### Basic Setup ```python from unicorn import * from unicorn.x86_const import * mu = Uc(UC_ARCH_X86, UC_MODE_64) # Map code segment mu.mem_map(0x400000, 0x10000) mu.mem_write(0x400000, code_bytes) # Map stack mu.mem_map(0x7fff0000, 0x10000) mu.reg_write(UC_X86_REG_RSP, 0x7fff0000 + 0xff00) # Run mu.emu_start(start_addr, end_addr) ``` ### Mixed-Mode (64→32) Switch ```python # When a 64-bit stub jumps into 32-bit code via retf/retfq: # - retf pops 4-byte EIP + 2-byte CS (6 bytes) # - retfq pops 8-byte RIP + 8-byte CS (16 bytes) uc32 = Uc(UC_ARCH_X86, UC_MODE_32) # Copy memory regions, then GPRs reg_map = { UC_X86_REG_EAX: UC_X86_REG_RAX, UC_X86_REG_EBX: UC_X86_REG_RBX, UC_X86_REG_ECX: UC_X86_REG_RCX, UC_X86_REG_EDX: UC_X86_REG_RDX, UC_X86_REG_ESI: UC_X86_REG_RSI, UC_X86_REG_EDI: UC_X86_REG_RDI, UC_X86_REG_EBP: UC_X86_REG_RBP, } for e, r in reg_map.items(): uc32.reg_write(e, mu.reg_read(r) & 0xffffffff) # mu = 64-bit emulator from above uc32.reg_write(UC_X86_REG_EFLAGS, mu.reg_read(UC_X86_REG_RFLAGS) & 0xffffffff) # SSE-heavy blobs need XMM registers copied for xr in [UC_X86_REG_XMM0, UC_X86_REG_XMM1, UC_X86_REG_XMM2, UC_X86_REG_XMM3, UC_X86_REG_XMM4, UC_X86_REG_XMM5, UC_X86_REG_XMM6, UC_X86_REG_XMM7]: uc32.reg_write(xr, mu.reg_read(xr)) # Run 32-bit, then copy regs/memory back to 64-bit ``` **Tip:** set `UC_IGNORE_REG_BREAK=1` to silence warnings on unimplemented regs. ### Register Tracing Hook ```python def hook_code(uc, address, size, user_data): if address == TARGET_ADDR: rsi = uc.reg_read(UC_X86_REG_RSI) print(f"0x{address:x}: rsi=0x{rsi:016x}") mu.hook_add(UC_HOOK_CODE, hook_code) ``` ### Track Register Changes ```python prev_rsi = [None] def hook_rsi_changes(uc, address, size, user_data): rsi = uc.reg_read(UC_X86_REG_RSI) if rsi != prev_rsi[0]: print(f"0x{address:x}: RSI changed to 0x{rsi:016x}") prev_rsi[0] = rsi mu.hook_add(UC_HOOK_CODE, hook_rsi_changes) ``` --- ## Python Bytecode ### Disassembly ```python import marshal, dis with open('file.pyc', 'rb') as f: f.read(16) # Skip header (varies by Python version) code = marshal.load(f) dis.dis(code) ``` ### Extract Constants ```python for ins in dis.get_instructions(code): if ins.opname == 'LOAD_CONST': print(ins.argval) ``` --- ## WASM Analysis ### Decompile to C ```bash wasm2c checker.wasm -o checker.c gcc -O3 checker.c wasm-rt-impl.c -o checker ``` ### Common Patterns - `w2c_memory` - Linear memory array - `wasm_rt_trap(N)` - Runtime errors - Function exports: `flagChecker`, `validate` --- ## Android APK ### Extraction ```bash apktool d app.apk -o decoded/ # Best - decodes XML jadx app.apk # Decompile to Java unzip app.apk -d extracted/ # Simple extraction ``` ### Key Locations - `res/values/strings.xml` - String resources - `AndroidManifest.xml` - App metadata - `classes.dex` - Dalvik bytecode - `assets/`, `res/raw/` - Resources ### Search ```bash grep -r "flag\|CTF" decoded/ strings decoded/classes*.dex | grep -i flag ``` --- ## .NET Analysis ### Tools - **dnSpy** - Debugging + decompilation (best) - **ILSpy** - Decompiler - **dotPeek** - JetBrains decompiler ### NativeAOT - Look for `System.Private.CoreLib` strings - Type metadata present but restructured - Search for length-prefixed UTF-16 patterns --- ## Packed Binaries ### UPX ```bash upx -d packed -o unpacked strings binary | grep UPX # Check for UPX signature ``` ### Custom Packers 1. Set breakpoint after unpacking stub 2. Dump memory 3. Fix PE/ELF headers ### PyInstaller ```bash python pyinstxtractor.py binary.exe # Look in: binary.exe_extracted/ ``` --- ## LLVM IR ### Convert to Assembly ```bash llc task.ll --x86-asm-syntax=intel gcc -c task.s -o file.o ``` --- ## RISC-V Binary Analysis (EHAX 2026) **Pattern (iguessbro):** Statically linked, stripped RISC-V ELF binary. Can't run natively on x86. **Disassembly with Capstone:** ```python from capstone import * with open('binary', 'rb') as f: code = f.read() # RISC-V 64-bit with compressed instruction support md = Cs(CS_ARCH_RISCV, CS_MODE_RISCVC | CS_MODE_RISCV64) md.detail = True # Disassemble from entry point (check ELF header for e_entry) TEXT_OFFSET = 0x10000 # typical for static RISC-V for insn in md.disasm(code[TEXT_OFFSET:], TEXT_OFFSET): print(f"0x{insn.address:x}:\t{insn.mnemonic}\t{insn.op_str}") ``` **Common RISC-V patterns:** - `li a0, N` → load immediate (argument setup) - `mv a0, s0` → register move - `call offset` → function call (auipc + jalr pair) - `beq/bne a0, zero, label` → conditional branch - `sd/ld` → 64-bit store/load - `addiw` → 32-bit add (W-suffix = word operations) **Key differences from x86:** - No flags register — comparisons are inline with branch instructions - Arguments in a0-a7 (not rdi/rsi/rdx) - Return value in a0 - Saved registers s0-s11 (callee-saved) - Compressed instructions (2 bytes) mixed with standard (4 bytes) — use `CS_MODE_RISCVC` **Anti-RE tricks in RISC-V:** - Fake flags as string constants (check for `"n0t_th3_r34l"` patterns) - Timing anti-brute-force (rdtime instruction) - XOR decryption with incremental key: `decrypted[i] = enc[i] ^ (key & 0xFF) ^ 0xA5; key += 7` **Emulation:** `qemu-riscv64 -L /usr/riscv64-linux-gnu/ ./binary` (needs cross-toolchain sysroot) --- ## Binary Ninja Interactive disassembler/decompiler with rapid community growth. **Decompilation outputs:** High-Level Intermediate Language (HLIL), pseudo-C, pseudo-Rust, pseudo-Python. ```bash # Open binary binaryninja binary ``` ```python # Headless analysis (Python API) import binaryninja bv = binaryninja.open_view("binary") for func in bv.functions: print(func.name, hex(func.start)) print(func.hlil) # High-Level IL ``` **Community plugins:** Available via Plugin Manager (Ctrl+Shift+P → "Plugin Manager"). **Free version:** https://binary.ninja/free/ (cloud-based, limited features). **Advantages over Ghidra:** Faster startup, cleaner IL representations, better Python API for scripting. --- ## Decompiler Comparison with dogbolt.org **dogbolt.org** runs multiple decompilers simultaneously on the same binary and shows results side-by-side. **Supported decompilers:** Hex-Rays (IDA), Ghidra, Binary Ninja, angr, RetDec, Snowman, dewolf, Reko, Relyze. **When to use:** - Decompiler output is confusing — compare with alternatives for clarity - One decompiler mishandles a construct — another may get it right - Quick triage without installing every tool locally - Validate decompiler correctness by cross-referencing outputs ```bash # Upload via web interface: https://dogbolt.org/ # Or use the API: curl -F "file=@binary" https://dogbolt.org/api/binaries/ ``` **Key insight:** Different decompilers excel at different constructs. When one produces unreadable output, another often generates clearer pseudocode. Cross-referencing catches decompiler bugs. --- ## Useful Commands ```bash # File info file binary checksec --file=binary rabin2 -I binary # String extraction strings binary | grep -iE "flag|secret" rabin2 -z binary # Sections readelf -S binary objdump -h binary # Symbols nm binary readelf -s binary # Disassembly objdump -d binary objdump -M intel -d binary ```