Angr 学习

Angr

angr 的介绍

angr是一个多体系结构的二进制分析工具包,能够执行动态符号执行(如Mayhem,KLEE等)和对二进制文件的各种静态分析。

安装

angr 是一个 python 库,强烈建议使用 python 虚拟环境进行安装。首先安装 virtualenv
在虚拟环境中执行 pip install angr 完成安装。

  • 注意:如果是在 macOS 上安装,建议使用自己安装的 python 环境进行安装,这样会减少系统 python 带来的各种权限冲突问题。

更多系统安装参考

核心概念

顶级接口 (Top level interface)

>>> import angr
>>> proj = angr.Project('/bin/true')

基本属性 (Basic properties)

>>> proj.arch
<Arch AMD64 (LE)>
>>> proj.entry
0x401670
>>> proj.filename
'/bin/true'

装载器 (The loader)

>>> proj.loader
<Loaded true, maps [0x400000:0x5004000]>

>>> proj.loader.shared_objects # may look a little different for you!
{'ld-linux-x86-64.so.2': <ELF Object ld-2.24.so, maps [0x2000000:0x2227167]>,
 'libc.so.6': <ELF Object libc-2.24.so, maps [0x1000000:0x13c699f]>}

>>> proj.loader.min_addr # 内存映射地址范围
0x400000
>>> proj.loader.max_addr
0x5004000

>>> proj.loader.main_object  
<ELF Object true, maps [0x400000:0x60721f]>

>>> proj.loader.main_object.execstack  # 是否包含可执行堆
False

工厂 (The factory)

Angr 为了避免使用多相同场景下出现多过的重复代码,提供了工厂类 project.factor

块 (Blocks)

>>> block = proj.factory.block(proj.entry) # 打印入口点的块信息
<Block for 0x401670, 42 bytes>

>>> block.pp()           # 当前块的反汇编
0x401670:       xor     ebp, ebp
0x401672:       mov     r9, rdx
0x401675:       pop     rsi
0x401676:       mov     rdx, rsp
0x401679:       and     rsp, 0xfffffffffffffff0
0x40167d:       push    rax
0x40167e:       push    rsp
0x40167f:       lea     r8, [rip + 0x2e2a]
0x401686:       lea     rcx, [rip + 0x2db3]
0x40168d:       lea     rdi, [rip - 0xd4]
0x401694:       call    qword ptr [rip + 0x205866]

>>> block.instructions # 指令数量
0xb
>>> block.instruction_addrs     # 指令地址列举
[0x401670, 0x401672, 0x401675, 0x401676, 0x401679, 0x40167d, 0x40167e, 0x40167f, 0x401686, 0x40168d, 0x401694]

>>> block.capstone        # 块的顶点地址
<CapstoneBlock for 0x401670>
>>> block.vex         # 块的 IR 地址,这是 python 空间的,不是程序空间的地址
<pyvex.block.IRSB at 0x7706330>

状态 (States)

angr 会返回一个模拟的状态值

>>> state = proj.factory.entry_state()
<SimState @ 0x401670>

模拟的状态包含了程序的内存,寄存器,文件系统数据等,所有的『实时数据』都可以被改写。

>>> state.regs.rip        # 获取寄存器的值
<BV64 0x401670>
>>> state.regs.rax
<BV64 0x1c>
>>> state.mem[proj.entry].int.resolved  # 将 proj.entry 内存的值解释为 int
<BV32 0x8949ed31>

这些不是 python 整数,而是 位向量 , 可以理解为返回的是一个封装的对象,这些位向量里都有 .length 属性,指明位向量的宽度。

# BVV => bitvector with value 
>>> bv = state.solver.BVV(0x1234, 32)       # 创建一个32位的变量并赋值为 0x1234
<BV32 0x1234>
>>> state.solver.eval(bv)                # 转换为 python 整数
0x1234

你可以保存这些位向量到寄存器和内存中,或者你可以直接储存一个 python 整数,它会自动转换成位向量适当的大小。

>>> state.regs.rsi = state.solver.BVV(3, 64) # 修改寄存器的值
>>> state.regs.rsi
<BV64 0x3>

>>> state.mem[0x1000].long = 4 # 修改内存的值
>>> state.mem[0x1000].long.resolved
<BV64 0x4>
  • 对内存的修改用 BVV 或者直接 python 整数都是可以的
  • .resolved 是取 mem 的 BVV 值
  • .concrete 取的是 python 整数

模拟管理器 (Simulation Managers)

模拟管理器是模拟执行的关键。

# 创建模拟器
>>> simgr = proj.factory.simulation_manager(state)
<SimulationManager with 1 active>
# 列出所有的 active
>>> simgr.active
[<SimState @ 0x401670>]

要访问指定的 active 可以用 simgr.active[0]

>>> simgr.step() # 单步执行

我们可以再次查看活动存储,注意到它已经更新,并且还没有修改我们的原始状态。SimState对象通过执行被视为不可变 – 您可以安全地将单个状态用作多次执行的“基础”。

>>> simgr.active
[<SimState @ 0x1020300>]
>>> simgr.active[0].regs.rip                 # 新值
<BV64 0x1020300>
>>> state.regs.rip                           # 原始值
<BV64 0x401670>

分析 (Analyses)

分析的相关类都在 proj.analyses.* 中。

# 创建 control-flow graph 控制流程图对象
>>> proj = angr.Project('/bin/true', auto_load_libs=False)
>>> cfg = proj.analyses.CFGFast()
<CFGFast Analysis Result at 0x2d85130>

>>> cfg.graph
<networkx.classes.digraph.DiGraph at 0x2da43a0>
>>> len(cfg.graph.nodes())
951

# 获取给定地址的 CFGNode 使用 get_any_node
>>> entry_node = cfg.get_any_node(proj.entry)
>>> len(list(cfg.graph.successors(entry_node)))
2

更多的使用方法请参看 API 文档

发表评论