reverse-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
# angr 命令速查与操作序列
|
||||
|
||||
angr 是 Python 库,没有 CLI 命令——"命令"即 API 调用。全部用法以官方文档(docs.angr.io)与目标版本源码为准;版本差异见 [[gotchas]]。
|
||||
|
||||
## 装载族(Project)
|
||||
|
||||
```python
|
||||
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)
|
||||
|
||||
```python
|
||||
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)
|
||||
```
|
||||
|
||||
## 文件系统族
|
||||
|
||||
```python
|
||||
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 族(路径探索)
|
||||
|
||||
```python
|
||||
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)
|
||||
|
||||
```python
|
||||
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 族(路径爆炸应对)
|
||||
|
||||
```python
|
||||
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" 等
|
||||
```
|
||||
|
||||
## 分析族(其他内置分析)
|
||||
|
||||
```python
|
||||
p.analyses.CFGFast() # 快速控制流图(找关键函数/循环边界)
|
||||
p.analyses.CFGEmulated() # 模拟 CFG(重/慢)
|
||||
p.analyses.BackwardSlice(...) # 后向切片(定位影响输出的输入字节)
|
||||
p.analyses.VariableRecovery(...) # 变量恢复
|
||||
```
|
||||
|
||||
## 操作序列(组合套路)
|
||||
|
||||
### 1. CTF find/avoid 标准模板(argv 变体)
|
||||
|
||||
```python
|
||||
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 读取型)
|
||||
|
||||
```python
|
||||
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 从校验函数入口起步(跳过无关代码)
|
||||
|
||||
```python
|
||||
# 反编译定位校验函数入口([[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/长度/通道放头部注释)逐参跑,别改一处全重写
|
||||
|
||||
## 使用注意
|
||||
|
||||
- 求解过程在进程内仿真器完成,无需沙箱;**用求解结果运行目标验证时默认沙箱**([[re-analyze/platform-tips]] 最高原则)
|
||||
- 结果(地址/约束/求解脚本/flag)存档 sha256 + 路径([[re-triage]] 证据链);结论入 [[re-analyze/analysis-contract]]
|
||||
@@ -0,0 +1,45 @@
|
||||
# angr 工具特有坑与边界
|
||||
|
||||
## 版本与安装坑
|
||||
|
||||
- **Python 版本线**:**当前主线为 10.x;9.3.0 起要求 Python 3.12+**(PyPI `requires-python` 实测);**9.2.x 下限随补丁版本抬高**(9.2.91 要求 ≥3.8、9.2.203 已要求 ≥3.10),不能按「9.2.x」整线判断——装前以目标版本的 PyPI `requires-python` 为准(`pip index versions angr` 看可用版本);9.3 装到 3.11 会直接拒绝或编译失败,老的 9.2 装到 3.13 也可能无 wheel
|
||||
- **主版本间 API 变更**:angr 9.1(迁移文档有专页)改过 `SimState`/`state.posix` 等细节;网上旧教程(8.x 时代)的 `state.mem`、`simprocedures` 写法可能失效——以目标版本 docs 的 "Migrating to angr X" 为准
|
||||
- **binutils 依赖**:angr 处理/重写二进制(`angr.Project` 的 back-end 或 `angr.SimProcedure` 无关但 binary rewriting 相关操作)会调 `objcopy`——Linux 装 `binutils`,缺了报 `objcopy: command not found` 类错误
|
||||
- **Windows 支持边界**:Windows 下 pip 装 win32 wheel 可用,但部分 cle/ELF 后端与 fork 类操作受限;需要 ELF/arm 目标分析时优先 WSL 内装 Linux 版
|
||||
- **多环境共存**:angr 依赖树大(pyvex/unicorn/claripy 等),别往系统解释器塞——venv 隔离,升级先看依赖是否连带升
|
||||
|
||||
## 装载与地址坑
|
||||
|
||||
- **PIE 基址**:angr 默认按 0x400000 装载,Ghidra 按 0x100000 显示——find/avoid 地址必须换算(`p.loader.main_object.mapped_base` 核对实际基址)
|
||||
- **auto_load_libs 默认值**:新版本默认 `auto_load_libs=False` 而旧版本为 True——行为差异导致同一脚本结果不同;显式写参数,别依赖默认
|
||||
- **壳/静态链接目标**:带壳目标先脱壳([[re-unpack-simple]]);静态链接大二进制装载后状态数爆炸——hook 掉无关库函数入口
|
||||
- **架构/字节序**:arm/armeb/mips 目标 angr 支持但要注意字节序(`archinfo` 按 ELF header 自动选);`file target` 先确认再建 Project
|
||||
|
||||
## 求解语义坑
|
||||
|
||||
- **除零约束缺失**:Z3 对 `a/b` 分母为 0 求值为全 1(4294967295)——涉除法表达式显式加"分母非零"约束
|
||||
- **未初始化寄存器/内存被自动符号化**:`entry_state` 对未初始化值生成无约束符号,求解空间虚胖、结果含垃圾——`ZERO_FILL_UNCONSTRAINED_MEMORY`/`ZERO_FILL_UNCONSTRAINED_REGISTERS` 或显式设已知初值
|
||||
- **float/NaN**:浮点路径 Z3 与实机可能不一致(FP 舍入/NaN 语义)——浮点校验题优先人工还原或换 [[re-z3]] 单独建模
|
||||
- **eval 返回值的陷阱**:`eval(expr, cast_to=bytes)` 对非整字节宽符号(如 6 字节长度符号)按位宽对齐返回,多解时 `n=` 取多个逐一重放验证
|
||||
- **求解超时**:复杂约束集 eval 可能分钟级无回——`timeout=` 设上限,配合 `solver.min/max` 缩小搜索
|
||||
- **约束集合 ≠ 路径语义**:angr 求解器拿的是该状态累积路径约束,不是"程序全部行为"——求解出的输入必须沙箱内原样重放验证([[re-analyze/platform-tips]] 最高原则)
|
||||
|
||||
## SimProcedure 与 hook 坑
|
||||
|
||||
- **摘要不准**:strlen/malloc 等 SimProcedure 替代实现有 bug 或不完整,结果反直觉(错误返回值/绕过校验)——异常先怀疑摘要:禁用对应 hook 或 `project.hook` 自定义
|
||||
- **hook 未卸掉**:hook 分发器后执行跳回再次进 hook 死循环——跳到目标块后立即 unhook
|
||||
- **hook_symbol 与库调用路径**:`hook_symbol` 只影响按符号解析的调用;地址间接调用要 `project.hook(addr, ...)` 按地址挂
|
||||
- **cmovxx 不分裂分支**:angr 对 cmov 通过积累约束实现而非分裂两个状态——需要后继关系时手动 `state.copy()` 分裂并跳过 cmov 指令
|
||||
|
||||
## 性能与路径爆炸坑
|
||||
|
||||
- **符号化范围**:整段程序符号化是爆炸主因——只符号化校验真正读取的字节,入口尽量后移(blank_state)
|
||||
- **veritesting 双刃**:合并路径省状态但可能丢失分支精度(结果与不合并不同)——两个都跑对照
|
||||
- **stash 无上限**:默认 stash 不限制状态数——主动 `simgr.step()` 循环内检查 `len(active)` 或限制步数
|
||||
- **lazy_solves 与 prune**:路径多了先 `simgr.prune()` 剪无解路径,或开 `lazy_solves` 延后求解
|
||||
|
||||
## 使用注意
|
||||
|
||||
- 求解在进程内仿真器完成(免沙箱);**用求解结果运行目标时默认沙箱**
|
||||
- 地址/约束/脚本/flag 全量存档([[re-triage]] 证据链),结论入 [[re-analyze/analysis-contract]]
|
||||
- 与 [[re-z3]] 的分工:需要精确约束集合而非一条路径时 z3 更轻;angr 求解慢时对约束子集转 z3
|
||||
Reference in New Issue
Block a user