深入浅出angr(二)
字数 747 2025-08-06 08:35:32
深入浅出angr(二) - 输入输出与条件约束
1. 命令行输入处理
1.1 基本方法
在angr中处理命令行输入需要使用claripy模块创建符号变量:
import angr
import claripy
# 创建符号变量
argv1 = claripy.BVS("argv1", 100*8) # 100字节的符号变量
initial_state = p.factory.entry_state(args=["./ais3_crackme", argv1])
1.2 获取执行结果
找到正确路径后,可以通过solver.eval()获取符号变量的具体值:
found = sm.found[0]
solution = found.solver.eval(argv1, cast_to=bytes)
solution = solution[:solution.find(b"\x00")] # 截取到第一个null字节
2. 标准输入处理
2.1 构造输入变量
对于标准输入(stdin),可以构造字符数组:
flag_chars = [claripy.BVS('flag_%d' % i, 8) for i in range(28)] # 28个字节
flag = claripy.Concat(*flag_chars + [claripy.BVV(b'\n')]) # 添加换行符
2.2 初始化状态
对于C++程序,需要使用full_init_state并启用unicorn引擎:
st = p.factory.full_init_state(
args=['./wyvern'],
add_options=angr.options.unicorn,
stdin=flag,
)
2.3 添加约束条件
为提高效率,可以添加输入约束:
for k in flag_chars:
st.solver.add(k != 0)
st.solver.add(k != 10) # 排除换行符
3. 执行管理
3.1 三种执行方式
- step(): 每次运行一个基本块
- run(): 运行所有基本块,返回deadended状态
- explore(): 根据find/avoid条件执行
3.2 从deadended状态提取结果
for pp in sm.deadended:
out = pp.posix.dumps(1) # 获取stdout
if b'flag{' in out:
return out[out.find(b"flag{"):]
4. 高级条件约束
4.1 内存约束
可以对内存内容添加约束:
flag_addr = found.regs.rdi
found.add_constraints(
found.memory.load(flag_addr, 5) == int(binascii.hexlify(b"ASIS{"), 16)
)
4.2 复杂约束示例
flag = found.memory.load(flag_addr, 40)
for i in range(5, 5+32):
# 约束字符在0-9或a-f范围内
cond_0 = flag.get_byte(i) >= ord('0')
cond_1 = flag.get_byte(i) <= ord('9')
cond_2 = flag.get_byte(i) >= ord('a')
cond_3 = flag.get_byte(i) <= ord('f')
cond_4 = found.solver.And(cond_0, cond_1)
cond_5 = found.solver.And(cond_2, cond_3)
found.add_constraints(found.solver.Or(cond_4, cond_5))
# 约束最后一个字符为}
found.add_constraints(flag.get_byte(32+5) == ord('}'))
5. 完整示例
5.1 命令行输入示例
#!/usr/bin/env python
import angr
import claripy
def main():
project = angr.Project("./ais3_crackme")
argv1 = claripy.BVS("argv1", 100*8)
initial_state = project.factory.entry_state(args=["./crackme1", argv1])
sm = project.factory.simulation_manager(initial_state)
sm.explore(find=0x400602) # 正确分支地址
found = sm.found[0]
solution = found.solver.eval(argv1, cast_to=bytes)
solution = solution[:solution.find(b"\x00")]
print(solution)
return solution
if __name__ == '__main__':
print(repr(main()))
5.2 标准输入示例
import angr
import claripy
def solve():
p = angr.Project("./wyvern")
flag_chars = [claripy.BVS('flag_%d' % i, 8) for i in range(28)]
flag = claripy.Concat(*flag_chars + [claripy.BVV(b'\n')])
st = p.factory.full_init_state(
args=['./wyvern'],
add_options=angr.options.unicorn,
stdin=flag,
)
for k in flag_chars:
st.solver.add(k != 0)
st.solver.add(k != 10)
sm = p.factory.simulation_manager(st)
sm.run()
for pp in sm.deadended:
out = pp.posix.dumps(1)
if b'flag{' in out:
return out[out.find(b"flag{"):]
6. 关键点总结
- 符号变量创建:使用
claripy.BVS()创建符号变量,BVV()创建具体值 - 状态初始化:
entry_state用于命令行参数,full_init_state用于复杂程序 - 约束添加:通过
solver.add()添加约束条件提高效率 - 结果提取:使用
solver.eval()将符号值转换为具体值 - 执行控制:根据需求选择
step()、run()或explore() - 内存操作:可以通过
memory.load()和memory.store()操作内存
通过掌握这些技术,可以有效地使用angr进行二进制分析,解决各种逆向工程问题。