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]]
|
||||
Reference in New Issue
Block a user