Files
reverse-skills-max/vendor-new/rev-skills/.claude/skills/re-angr/references/commands.md

6.7 KiB
Raw Blame History

angr 命令速查与操作序列

angr 是 Python 库,没有 CLI 命令——"命令"即 API 调用。全部用法以官方文档(docs.angr.io)与目标版本源码为准;版本差异见 gotchas。

装载族(Project)

import angr, claripy
p = angr.Project("./target", auto_load_libs=False)   # 关库加载:快且稳(默认行为按版本有差异)
p.loader.main_object.mapped_base                     # 实际装载基址(PIE 目标先查它)
p.loader.main_object.entry                           # 入口点
p.factory                                             # 状态/块工厂
p.factory.block(addr).vex                             # 取 VEX IR(汇编级核对)

状态族(State)

s = p.factory.entry_state()                          # 从入口
s = p.factory.full_init_state(stdin=claripy.BVS('in', 32*8))            # 标准输入符号化
s = p.factory.full_init_state(args=["./target", claripy.BVS("arg1", 64*8)])  # argv 符号化
s = p.factory.blank_state(addr=0x401000)             # 任意地址起步(校验函数入口常用)
s.regs.rax = claripy.BVS('rax', 64)                  # 设符号寄存器
s.mem[0x601000].int32 = ...                          # 符号内存写
s.posix.dumps(0)                                     # 当前 stdin 内容(find 条件常用)
s.solver.eval(...) / s.solver.add(...)               # 该状态的求解器
# 状态选项(未初始化内存/寄存器行为)
opts = {angr.options.ZERO_FILL_UNCONSTRAINED_MEMORY, angr.options.ZERO_FILL_UNCONSTRAINED_REGISTERS}
s = p.factory.entry_state(add_options=opts)

文件系统族

s.fs.insert('/tmp/in', angr.SimFile('in', content=claripy.BVS('in', 32*8)))  # 符号文件
# 程序 fopen('/tmp/in') 后读取即符号化;按 fd 操作走 s.posix.fd[0](0=stdin,1/2 是 stdout/stderr 勿混)

SimManager 族(路径探索)

simgr = p.factory.simgr(s, veritesting=True)         # veritesting 合并路径(长循环题常用)
simgr.explore(find=0x401100, avoid=[0x401200, 0x401300], num_find=1)  # 示意地址:按目标实际入口/分支替换;找到即停
simgr.found / simgr.deadended / simgr.active         # 各 stash
simgr.move('deadended', 'active')                    # stash 操作
simgr.run(n=100)                                     # 手动步数上限
simgr.prune()                                        # 剪掉已无解路径

求解族(claripy / solver)

inp = claripy.BVS('inp', 32*8)                       # 符号变量(位宽=字节数*8)
claripy.BVV(0x41, 8)                                 # 具体值
solver = found.solver
solver.add(inp.get_byte(0) == ord('f'))              # 加约束(逐字节)
flag = solver.eval(inp, cast_to=bytes)               # 求值
solver.satisfiable()                                 # 可满足性检查
solver.min(inp) / solver.max(inp)                    # 极值(长度约束常用)
for c in flag_sym.chop(8): solver.add(0x20 <= c, c <= 0x7e)   # 可打印字符约束
# eval 超时与数量: solver.eval(expr, n=..., cast_to=bytes, timeout=...) 按需

Hook 族(路径爆炸应对)

project.hook(addr, angr.SIM_PROCEDURES["stubs"]["ReturnUnconstrained"]())  # 地址处替换
project.hook_symbol("sleep", angr.SIM_PROCEDURES["libc"]["sleep"])          # 按符号替换
project.hook_symbol("rand", angr.SIM_PROCEDURES["libc"]["rand"])            # 固定随机值
project.unhook(addr) / project.unhook_symbol("sleep")                        # 还原
# SIM_PROCEDURES 类目: "stubs" 通用替身 / "libc" / "linux_kernel" / "posix" 等

分析族(其他内置分析)

p.analyses.CFGFast()              # 快速控制流图(找关键函数/循环边界)
p.analyses.CFGEmulated()          # 模拟 CFG(重/慢)
p.analyses.BackwardSlice(...)     # 后向切片(定位影响输出的输入字节)
p.analyses.VariableRecovery(...)  # 变量恢复

操作序列(组合套路)

1. CTF find/avoid 标准模板(argv 变体)

import angr, claripy
p = angr.Project("./target", auto_load_libs=False)
arg = claripy.BVS("arg1", 64*8)
s = p.factory.full_init_state(args=["./target", arg])
simgr = p.factory.simgr(s, veritesting=True)
simgr.explore(find=0x401100, avoid=[0x401200, 0x401300])   # 示意地址:按目标实际入口/分支替换
if simgr.found:
    print(simgr.found[0].solver.eval(arg, cast_to=bytes))

2. 文件输入符号化(fopen 读取型)

s = p.factory.full_init_state()
s.fs.insert('/tmp/in', angr.SimFile('in', content=claripy.BVS('in', 0x40*8)))  # 长度按读取上限
simgr = p.factory.simgr(s)
simgr.explore(find=..., avoid=...)
sol = simgr.found[0].solver.eval(s.fs.get('/tmp/in').content, cast_to=bytes)
open('flag.bin','wb').write(sol)     # 沙箱内 ./target < flag.bin 验证

3. blank_state 从校验函数入口起步(跳过无关代码)

# 反编译定位校验函数入口([[re-ghidra]])与参数约定
s = p.factory.blank_state(addr=check_entry, add_options={angr.options.ZERO_FILL_UNCONSTRAINED_REGISTERS})
s.regs.rdi = claripy.BVS('inp', 32*8)     # 按调用约定放参数
s.mem[s.regs.rsp] = 0x0                   # 栈帧最小化(返回地址)
simgr = p.factory.simgr(s)
simgr.explore(find=success_addr, avoid=fail_addr)

4. 循环题提速(hook + veritesting + 分段)

hook 无关调用(sleep/memset/rand)→ simgr(veritesting=True)
仍慢: 探索到循环边界地址 → 对循环体单独符号化展开(人工定位循环不变量)
仍爆炸 → 换 [[re-z3]] 对已展开约束建模

实现教训(内化)

  • auto_load_libs=False 是默认建议:库加载既慢又让求解空间虚胖;样本带壳先脱壳(re-deobfuscate)再符号执行
  • find/avoid 的地址按 angr 装载基址填(PIE 与 Ghidra 显示基址差固定偏移),脚本头部注释标明换算关系
  • 求解出的值先 repr() 检查再重放:非打印字符常是符号化长度 < 实际读取长度
  • 涉及除法的表达式显式加"分母非零"约束(Z3 对 a/0 求值全 1)——见 gotchas
  • 探索超时用 simgr.explore(..., timeout=...) 或 s.solver.eval(..., timeout=...) 设秒级上限,别无限等
  • eval(expr, cast_to=bytes) 只对整字节宽符号有意义;逐字节用 get_byte(i)/chop(8)
  • 一个目标多轮求解时,脚本参数化(find/avoid/长度/通道放头部注释)逐参跑,别改一处全重写

使用注意