ctf-reverse
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCTF Reverse Engineering
CTF逆向工程
Quick reference for RE challenges. For detailed techniques, see supporting files.
这是RE(逆向工程)挑战的速查手册。如需详细技术内容,请参考配套文件。
Additional Resources
额外资源
- tools.md - Tool-specific commands (GDB, Ghidra, radare2, IDA)
- patterns.md - Core 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
- languages.md - Language/platform-specific: Python bytecode & opcode remapping, DOS stubs, Unity IL2CPP, Brainfuck/esolangs, UEFI, transpilation to C, code coverage side-channel, OPAL functional reversing, non-bijective substitution
- tools.md - 工具专属命令(GDB、Ghidra、radare2、IDA)
- patterns.md - 核心二进制模式:自定义VM、反调试、纳米炸弹、自修改代码、XOR密码、混合模式加载器、LLVM混淆、S盒/密钥流、SECCOMP/BPF、异常处理程序、内存转储、字节级变换、x86-64注意事项
- languages.md - 语言/平台专属内容:Python字节码与操作码重映射、DOS存根、Unity IL2CPP、Brainfuck/小众语言、UEFI、转译为C、代码覆盖率侧信道、OPAL函数式逆向、非双射替换
Problem-Solving Workflow
问题解决流程
- Start with strings extraction - many easy challenges have plaintext flags
- Try ltrace/strace - dynamic analysis often reveals flags without reversing
- Map control flow before modifying execution
- Automate manual processes via scripting (r2pipe, Python)
- Validate assumptions by comparing decompiler outputs
- 先提取字符串 - 许多简单挑战包含明文flag
- 尝试ltrace/strace - 动态分析通常无需逆向即可直接获取flag
- 先映射控制流再修改执行逻辑
- 通过脚本自动化手动流程(r2pipe、Python)
- 通过对比反编译器输出验证假设
Quick Wins (Try First!)
快速尝试技巧(优先试试!)
bash
undefinedbash
undefinedPlaintext flag extraction
明文flag提取
strings binary | grep -E "flag{|CTF{|pico"
strings binary | grep -iE "flag|secret|password"
rabin2 -z binary | grep -i "flag"
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
动态分析 - 常能直接捕获flag
ltrace ./binary
strace -f -s 500 ./binary
ltrace ./binary
strace -f -s 500 ./binary
Hex dump search
十六进制转储搜索
xxd binary | grep -i flag
xxd binary | grep -i flag
Run with test inputs
使用测试输入运行
./binary AAAA
echo "test" | ./binary
undefined./binary AAAA
echo "test" | ./binary
undefinedInitial Analysis
初始分析
bash
file binary # Type, architecture
checksec --file=binary # Security features (for pwn)
chmod +x binary # Make executablebash
file binary # 查看文件类型、架构
checksec --file=binary # 检查安全特性(适用于Pwn题)
chmod +x binary # 设置可执行权限Memory Dumping Strategy
内存转储策略
Key insight: Let the program compute the answer, then dump it.
bash
gdb ./binary
start
b *main+0x198 # Break at final comparison
run核心思路: 让程序计算出答案,然后转储结果。
bash
gdb ./binary
start
b *main+0x198 # 在最终比较处下断点
runEnter any input of correct length
输入任意长度正确的内容
x/s $rsi # Dump computed flag
x/38c $rsi # As characters
undefinedx/s $rsi # 转储计算出的flag
x/38c $rsi # 以字符形式查看
undefinedDecoy Flag Detection
诱饵Flag检测
Pattern: Multiple fake targets before real check.
Identification:
- Look for multiple comparison targets in sequence
- Check for different success messages
- Trace which comparison is checked LAST
Solution: Set breakpoint at FINAL comparison, not earlier ones.
模式: 真实校验前存在多个伪造目标。
识别方法:
- 查找连续的多个比较目标
- 检查是否有不同的成功提示信息
- 追踪最后被校验的比较逻辑
解决方案: 在最终比较处下断点,而非更早的位置。
GDB PIE Debugging
GDB PIE调试
PIE binaries randomize base address. Use relative breakpoints:
bash
gdb ./binary
start # Forces PIE base resolution
b *main+0xca # Relative to main
runPIE二进制文件会随机化基地址,使用相对断点:
bash
gdb ./binary
start # 强制解析PIE基地址
b *main+0xca # 相对于main函数的偏移
runComparison Direction (Critical!)
比较方向(至关重要!)
Two patterns:
- - Reverse the transform
transform(flag) == stored_target - - Flag IS the transformed data!
transform(stored_target) == flag
Pattern 2 solution: Don't reverse - just apply transform to stored target.
两种模式:
- - 逆向转换逻辑
transform(flag) == stored_target - - Flag就是转换后的数据!
transform(stored_target) == flag
模式2解决方案: 无需逆向,直接对存储的目标应用转换即可。
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 (or
^ i) layered with a repeating key^ (i & 0xff)
- 单字节XOR - 尝试所有256种可能值
- 已知明文XOR(、
flag{)CTF{ - 硬编码密钥的RC4
- 自定义置换+XOR
- 结合位置索引的XOR(或
^ i),并搭配重复密钥^ (i & 0xff)
Quick Tool Reference
工具速查
bash
undefinedbash
undefinedRadare2
Radare2
r2 -d ./binary # Debug mode
aaa # Analyze
afl # List functions
pdf @ main # Disassemble main
r2 -d ./binary # 调试模式
aaa # 执行分析
afl # 列出函数
pdf @ main # 反汇编main函数
Ghidra (headless)
Ghidra(无头模式)
analyzeHeadless project/ tmp -import binary -postScript script.py
analyzeHeadless project/ tmp -import binary -postScript script.py
IDA
IDA
ida64 binary # Open in IDA64
undefinedida64 binary # 用IDA64打开
undefinedBinary Types
二进制文件类型
Python .pyc
Python .pyc
python
import marshal, dis
with open('file.pyc', 'rb') as f:
f.read(16) # Skip header
code = marshal.load(f)
dis.dis(code)python
import marshal, dis
with open('file.pyc', 'rb') as f:
f.read(16) # 跳过头部
code = marshal.load(f)
dis.dis(code)WASM
WASM
bash
wasm2c checker.wasm -o checker.c
gcc -O3 checker.c wasm-rt-impl.c -o checkerbash
wasm2c checker.wasm -o checker.c
gcc -O3 checker.c wasm-rt-impl.c -o checkerWASM patching (game challenges):
WASM补丁(游戏挑战):
wasm2wat main.wasm -o main.wat # Binary → text
wasm2wat main.wasm -o main.wat # 二进制转文本
Edit WAT: flip comparisons, change constants
编辑WAT:翻转比较逻辑、修改常量
wat2wasm main.wat -o patched.wasm # Text → binary
**WASM game patching (Tac Tic Toe, Pragyan 2026):** If proof generation is independent of move quality, patch minimax (flip `i64.lt_s` → `i64.gt_s`, change bestScore sign) to make AI play badly while proofs remain valid. See ctf-misc SKILL.md for full pattern.wat2wasm main.wat -o patched.wasm # 文本转二进制
**WASM游戏补丁示例(Tac Tic Toe, Pragyan 2026):** 如果证明生成与移动质量无关,可修补极小极大算法(将`i64.lt_s`改为`i64.gt_s`,修改bestSign符号),让AI表现糟糕同时保持证明有效。详细模式请参考ctf-misc SKILL.md。Android APK
Android APK
bash
apktool d app.apk -o decoded/ # Best - decodes resources
jadx app.apk # Decompile to Java
grep -r "flag" decoded/res/values/strings.xmlbash
apktool d app.apk -o decoded/ # 最优选择 - 解码资源
jadx app.apk # 反编译为Java
grep -r "flag" decoded/res/values/strings.xml.NET
.NET
- dnSpy - debugging + decompilation
- ILSpy - decompiler
- dnSpy - 调试+反编译
- ILSpy - 反编译器
Packed (UPX)
加壳文件(UPX)
bash
upx -d packed -o unpackedbash
upx -d packed -o unpackedAnti-Debugging Bypass
反调试绕过
Common checks:
- (Windows)
IsDebuggerPresent() - (Linux)
ptrace(PTRACE_TRACEME) - TracerPid
/proc/self/status - Timing checks
Bypass: Set breakpoint at check, modify register to bypass conditional.
常见检查手段:
- (Windows)
IsDebuggerPresent() - (Linux)
ptrace(PTRACE_TRACEME) - 中的TracerPid
/proc/self/status - 计时检查
绕过方法:在检查处下断点,修改寄存器值以绕过条件判断。
S-Box / Keystream Patterns
S盒 / 密钥流模式
Xorshift32: Shifts 13, 17, 5
Xorshift64: Shifts 12, 25, 27
Magic constants: ,
0x2545f4914f6cdd1d0x9e3779b97f4a7c15Xorshift32: 移位13、17、5位
Xorshift64: 移位12、25、27位
魔术常量: 、
0x2545f4914f6cdd1d0x9e3779b97f4a7c15Custom VM Analysis
自定义VM分析
- Identify structure: registers, memory, IP
- Reverse for opcode meanings
executeIns - Write disassembler mapping opcodes to mnemonics
- Often easier to bruteforce than fully reverse
- Look for the bytecode file loaded via command-line arg
VM challenge workflow (C'est La V(M)ie):
python
undefined- 识别结构:寄存器、内存、指令指针(IP)
- 逆向以理解操作码含义
executeIns - 编写反汇编器,将操作码映射为助记符
- 通常暴力破解比完全逆向更简单
- 查找通过命令行参数加载的字节码文件
VM挑战流程(C'est La V(M)ie):
python
undefined1. Find entry point: entry() → __libc_start_main(FUN_xxx, ...)
1. 找到入口点:entry() → __libc_start_main(FUN_xxx, ...)
2. Identify loader function (reads .bin file into global buffer)
2. 识别加载函数(将.bin文件读入全局缓冲区)
3. Find executor with giant switch statement (opcode dispatch)
3. 找到包含巨型switch语句的执行器(操作码分发)
4. Map each case to instruction: MOVI, ADD, XOR, CMP, JZ, READ, PRINT, HLT...
4. 将每个case映射为指令:MOVI、ADD、XOR、CMP、JZ、READ、PRINT、HLT...
5. Write disassembler, annotate output
5. 编写反汇编器,标注输出内容
6. Identify flag transform (often reversible byte-by-byte)
6. 识别flag转换逻辑(通常可逐字节逆向)
**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)` | PRINT |
**需关注的常见VM操作码:**
| 反编译器中的模式 | 可能对应的指令 |
|-----------------------|-------------------|
| `global[param1] = param2` | MOVI(立即数移动) |
| `global[p1] = global[p2]` | MOVR(寄存器移动) |
| `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)` | PRINT |Python Bytecode Reversing
Python字节码逆向
Pattern (Slithering Bytes): Given output of a flag checker.
dis.dis()Key instructions:
- /
LOAD_GLOBAL— push name/variable onto stackLOAD_FAST - — pop function + N args, call, push result
CALL N - — pop index and sequence, push
BINARY_SUBSCRseq[idx] - — pop two values, compare (55=
COMPARE_OP, 40=!=)== - — conditional branch
POP_JUMP_IF_TRUE/FALSE
Reversing XOR flag checkers:
python
undefined模式(Slithering Bytes): 给定flag检查器的输出。
dis.dis()关键指令:
- /
LOAD_GLOBAL— 将名称/变量推入栈LOAD_FAST - — 弹出函数+N个参数,调用后将结果推入栈
CALL N - — 弹出索引和序列,将
BINARY_SUBSCR推入栈seq[idx] - — 弹出两个值进行比较(55=
COMPARE_OP,40=!=)== - — 条件分支
POP_JUMP_IF_TRUE/FALSE
逆向XOR flag检查器:
python
undefinedPattern: ord(flag[i]) ^ KEY == EXPECTED[i]
模式:ord(flag[i]) ^ KEY == EXPECTED[i]
Reverse: chr(EXPECTED[i] ^ KEY) for each position
逆向:对每个位置执行chr(EXPECTED[i] ^ KEY)
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[i2] = chr(val ^ key_even)
for i, val in enumerate(odd_table):
flag[i2+1] = chr(val ^ key_odd)
undefinedodd_table = [...] # 索引1、3、5...对应的值
even_table = [...] # 索引0、2、4...对应的值
flag = [''] * 30
for i, val in enumerate(even_table):
flag[i2] = chr(val ^ key_even)
for i, val in enumerate(odd_table):
flag[i2+1] = chr(val ^ key_odd)
undefinedSignal-Based Binary Exploration
基于信号的二进制探索
Pattern (Signal Signal Little Star): Binary uses UNIX signals as a binary tree navigation mechanism.
Identification:
- Multiple calls with
sigaction()SA_SIGINFO - setup (alternate signal stack)
sigaltstack() - Handler decodes embedded payload, installs next pair of signals
- Two types: Node (installs children) vs Leaf (prints message + exits)
Solving approach:
- Hook via
sigactionto log signal installationsLD_PRELOAD - DFS through the binary tree by sending signals
- At each stage, observe which 2 signals are installed
- Send one, check if program exits (leaf) or installs 2 more (node)
- If wrong leaf, backtrack and try sibling
c
// 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);
}模式(Signal Signal Little Star): 二进制文件使用UNIX信号作为二叉树导航机制。
识别方法:
- 多次调用并设置
sigaction()SA_SIGINFO - 调用设置(备用信号栈)
sigaltstack() - 处理函数解码嵌入的有效载荷,安装下一对信号
- 两种类型:节点(安装子节点) vs 叶子(打印信息并退出)
解决方法:
- 通过挂钩
LD_PRELOAD以记录信号安装情况sigaction - 通过发送信号深度优先遍历二叉树
- 在每个阶段,观察安装的两个信号
- 发送其中一个,检查程序是否退出(叶子节点)或安装另外两个信号(节点)
- 如果是错误的叶子节点,回溯并尝试兄弟节点
c
// 用于记录sigaction调用的LD_PRELOAD拦截器
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);
}Malware Anti-Analysis Bypass via Patching
通过补丁绕过恶意软件反分析
Pattern (Carrot): Malware with multiple environment checks before executing payload.
Common checks to patch:
| Check | Technique | Patch |
|---|---|---|
| Anti-debug | Change |
| Anti-sandbox timing | Change sleep value to 1 |
| Anti-VM | Flip |
| "VMware"/"VirtualBox" strings | Anti-VM | Flip |
| Environment | Flip comparison |
| Anti-hook | Skip check |
| Fan count / hardware check | Anti-VM | Flip |
| Hostname check | Environment | Flip |
Ghidra patching workflow:
- Find check function, identify the conditional jump
- Click on instruction → → modify opcode
Ctrl+Shift+G - For (0x75) →
JNZ(0x74), or vice versaJZ - For immediate values: change operand bytes directly
- Export: press → choose "Original File" format
O - the patched binary
chmod +x
Server-side validation bypass:
- If patched binary sends system info to remote server, patch the data too
- Modify string addresses in data-gathering functions
- Change format strings to embed correct values directly
模式(Carrot): 恶意软件在执行有效载荷前会进行多项环境检查。
常见需补丁的检查项:
| 检查项 | 技术类型 | 补丁方法 |
|---|---|---|
| 反调试 | 将 |
| 反沙箱计时 | 将sleep值改为1 |
| 反虚拟机 | 将 |
| "VMware"/"VirtualBox"字符串 | 反虚拟机 | 将 |
| 环境检查 | 翻转比较逻辑 |
| 反挂钩 | 跳过检查 |
| 风扇数量/硬件检查 | 反虚拟机 | 将 |
| 主机名检查 | 环境检查 | 翻转比较逻辑 |
Ghidra补丁流程:
- 找到检查函数,识别条件跳转指令
- 点击指令 → → 修改操作码
Ctrl+Shift+G - 例如将(0x75)改为
JNZ(0x74),反之亦然JZ - 对于立即数:直接修改操作数字节
- 导出:按→ 选择"Original File"格式
O - 给补丁后的二进制文件设置可执行权限:
chmod +x
服务端验证绕过:
- 如果补丁后的二进制文件会向远程服务器发送系统信息,也需补丁这些数据
- 修改数据收集函数中的字符串地址
- 修改格式字符串以直接嵌入正确值
Expected Values Tables
期望值表
Locating:
bash
objdump -s -j .rodata binary | less定位方法:
bash
objdump -s -j .rodata binary | lessLook near comparison instructions
在比较指令附近查找
Size matches flag length
大小与flag长度匹配
undefinedundefinedx86-64 Gotchas
x86-64注意事项
Sign extension: behaves differently in XOR vs addition
0xffffffc7python
undefined符号扩展: 在XOR和加法中的表现不同
0xffffffc7python
undefinedFor XOR: use low byte
对于XOR:使用低字节
esi_xor = esi & 0xff
esi_xor = esi & 0xff
For addition: use full value with overflow
对于加法:使用带溢出的完整值
result = (r13 + esi) & 0xffffffff
undefinedresult = (r13 + esi) & 0xffffffff
undefinedIterative Solver Pattern
迭代求解器模式
python
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)
breakUniform 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.
python
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统一转换捷径: 如果修改一个输入字节仅改变一个输出字节,可通过重复输入单个字节构建0..255的映射表,然后逆向求解。
Unicorn Emulation (Complex State)
Unicorn仿真(复杂状态)
python
from unicorn import *
from unicorn.x86_const import *
mu = Uc(UC_ARCH_X86, UC_MODE_64)python
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.mu.emu_start(start_addr, end_addr)
**混合模式陷阱:** 如果64位存根通过`retf/retfq`跳转到32位代码,必须切换到UC_MODE_32仿真器,并复制**通用寄存器、EFLAGS和XMM寄存器**;缺失XMM状态会破坏基于SSE的转换逻辑。Multi-Stage Shellcode Loaders
多阶段Shellcode加载器
Pattern (I Heard You Liked Loaders): Nested shellcode with XOR decode loops and anti-debug.
Debugging workflow:
- Break at in launcher, step into shellcode
call rax - Bypass ptrace anti-debug: step to syscall,
set $rax=0 - Step through XOR decode loop (or break on if hidden)
int3 - Repeat for each stage until final payload
Flag extraction from instructions:
movpython
undefined模式(I Heard You Liked Loaders): 嵌套的Shellcode,包含XOR解码循环和反调试逻辑。
调试流程:
- 在加载器的处下断点,单步进入Shellcode
call rax - 绕过ptrace反调试:单步到系统调用,执行
set $rax=0 - 单步执行XOR解码循环(或在隐藏的处下断点)
int3 - 对每个阶段重复上述步骤,直到获取最终有效载荷
从指令提取flag:
movpython
undefinedFinal stage loads flag 4 bytes at a time via mov ebx, value
最终阶段通过mov ebx, value指令4字节加载flag
Extract little-endian 4-byte chunks
提取小端序的4字节块
values = [0x6174654d, 0x7b465443, ...] # From disassembly
flag = b''.join(v.to_bytes(4, 'little') for v in values)
undefinedvalues = [0x6174654d, 0x7b465443, ...] # 来自反汇编结果
flag = b''.join(v.to_bytes(4, 'little') for v in values)
undefinedTiming Side-Channel Attack
计时侧信道攻击
Pattern (Clock Out): Validation time varies per correct character (longer sleep on match).
Exploitation:
python
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模式(Clock Out): 验证时间随正确字符变化(匹配时睡眠更长)。
利用方法:
python
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_charGodot Game Asset Extraction
Godot游戏资产提取
Pattern (Steal the Xmas): Encrypted Godot .pck packages.
Tools:
Workflow:
- Run KeyDot against game executable → extract encryption key
- Input key into gdsdecomp
- Extract and open project in Godot editor
- Search scripts/resources for flag data
Unstripped Binary Information Leaks
未剥离二进制文件的信息泄露
Pattern (Bad Opsec): Debug info and file paths leak author identity.
Quick checks:
bash
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?模式(Bad Opsec): 调试信息和文件路径会泄露作者身份。
快速检查:
bash
strings binary | grep "/home/" # 主目录路径
strings binary | grep "/Users/" # macOS路径
file binary # 检查是否已剥离调试信息
readelf -S binary | grep debug # 是否存在调试段?Custom Mangle Function Reversing
自定义混淆函数逆向
Pattern (Flag Appraisal): Binary mangles input 2 bytes at a time with intermediate state, compares to static target.
Approach:
- Extract static target bytes from section
.rodata - Understand mangle: processes pairs with running state value
- Write inverse function (process in reverse, undo each operation)
- Feed target bytes through inverse → recovers flag
模式(Flag Appraisal): 二进制文件每次处理2字节输入并结合中间状态进行混淆,然后与静态目标比较。
方法:
- 从.rodata段提取静态目标字节
- 理解混淆逻辑:以对为单位处理并维护运行状态
- 编写逆向函数(反向处理,撤销每个操作)
- 将目标字节输入逆向函数 → 恢复flag
Rust serde_json Schema Recovery
Rust serde_json Schema恢复
Pattern (Curly Crab, PascalCTF 2026): Rust binary reads JSON from stdin, deserializes via serde_json, prints success/failure emoji.
Approach:
- Disassemble serde-generated implementations
Visitor - Each visitor's /
visit_mapreveals expected keys and typesvisit_seq - Look for string literals in deserializer code (field names like ,
"pascal")"CTF" - Reconstruct nested JSON schema from visitor call hierarchy
- Identify value types from visitor method names: = string,
visit_str= number,visit_u64= boolean,visit_bool= arrayvisit_seq
json
{"pascal":"CTF","CTF":2026,"crab":{"I_":true,"cr4bs":1337,"crabby":{"l0v3_":["rust"],"r3vv1ng_":42}}}Key insight: Flag is the concatenation of JSON keys in schema order. Reading field names in order reveals the flag.
模式(Curly Crab, PascalCTF 2026): Rust二进制文件从标准输入读取JSON,通过serde_json反序列化,然后打印成功/失败表情。
方法:
- 反汇编serde生成的实现
Visitor - 每个访问器的/
visit_map会暴露预期的键和类型visit_seq - 在反序列化代码中查找字符串字面量(如、
"pascal"等字段名)"CTF" - 从访问器调用层次结构重建嵌套JSON Schema
- 从访问器方法名识别值类型:=字符串,
visit_str=数字,visit_u64=布尔值,visit_bool=数组visit_seq
json
{"pascal":"CTF","CTF":2026,"crab":{"I_":true,"cr4bs":1337,"crabby":{"l0v3_":["rust"],"r3vv1ng_":42}}}核心思路: Flag是Schema中JSON键的拼接。按顺序读取字段名即可得到flag。
Position-Based Transformation Reversing
基于位置的转换逆向
Pattern (PascalCTF 2026): Binary transforms input by adding/subtracting position index.
Reversing:
python
expected = [...] # Extract from .rodata
flag = ''
for i, b in enumerate(expected):
if i % 2 == 0:
flag += chr(b - i) # Even: input = output - i
else:
flag += chr(b + i) # Odd: input = output + i模式(PascalCTF 2026): 二进制文件通过加减位置索引来转换输入。
逆向:
python
expected = [...] # 从.rodata提取
flag = ''
for i, b in enumerate(expected):
if i % 2 == 0:
flag += chr(b - i) # 偶数位:输入 = 输出 - i
else:
flag += chr(b + i) # 奇数位:输入 = 输出 + iHex-Encoded String Comparison
十六进制编码字符串比较
Pattern (Spider's Curse): Input converted to hex, compared against hex constant.
Quick solve: Extract hex constant from strings/Ghidra, decode:
bash
echo "4d65746143..." | xxd -r -p模式(Spider's Curse): 输入被转换为十六进制,然后与十六进制常量比较。
快速解决: 从字符串/Ghidra中提取十六进制常量,解码:
bash
echo "4d65746143..." | xxd -r -p