关于 symbolic-execution-tools
面向安全分析人员的符号执行与仿真指南,涵盖 CTF 和二进制分析。内容按任务介绍 angr、Z3、Unicorn 与 Qiling 的选择、配置、钩子和路径管理。
主要功能
- 01angr 二进制路径探索
- 02Z3 约束求解
- 03Unicorn 代码仿真
- 04函数钩子与 SimProcedure
- 05路径爆炸管理
适用场景
- 01求解 CTF 二进制检查
- 02从约束中恢复数值
- 03仿真代码以解包或解密
- 04分析固件或自定义 VM 例程
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ yaklang/ hack-skills --skill symbolic-execution-tools安装后在 Claude Code 中输入/symbolic-execution-tools确认已加载。
附带文件ANGR_COOKBOOK.md
详细介绍
技能:符号执行工具——专家分析手册
AI 加载说明:介绍使用 angr、Z3 和 Unicorn Engine 的专家级符号执行技巧。涵盖 CTF 挑战自动化、约束求解模式、函数 Hook、SimProcedure 替换和基于仿真的解包。由于状态初始化不正确或缺少 libc 函数的 Hook,基础模型经常会生成有问题的 angr 脚本。
0. 相关路由
- 需要通过符号执行绕过反调试检查时,参阅 anti-debugging-techniques
- 使用符号执行进行反混淆时,参阅 code-obfuscation-deobfuscation
- 将 angr 应用于自定义 VM 挑战时,参阅 vm-and-bytecode-reverse
进阶参考
遇到以下需求时,也请加载 ANGR_COOKBOOK.md:
- 15+ 种适用于常见 CTF 挑战的即用型 angr 脚本模式
- scanf、printf、malloc、strcmp 的 Hook 模板
- 符号化文件输入、stdin、argv 模式
- 管理路径爆炸的优化技巧
何时使用哪种工具
| 场景 | 最佳工具 | 原因 |
|---|---|---|
| 纯数学问题/方程组 | Z3 | 直接求解约束,无需二进制文件 |
| 带控制流的二进制文件 | angr | 探索路径,自动管理约束 |
| 仿真特定代码区域 | Unicorn | 速度快,无符号执行开销,适合解包 |
| 复杂二进制文件 + 自定义 VM | angr + Unicorn(组合使用) | angr 负责控制流,Unicorn 负责 VM 处理程序 |
| 内核/固件代码 | Qiling | 提供具备 OS 感知能力的完整系统仿真 |
1. ANGR——核心概念
1.1 流程
Project(binary)
→ Factory.entry_state() / blank_state(addr=)
→ SimulationManager(state)
→ explore(find=target, avoid=bad)
→ found[0].solver.eval(symbolic_var)
1.2 基本设置
import angr
import claripy
proj = angr.Project('./challenge', auto_load_libs=False)
# Entry state: start from program entry point
state = proj.factory.entry_state()
# Blank state: start from arbitrary address
state = proj.factory.blank_state(addr=0x401000)
# Full init state: with command-line args
state = proj.factory.full_init_state(args=['./challenge', arg1_sym])
simgr = proj.factory.simulation_manager(state)
simgr.explore(find=0x401234, avoid=[0x401300])
if simgr.found:
found = simgr.found[0]
solution = found.solver.eval(symbolic_input, cast_to=bytes)
print(f"Solution: {solution}")
1.3 符号变量(claripy)
# Bitvector (fixed-size integer)
sym_input = claripy.BVS("input", 64) # 64-bit symbolic
sym_byte = claripy.BVS("byte", 8) # 8-bit symbolic
sym_buf = claripy.BVS("buffer", 8 * 32) # 32-byte buffer
# Concrete bitvector
concrete = claripy.BVV(0x41, 8) # concrete value 0x41
# Constraints
state.solver.add(sym_input > 0)
state.solver.add(sym_input < 100)
state.solver.add(sym_byte >= 0x20) # printable ASCII
state.solver.add(sym_byte <= 0x7e)
# Evaluate
value = state.solver.eval(sym_input)
all_values = state.solver.eval_upto(sym_input, 10) # up to 10 solutions
1.4 符号化 stdin
flag_len = 32
sym_stdin = claripy.BVS("stdin", 8 * flag_len)
state = proj.factory.entry_state(stdin=sym_stdin)
# Constrain to printable ASCII
for i in range(flag_len):
byte = sym_stdin.get_byte(i)
state.solver.add(byte >= 0x20)
state.solver.add(byte <= 0x7e)
1.5 Hook 函数
# Hook by address (skip N bytes of original code)
@proj.hook(0x401100, length=5)
def skip_check(state):
state.regs.eax = 1 # force success
# SimProcedure: replace library function
class MyStrcmp(angr.SimProcedure):
def run(self, s1, s2):
return claripy.If(
self.state.memory.load(s1, 32) == self.state.memory.load(s2, 32),
claripy.BVV(0, 32),
claripy.BVV(1, 32)
)
proj.hook_symbol('strcmp', MyStrcmp())
# Hook common problematic functions
proj.hook_symbol('printf', angr.SIM_PROCEDURES['libc']['printf']())
proj.hook_symbol('scanf', angr.SIM_PROCEDURES['libc']['scanf']())
proj.hook_symbol('puts', angr.SIM_PROCEDURES['libc']['puts']())
1.6 内存操作
# Read memory (symbolic-aware)
data = state.memory.load(addr, size) # returns BV
data_concrete = state.solver.eval(data, cast_to=bytes)
# Write memory
state.memory.store(addr, claripy.BVV(0x41, 8))
state.memory.store(addr, sym_buf)
# Read/write registers
rax = state.regs.rax
state.regs.rdi = claripy.BVV(0x1000, 64)
2. Z3 约束求解
2.1 核心 API
from z3 import *
# Sorts
x = BitVec('x', 32) # 32-bit bitvector
y = Int('y') # arbitrary precision integer
b = Bool('b') # boolean
# Solver
s = Solver()
s.add(x + y == 42)
s.add(x > 0)
s.add(y > 0)
if s.check() == sat:
m = s.model()
print(f"x = {m[x]}, y = {m[y]}")
2.2 常见 CTF 模式
# Serial key validation: each char satisfies constraints
key = [BitVec(f'k{i}', 8) for i in range(16)]
s = Solver()
for k in key:
s.add(k >= 0x30, k <= 0x7a) # alphanumeric-ish
# XOR key recovery
plaintext = b"known_plaintext"
ciphertext = b"\x12\x34..."
key_byte = BitVec('key', 8)
s = Solver()
for p, c in zip(plaintext, ciphertext):
s.add(p ^ key_byte == c)
# System of linear equations (modular)
a, b, c = BitVecs('a b c', 32)
s = Solver()
s.add(3*a + 5*b + 7*c == 0x12345678)
s.add(2*a + 4*b + 6*c == 0xDEADBEEF)
s.add(a ^ b ^ c == 0xCAFEBABE)
2.3 优化
from z3 import Optimize
opt = Optimize()
x = BitVec('x', 32)
opt.add(x > 0)
opt.add(x < 1000)
opt.minimize(x) # find smallest satisfying value
opt.check()
print(opt.model())
3. UNICORN ENGINE——代码仿真
3.1 基本设置
from unicorn import *
from unicorn.x86_const import *
from capstone import Cs, CS_ARCH_X86, CS_MODE_64
mu = Uc(UC_ARCH_X86, UC_MODE_64)
CODE_ADDR = 0x400000
STACK_ADDR = 0x7fff0000
STACK_SIZE = 0x10000
mu.mem_map(CODE_ADDR, 0x10000)
mu.mem_map(STACK_ADDR, STACK_SIZE)
mu.mem_write(CODE_ADDR, code_bytes)
mu.reg_write(UC_X86_REG_RSP, STACK_ADDR + STACK_SIZE - 0x1000)
mu.reg_write(UC_X86_REG_RBP, STACK_ADDR + STACK_SIZE - 0x1000)
mu.emu_start(CODE_ADDR, CODE_ADDR + len(code_bytes))
result = mu.reg_read(UC_X86_REG_RAX)
3.2 Hook 内存和指令
# Hook memory access
def hook_mem(uc, access, address, size, value, user_data):
if access == UC_MEM_WRITE:
print(f"Write {value:#x} to {address:#x}")
elif access == UC_MEM_READ:
print(f"Read from {address:#x}")
mu.hook_add(UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE, hook_mem)
# Hook specific instruction (for tracing)
def hook_code(uc, address, size, user_data):
code = uc.mem_read(address, size)
md = Cs(CS_ARCH_X86, CS_MODE_64)
for insn in md.disasm(bytes(code), address):
print(f" {insn.address:#x}: {insn.mnemonic} {insn.op_str}")
mu.hook_add(UC_HOOK_CODE, hook_code)
3.3 使用场景
| 使用场景 | 方法 |
|---|---|
| 解包 shellcode | 映射 shellcode,进行仿真,并转储解码后的载荷 |
| 解密字符串 | 使用受控输入仿真解密函数 |
| 暴力破解短密钥 | 使用不同密钥输入循环仿真 |
| 分析混淆函数 | 仿真函数,观察寄存器/内存状态 |
| 固件代码仿真 | 映射固件内存布局,仿真例程 |
4. ANGR 探索策略
4.1 find/avoid
simgr.explore(
find=lambda s: b"Correct" in s.posix.dumps(1), # stdout contains "Correct"
avoid=lambda s: b"Wrong" in s.posix.dumps(1) # avoid "Wrong" output
)
4.2 管理路径爆炸
| 策略 | 实现方式 |
|---|---|
| 限制输入空间 | 添加约束(可打印字符、长度限制) |
| 避开死胡同路径 | 使用 avoid= 指定已知失败地址 |
| Hook 复杂函数 | 替换为简化的 SimProcedure |
| 限制循环迭代次数 | state.options.add(angr.options.LAZY_SOLVES) |
| 使用 veritesting | simgr.explore(..., technique=angr.exploration_techniques.Veritesting()) |
| 使用 DFS 而非 BFS | simgr.use_technique(angr.exploration_techniques.DFS()) |
| 设置每条路径的超时 | simgr.explore(..., num_find=1) + 超时封装 |
4.3 具体执行与符号执行混合模式
state = proj.factory.entry_state(
add_options={angr.options.UNICORN} # use Unicorn for concrete regions
)
这会大幅提升执行速度:具体代码通过 Unicorn 原生运行,只有涉及符号变量时才切换到符号执行。
5. 实用工作流
5.1 CTF 二进制求解工作流
1. Static analysis: identify input method, success/fail conditions
└─ Find "Correct" / "Wrong" strings → get their xref addresses
2. Choose tool:
├─ Pure math (no binary needed) → Z3
├─ Small binary, clear success/fail → angr explore
└─ Specific function to emulate → Unicorn
3. Set up symbolic input:
├─ stdin → claripy.BVS + entry_state(stdin=)
├─ argv → full_init_state(args=[...])
├─ file input → SimFile
└─ specific memory → state.memory.store(addr, sym)
4. Hook problematic functions:
├─ printf/puts → SimProcedure or no-op
├─ scanf → custom handler
├─ time/random → return concrete value
└─ anti-debug → skip entirely
5. Explore and extract:
└─ simgr.explore(find=, avoid=) → solver.eval()
6. 决策树
Need to solve a reversing challenge?
│
├─ Is the challenge pure math / equations?
│ └─ Yes → Z3
│ ├─ Linear equations → BitVec + Solver
│ ├─ Modular arithmetic → BitVec (natural mod 2^n)
│ ├─ Boolean logic → Bool + Solver
│ └─ Optimization → Optimize + minimize/maximize
│
├─ Is it a compiled binary with clear success/fail?
│ └─ Yes → angr
│ ├─ Input via stdin → symbolic stdin
│ ├─ Input via argv → full_init_state with symbolic args
│ ├─ Input via file → SimFile
│ ├─ Path explosion → add constraints, avoid paths, hook loops
│ └─ Complex library calls → hook with SimProcedure
│
├─ Need to emulate a specific function/region?
│ └─ Yes → Unicorn Engine
│ ├─ Decryption routine → map code + data, emulate, read result
│ ├─ Shellcode analysis → map shellcode, hook syscalls
│ └─ Key schedule → emulate with different inputs
│
├─ Need to analyze firmware / exotic arch?
│ └─ Yes → Qiling (full system emulation with OS support)
│
├─ Binary has VM protection?
│ └─ angr for handler analysis + Z3 for bytecode constraints
│
└─ None of the above working?
├─ Combine: Unicorn for concrete regions + Z3 for constraints
├─ Manual reverse engineering with debugger
└─ Side-channel approach (timing, power analysis for hardware)
7. 常见问题与修复方法
| 问题 | 原因 | 修复方法 |
|---|---|---|
| angr 永远卡住 | 循环中路径爆炸 | 为循环回边添加 avoid=,或挂钩循环 |
Z3 返回 unknown | 非线性约束过于复杂 | 简化约束,将问题拆分为多个子问题,使用 set_param("timeout", 5000) |
| Unicorn 遇到系统调用时崩溃 | 系统调用未处理 | 挂钩系统调用中断,并进行处理或跳过 |
| angr 结果错误 | 状态初始化有误 | 确认初始内存布局与实际二进制文件一致 |
| 符号内存过大 | 符号化读取无界 | 尽可能具体化数组索引 |
| SimProcedure 类型错误 | 参数类型不匹配 | 检查调用约定(cdecl 还是 fastcall) |
| angr 无法加载二进制文件 | 缺少库 | 使用 auto_load_libs=False,并挂钩所需符号 |
8. 工具版本与安装
# angr (Python 3.8+)
pip install angr
# Z3
pip install z3-solver
# Unicorn Engine
pip install unicorn
# Capstone (disassembly, pairs with Unicorn)
pip install capstone
# Keystone (assembly)
pip install keystone-engine
评论
支持 Markdown