一键导入
ctf-reverse
Reverse engineering techniques for CTF challenges. Use when analyzing binaries, game clients, obfuscated code, or esoteric languages.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Reverse engineering techniques for CTF challenges. Use when analyzing binaries, game clients, obfuscated code, or esoteric languages.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
Cryptography techniques for CTF challenges. Use when attacking encryption, hashing, ZKP, signatures, or mathematical crypto problems.
Digital forensics and blockchain analysis for CTF challenges. Use when analyzing disk images, memory dumps, event logs, network captures, or cryptocurrency transactions.
Malware and network analysis techniques for CTF challenges. Use when analyzing obfuscated scripts, malicious packages, custom protocols, or C2 traffic.
Miscellaneous CTF challenge techniques. Use for trivia, automation scripts, encoding puzzles, RF/SDR signal processing, or challenges that don't fit other categories.
Open Source Intelligence techniques for CTF challenges. Use when gathering information from public sources, social media, geolocation, or identifying unknown data.
Binary exploitation (pwn) techniques for CTF challenges. Use when exploiting buffer overflows, format strings, heap vulnerabilities, race conditions, or kernel bugs.
| name | ctf-reverse |
| description | Reverse engineering techniques for CTF challenges. Use when analyzing binaries, game clients, obfuscated code, or esoteric languages. |
| user-invocable | false |
| allowed-tools | ["Bash","Read","Write","Edit","Glob","Grep","Task","WebFetch","WebSearch"] |
Quick reference for RE challenges. For detailed techniques, see supporting files.
# 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" | ./binary
file binary # Type, architecture
checksec --file=binary # Security features (for pwn)
chmod +x binary # Make executable
Key insight: Let the program compute the answer, then dump it.
gdb ./binary
start
b *main+0x198 # Break at final comparison
run
# Enter any input of correct length
x/s $rsi # Dump computed flag
x/38c $rsi # As characters
Pattern: Multiple fake targets before real check.
Identification:
Solution: Set breakpoint at FINAL comparison, not earlier ones.
PIE binaries randomize base address. Use relative breakpoints:
gdb ./binary
start # Forces PIE base resolution
b *main+0xca # Relative to main
run
Two patterns:
transform(flag) == stored_target - Reverse the transformtransform(stored_target) == flag - Flag IS the transformed data!Pattern 2 solution: Don't reverse - just apply transform to stored target.
flag{, CTF{)^ i or ^ (i & 0xff)) layered with a repeating key# 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 IDA64
import marshal, dis
with open('file.pyc', 'rb') as f:
f.read(16) # Skip header
code = marshal.load(f)
dis.dis(code)
wasm2c checker.wasm -o checker.c
gcc -O3 checker.c wasm-rt-impl.c -o checker
apktool d app.apk -o decoded/ # Best - decodes resources
jadx app.apk # Decompile to Java
grep -r "flag" decoded/res/values/strings.xml
upx -d packed -o unpacked
Common checks:
IsDebuggerPresent() (Windows)ptrace(PTRACE_TRACEME) (Linux)/proc/self/status TracerPidBypass: Set breakpoint at check, modify register to bypass conditional.
Xorshift32: Shifts 13, 17, 5
Xorshift64: Shifts 12, 25, 27
Magic constants: 0x2545f4914f6cdd1d, 0x9e3779b97f4a7c15
executeIns for opcode meaningsVM challenge workflow (C'est La V(M)ie):
# 1. Find entry point: entry() → __libc_start_main(FUN_xxx, ...)
# 2. Identify loader function (reads .bin file into global buffer)
# 3. Find executor with giant switch statement (opcode dispatch)
# 4. Map each case to instruction: MOVI, ADD, XOR, CMP, JZ, READ, PRINT, HLT...
# 5. Write disassembler, annotate output
# 6. Identify flag transform (often reversible byte-by-byte)
Common VM opcodes to look for:
| Pattern in decompiler | Likely instruction |
|---|---|
global[param1] = param2 | MOVI (move immediate) |
global[p1] = global[p2] | MOVR (move register) |
global[p1] ^= global[p2] | XOR |
global[p1] op global[p2]; set flag | CMP |
if (flag) IP = param | JZ/JNZ |
read(stdin, &global[p1], 1) | READ |
write(stdout, &global[p1], 1) |
Pattern (Slithering Bytes): Given dis.dis() output of a flag checker.
Key instructions:
LOAD_GLOBAL / LOAD_FAST — push name/variable onto stackCALL N — pop function + N args, call, push resultBINARY_SUBSCR — pop index and sequence, push seq[idx]COMPARE_OP — pop two values, compare (55=!=, 40===)POP_JUMP_IF_TRUE/FALSE — conditional branchReversing XOR flag checkers:
# Pattern: ord(flag[i]) ^ KEY == EXPECTED[i]
# Reverse: chr(EXPECTED[i] ^ KEY) for each position
# Interleaved tables (odd/even indices):
odd_table = [...] # Values for indices 1, 3, 5, ...
even_table = [...] # Values for indices 0, 2, 4, ...
flag = [''] * 30
for i, val in enumerate(even_table):
flag[i*2] = chr(val ^ key_even)
for i, val in enumerate(odd_table):
flag[i*2+1] = chr(val ^ key_odd)
Pattern (Signal Signal Little Star): Binary uses UNIX signals as a binary tree navigation mechanism.
Identification:
sigaction() calls with SA_SIGINFOsigaltstack() setup (alternate signal stack)Solving approach:
sigaction via LD_PRELOAD to log signal installations// LD_PRELOAD interposer to log sigaction calls
int sigaction(int signum, const struct sigaction *act, ...) {
if (act && (act->sa_flags & SA_SIGINFO))
log("SET %d SA_SIGINFO=1\n", signum);
return real_sigaction(signum, act, oldact);
}
Pattern (Carrot): Malware with multiple environment checks before executing payload.
Common checks to patch:
| Check | Technique | Patch |
|---|---|---|
ptrace(PTRACE_TRACEME) | Anti-debug | Change cmp -1 to cmp 0 |
sleep(150) | Anti-sandbox timing | Change sleep value to 1 |
/proc/cpuinfo "hypervisor" | Anti-VM | Flip JNZ to JZ |
| "VMware"/"VirtualBox" strings | Anti-VM | Flip JNZ to JZ |
getpwuid username check | Environment | Flip comparison |
LD_PRELOAD check | Anti-hook | Skip check |
| Fan count / hardware check | Anti-VM | Flip JLE to JGE |
| Hostname check | Environment | Flip JNZ to JZ |
Ghidra patching workflow:
Ctrl+Shift+G → modify opcodeJNZ (0x75) → JZ (0x74), or vice versaO → choose "Original File" formatchmod +x the patched binaryServer-side validation bypass:
Locating:
objdump -s -j .rodata binary | less
# Look near comparison instructions
# Size matches flag length
Sign extension: 0xffffffc7 behaves differently in XOR vs addition
# For XOR: use low byte
esi_xor = esi & 0xff
# For addition: use full value with overflow
result = (r13 + esi) & 0xffffffff
for pos in range(flag_length):
for c in range(256):
computed = compute_output(c, current_state)
if computed == EXPECTED[pos]:
flag.append(c)
update_state(c, computed)
break
Uniform transform shortcut: if changing one input byte only changes one output byte, build a 0..255 mapping by repeating a single byte across the whole input, then invert.
from unicorn import *
from unicorn.x86_const import *
mu = Uc(UC_ARCH_X86, UC_MODE_64)
# Map segments, set up stack
# Hook to trace register changes
mu.emu_start(start_addr, end_addr)
Mixed-mode pitfall: if a 64-bit stub jumps into 32-bit code via retf/retfq, you must
switch to a UC_MODE_32 emulator and copy GPRs, EFLAGS, and XMM regs; missing XMM state
will corrupt SSE-based transforms.
Pattern (I Heard You Liked Loaders): Nested shellcode with XOR decode loops and anti-debug.
Debugging workflow:
call rax in launcher, step into shellcodeset $rax=0int3 if hidden)Flag extraction from mov instructions:
# Final stage loads flag 4 bytes at a time via mov ebx, value
# Extract little-endian 4-byte chunks
values = [0x6174654d, 0x7b465443, ...] # From disassembly
flag = b''.join(v.to_bytes(4, 'little') for v in values)
Pattern (Clock Out): Validation time varies per correct character (longer sleep on match).
Exploitation:
import time
from pwn import *
flag = ""
for pos in range(flag_length):
best_char, best_time = '', 0
for c in string.printable:
io = remote(host, port)
start = time.time()
io.sendline((flag + c).ljust(total_len, 'X'))
io.recvall()
elapsed = time.time() - start
if elapsed > best_time:
best_time = elapsed
best_char = c
io.close()
flag += best_char
Pattern (Steal the Xmas): Encrypted Godot .pck packages.
Tools:
Workflow:
Pattern (Bad Opsec): Debug info and file paths leak author identity.
Quick checks:
strings binary | grep "/home/" # Home directory paths
strings binary | grep "/Users/" # macOS paths
file binary # Check if stripped
readelf -S binary | grep debug # Debug sections present?
Pattern (Flag Appraisal): Binary mangles input 2 bytes at a time with intermediate state, compares to static target.
Approach:
.rodata sectionPattern (Spider's Curse): Input converted to hex, compared against hex constant.
Quick solve: Extract hex constant from strings/Ghidra, decode:
echo "4d65746143..." | xxd -r -p