ctf-reverse

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

CTF 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

问题解决流程

  1. Start with strings extraction - many easy challenges have plaintext flags
  2. Try ltrace/strace - dynamic analysis often reveals flags without reversing
  3. Map control flow before modifying execution
  4. Automate manual processes via scripting (r2pipe, Python)
  5. Validate assumptions by comparing decompiler outputs
  1. 先提取字符串 - 许多简单挑战包含明文flag
  2. 尝试ltrace/strace - 动态分析通常无需逆向即可直接获取flag
  3. 先映射控制流再修改执行逻辑
  4. 通过脚本自动化手动流程(r2pipe、Python)
  5. 通过对比反编译器输出验证假设

Quick Wins (Try First!)

快速尝试技巧(优先试试!)

bash
undefined
bash
undefined

Plaintext 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
undefined

Initial Analysis

初始分析

bash
file binary           # Type, architecture
checksec --file=binary # Security features (for pwn)
chmod +x binary       # Make executable
bash
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           # 在最终比较处下断点
run

Enter any input of correct length

输入任意长度正确的内容

x/s $rsi # Dump computed flag x/38c $rsi # As characters
undefined
x/s $rsi # 转储计算出的flag x/38c $rsi # 以字符形式查看
undefined

Decoy Flag Detection

诱饵Flag检测

Pattern: Multiple fake targets before real check.
Identification:
  1. Look for multiple comparison targets in sequence
  2. Check for different success messages
  3. Trace which comparison is checked LAST
Solution: Set breakpoint at FINAL comparison, not earlier ones.
模式: 真实校验前存在多个伪造目标。
识别方法:
  1. 查找连续的多个比较目标
  2. 检查是否有不同的成功提示信息
  3. 追踪最后被校验的比较逻辑
解决方案:最终比较处下断点,而非更早的位置。

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
run
PIE二进制文件会随机化基地址,使用相对断点:
bash
gdb ./binary
start                    # 强制解析PIE基地址
b *main+0xca            # 相对于main函数的偏移
run

Comparison Direction (Critical!)

比较方向(至关重要!)

Two patterns:
  1. transform(flag) == stored_target
    - Reverse the transform
  2. transform(stored_target) == flag
    - Flag IS the transformed data!
Pattern 2 solution: Don't reverse - just apply transform to stored target.
两种模式:
  1. transform(flag) == stored_target
    - 逆向转换逻辑
  2. transform(stored_target) == flag
    - 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 (
    ^ i
    or
    ^ (i & 0xff)
    ) layered with a repeating key
  • 单字节XOR - 尝试所有256种可能值
  • 已知明文XOR(
    flag{
    CTF{
  • 硬编码密钥的RC4
  • 自定义置换+XOR
  • 结合位置索引的XOR(
    ^ i
    ^ (i & 0xff)
    ),并搭配重复密钥

Quick Tool Reference

工具速查

bash
undefined
bash
undefined

Radare2

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
undefined
ida64 binary # 用IDA64打开
undefined

Binary 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 checker
bash
wasm2c checker.wasm -o checker.c
gcc -O3 checker.c wasm-rt-impl.c -o checker

WASM 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.xml
bash
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 unpacked
bash
upx -d packed -o unpacked

Anti-Debugging Bypass

反调试绕过

Common checks:
  • IsDebuggerPresent()
    (Windows)
  • ptrace(PTRACE_TRACEME)
    (Linux)
  • /proc/self/status
    TracerPid
  • Timing checks
Bypass: Set breakpoint at check, modify register to bypass conditional.
常见检查手段:
  • IsDebuggerPresent()
    (Windows)
  • ptrace(PTRACE_TRACEME)
    (Linux)
  • /proc/self/status
    中的TracerPid
  • 计时检查
绕过方法:在检查处下断点,修改寄存器值以绕过条件判断。

S-Box / Keystream Patterns

S盒 / 密钥流模式

Xorshift32: Shifts 13, 17, 5 Xorshift64: Shifts 12, 25, 27 Magic constants:
0x2545f4914f6cdd1d
,
0x9e3779b97f4a7c15
Xorshift32: 移位13、17、5位 Xorshift64: 移位12、25、27位 魔术常量:
0x2545f4914f6cdd1d
0x9e3779b97f4a7c15

Custom VM Analysis

自定义VM分析

  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
VM challenge workflow (C'est La V(M)ie):
python
undefined
  1. 识别结构:寄存器、内存、指令指针(IP)
  2. 逆向
    executeIns
    以理解操作码含义
  3. 编写反汇编器,将操作码映射为助记符
  4. 通常暴力破解比完全逆向更简单
  5. 查找通过命令行参数加载的字节码文件
VM挑战流程(C'est La V(M)ie):
python
undefined

1. 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
dis.dis()
output of a flag checker.
Key instructions:
  • LOAD_GLOBAL
    /
    LOAD_FAST
    — push name/variable onto stack
  • CALL N
    — pop function + N args, call, push result
  • BINARY_SUBSCR
    — pop index and sequence, push
    seq[idx]
  • COMPARE_OP
    — pop two values, compare (55=
    !=
    , 40=
    ==
    )
  • POP_JUMP_IF_TRUE/FALSE
    — conditional branch
Reversing XOR flag checkers:
python
undefined
模式(Slithering Bytes): 给定flag检查器的
dis.dis()
输出。
关键指令:
  • LOAD_GLOBAL
    /
    LOAD_FAST
    — 将名称/变量推入栈
  • CALL N
    — 弹出函数+N个参数,调用后将结果推入栈
  • BINARY_SUBSCR
    — 弹出索引和序列,将
    seq[idx]
    推入栈
  • COMPARE_OP
    — 弹出两个值进行比较(55=
    !=
    ,40=
    ==
  • POP_JUMP_IF_TRUE/FALSE
    — 条件分支
逆向XOR flag检查器:
python
undefined

Pattern: 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)
undefined
odd_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)
undefined

Signal-Based Binary Exploration

基于信号的二进制探索

Pattern (Signal Signal Little Star): Binary uses UNIX signals as a binary tree navigation mechanism.
Identification:
  • Multiple
    sigaction()
    calls with
    SA_SIGINFO
  • sigaltstack()
    setup (alternate signal stack)
  • Handler decodes embedded payload, installs next pair of signals
  • Two types: Node (installs children) vs Leaf (prints message + exits)
Solving approach:
  1. Hook
    sigaction
    via
    LD_PRELOAD
    to log signal installations
  2. DFS through the binary tree by sending signals
  3. At each stage, observe which 2 signals are installed
  4. Send one, check if program exits (leaf) or installs 2 more (node)
  5. 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 叶子(打印信息并退出)
解决方法:
  1. 通过
    LD_PRELOAD
    挂钩
    sigaction
    以记录信号安装情况
  2. 通过发送信号深度优先遍历二叉树
  3. 在每个阶段,观察安装的两个信号
  4. 发送其中一个,检查程序是否退出(叶子节点)或安装另外两个信号(节点)
  5. 如果是错误的叶子节点,回溯并尝试兄弟节点
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:
CheckTechniquePatch
ptrace(PTRACE_TRACEME)
Anti-debugChange
cmp -1
to
cmp 0
sleep(150)
Anti-sandbox timingChange sleep value to 1
/proc/cpuinfo
"hypervisor"
Anti-VMFlip
JNZ
to
JZ
"VMware"/"VirtualBox" stringsAnti-VMFlip
JNZ
to
JZ
getpwuid
username check
EnvironmentFlip comparison
LD_PRELOAD
check
Anti-hookSkip check
Fan count / hardware checkAnti-VMFlip
JLE
to
JGE
Hostname checkEnvironmentFlip
JNZ
to
JZ
Ghidra patching workflow:
  1. Find check function, identify the conditional jump
  2. Click on instruction →
    Ctrl+Shift+G
    → modify opcode
  3. For
    JNZ
    (0x75) →
    JZ
    (0x74), or vice versa
  4. For immediate values: change operand bytes directly
  5. Export: press
    O
    → choose "Original File" format
  6. chmod +x
    the patched binary
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): 恶意软件在执行有效载荷前会进行多项环境检查。
常见需补丁的检查项:
检查项技术类型补丁方法
ptrace(PTRACE_TRACEME)
反调试
cmp -1
改为
cmp 0
sleep(150)
反沙箱计时将sleep值改为1
/proc/cpuinfo
中的"hypervisor"
反虚拟机
JNZ
改为
JZ
"VMware"/"VirtualBox"字符串反虚拟机
JNZ
改为
JZ
getpwuid
用户名检查
环境检查翻转比较逻辑
LD_PRELOAD
检查
反挂钩跳过检查
风扇数量/硬件检查反虚拟机
JLE
改为
JGE
主机名检查环境检查翻转比较逻辑
Ghidra补丁流程:
  1. 找到检查函数,识别条件跳转指令
  2. 点击指令 →
    Ctrl+Shift+G
    → 修改操作码
  3. 例如将
    JNZ
    (0x75)改为
    JZ
    (0x74),反之亦然
  4. 对于立即数:直接修改操作数字节
  5. 导出:按
    O
    → 选择"Original File"格式
  6. 给补丁后的二进制文件设置可执行权限:
    chmod +x
服务端验证绕过:
  • 如果补丁后的二进制文件会向远程服务器发送系统信息,也需补丁这些数据
  • 修改数据收集函数中的字符串地址
  • 修改格式字符串以直接嵌入正确值

Expected Values Tables

期望值表

Locating:
bash
objdump -s -j .rodata binary | less
定位方法:
bash
objdump -s -j .rodata binary | less

Look near comparison instructions

在比较指令附近查找

Size matches flag length

大小与flag长度匹配

undefined
undefined

x86-64 Gotchas

x86-64注意事项

Sign extension:
0xffffffc7
behaves differently in XOR vs addition
python
undefined
符号扩展:
0xffffffc7
在XOR和加法中的表现不同
python
undefined

For XOR: use low byte

对于XOR:使用低字节

esi_xor = esi & 0xff
esi_xor = esi & 0xff

For addition: use full value with overflow

对于加法:使用带溢出的完整值

result = (r13 + esi) & 0xffffffff
undefined
result = (r13 + esi) & 0xffffffff
undefined

Iterative 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)
            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.
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:
  1. Break at
    call rax
    in launcher, step into shellcode
  2. Bypass ptrace anti-debug: step to syscall,
    set $rax=0
  3. Step through XOR decode loop (or break on
    int3
    if hidden)
  4. Repeat for each stage until final payload
Flag extraction from
mov
instructions:
python
undefined
模式(I Heard You Liked Loaders): 嵌套的Shellcode,包含XOR解码循环和反调试逻辑。
调试流程:
  1. 在加载器的
    call rax
    处下断点,单步进入Shellcode
  2. 绕过ptrace反调试:单步到系统调用,执行
    set $rax=0
  3. 单步执行XOR解码循环(或在隐藏的
    int3
    处下断点)
  4. 对每个阶段重复上述步骤,直到获取最终有效载荷
mov
指令提取flag:
python
undefined

Final 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)
undefined
values = [0x6174654d, 0x7b465443, ...] # 来自反汇编结果 flag = b''.join(v.to_bytes(4, 'little') for v in values)
undefined

Timing 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_char

Godot Game Asset Extraction

Godot游戏资产提取

Pattern (Steal the Xmas): Encrypted Godot .pck packages.
Tools:
  • gdsdecomp - Extract Godot packages
  • KeyDot - Extract encryption key from Godot executables
Workflow:
  1. Run KeyDot against game executable → extract encryption key
  2. Input key into gdsdecomp
  3. Extract and open project in Godot editor
  4. Search scripts/resources for flag data
模式(Steal the Xmas): 加密的Godot .pck包。
工具:
  • gdsdecomp - 提取Godot包
  • KeyDot - 从Godot可执行文件中提取加密密钥
流程:
  1. 对游戏可执行文件运行KeyDot → 提取加密密钥
  2. 将密钥输入gdsdecomp
  3. 提取项目并在Godot编辑器中打开
  4. 在脚本/资源中搜索flag数据

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:
  1. Extract static target bytes from
    .rodata
    section
  2. Understand mangle: processes pairs with running state value
  3. Write inverse function (process in reverse, undo each operation)
  4. Feed target bytes through inverse → recovers flag
模式(Flag Appraisal): 二进制文件每次处理2字节输入并结合中间状态进行混淆,然后与静态目标比较。
方法:
  1. 从.rodata段提取静态目标字节
  2. 理解混淆逻辑:以对为单位处理并维护运行状态
  3. 编写逆向函数(反向处理,撤销每个操作)
  4. 将目标字节输入逆向函数 → 恢复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:
  1. Disassemble serde-generated
    Visitor
    implementations
  2. Each visitor's
    visit_map
    /
    visit_seq
    reveals expected keys and types
  3. Look for string literals in deserializer code (field names like
    "pascal"
    ,
    "CTF"
    )
  4. Reconstruct nested JSON schema from visitor call hierarchy
  5. Identify value types from visitor method names:
    visit_str
    = string,
    visit_u64
    = number,
    visit_bool
    = boolean,
    visit_seq
    = array
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反序列化,然后打印成功/失败表情。
方法:
  1. 反汇编serde生成的
    Visitor
    实现
  2. 每个访问器的
    visit_map
    /
    visit_seq
    会暴露预期的键和类型
  3. 在反序列化代码中查找字符串字面量(如
    "pascal"
    "CTF"
    等字段名)
  4. 从访问器调用层次结构重建嵌套JSON Schema
  5. 从访问器方法名识别值类型:
    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)   # 奇数位:输入 = 输出 + i

Hex-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