CTF-All-In-One/doc/5.3.1_angr.md
firmianay 070603e235 fix
2018-05-29 21:51:00 +08:00

304 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 5.3.1 angr
- [简介](#简介)
- [安装](#安装)
- [使用方法](#使用方法)
- [快速入门](#快速入门)
- [二进制文件加载器](#二进制文件加载器)
- [求解器引擎](#求解器引擎)
- [VEX IR 翻译器](#vex-ir-翻译)
- [体系结构信息收集](#体系结构信息收集)
- [CTF 实例](#ctf-实例)
- [参考资料](#参考资料)
## 简介
[angr](https://github.com/angr/angr) 是一个多架构的二进制分析平台,具备对二进制文件的动态符号执行能力和多种静态分析能力。在近几年的 CTF 中也大有用途。
## 安装
在 Ubuntu 上,首先我们应该安装所有的编译所需要的依赖环境:
```shell
$ sudo apt install python-dev libffi-dev build-essential virtualenvwrapper
```
强烈建议在虚拟环境中安装 angr因为有几个 angr 的依赖比如z3是从他们的原始库中 fork 而来,如果你已经安装了 z3,那么你肯定不希望 angr 的依赖覆盖掉官方的共享库。
对于大多数 *nix系统只需要 `mkvirtualenv angr && pip install angr` 安装就好了。
如果这样安装失败的话,那么你可以按照下面的顺序从 angr 的官方仓库安装:
```text
1. claripy
2. archinfo
3. pyvex
4. cle
5. angr
```
例如下面这样:
```shell
$ git clone https://github.com/angr/claripy
$ cd claripy
$ sudo pip install -r requirements.txt
$ sudo python setup.py build
$ sudo python setup.py install
```
安装过程中可能会有一些奇怪的错误,可以到官方文档中查看。
## 使用方法
#### 快速入门
使用 angr 的第一步是新建一个工程,几乎所有的操作都是围绕这个工程展开的:
```python
>>> import angr
>>> proj = angr.Project('/bin/true')
WARNING | 2017-12-08 10:46:58,836 | cle.loader | The main binary is a position-independent executable. It is being loaded with a base address of 0x400000.
```
这样就得到了二进制文件的各种信息,如:
```python
>>> proj.filename
'/bin/true'
>>> proj.arch
<Arch AMD64 (LE)>
>>> hex(proj.entry)
'0x4013b0'
```
程序加载时会将二进制文件和共享库映射到虚拟地址中CLE 模块就是用来处理这些东西的。
```python
>>> proj.loader
<Loaded true, maps [0x400000:0x5008000]>
```
所有对象文件如下,其中二进制文件是 main object
```
>>> proj.loader.all_objects
[<ELF Object true, maps [0x400000:0x60721f]>, <ELF Object libc-2.26.so, maps [0x1000000:0x13b78cf]>, <ELF Object ld-2.26.so, maps [0x2000000:0x22260f7]>, <ELFTLSObject Object cle##tls, maps [0x3000000:0x300d010]>, <ExternObject Object cle##externs, maps [0x4000000:0x4008000]>, <KernelObject Object cle##kernel, maps [0x5000000:0x5008000]>]
>>> proj.loader.main_object
<ELF Object true, maps [0x400000:0x60721f]>
>>> proj.loader.main_object.pic
True
```
通常我们在创建工程时选择关闭 `auto_load_libs` 以避免 angr 加载共享库:
```
>>> p = angr.Project('/bin/true', auto_load_libs=False)
WARNING | 2017-12-08 11:09:28,629 | cle.loader | The main binary is a position-independent executable. It is being loaded with a base address of 0x400000.
>>> p.loader.all_objects
[<ELF Object true, maps [0x400000:0x60721f]>, <ExternObject Object cle##externs, maps [0x1000000:0x1008000]>, <KernelObject Object cle##kernel, maps [0x2000000:0x2008000]>, <ELFTLSObject Object cle##tls, maps [0x3000000:0x300d010]>]
```
`project.factory` 提供了很多类对二进制文件进行分析,它提供了几个方便的构造函数。
`project.factory.block()` 用于从给定地址解析一个 basic block
```python
>>> block = proj.factory.block(proj.entry) # 从程序头开始解析一个 basic block
>>> block
<Block for 0x4013b0, 42 bytes>
>>> block.pp() # pretty-print即打印出反汇编代码
0x4013b0: xor ebp, ebp
0x4013b2: mov r9, rdx
0x4013b5: pop rsi
0x4013b6: mov rdx, rsp
0x4013b9: and rsp, 0xfffffffffffffff0
0x4013bd: push rax
0x4013be: push rsp
0x4013bf: lea r8, qword ptr [rip + 0x32ca]
0x4013c6: lea rcx, qword ptr [rip + 0x3253]
0x4013cd: lea rdi, qword ptr [rip - 0xe4]
0x4013d4: call qword ptr [rip + 0x205b26]
>>> block.instructions # 指令数量
11
>>> block.instruction_addrs # 指令地址
[4199344L, 4199346L, 4199349L, 4199350L, 4199353L, 4199357L, 4199358L, 4199359L, 4199366L, 4199373L, 4199380L]
```
另外,还可以将 block 对象转换成其他形式:
```python
>>> block.capstone
<CapstoneBlock for 0x4013b0>
>>> block.capstone.pp()
>>> block.vex
<pyvex.block.IRSB object at 0x7fe526b98670>
>>> block.vex.pp()
```
程序的执行需要初始化一个 `SimState` 对象:
```python
>>> state = proj.factory.entry_state()
>>> state
<SimState @ 0x4013b0>
```
该对象包含了程序的内存、寄存器、文件系统数据等:
```python
>>> state.regs.rip
<BV64 0x4013b0>
>>> state.regs.rsp
<BV64 0x7fffffffffeff98>
>>> state.regs.rdi
<BV64 reg_48_0_64{UNINITIALIZED}> # 符号变量,它是符号执行的基础
>>> state.mem[proj.entry].int.resolved
<BV32 0x8949ed31>
```
这里的 BV即 bitvectors用于表示 angr 里的 CPU 数据。下面是 python int 和 bitvectors 之间的转换:
```python
>>> bv = state.solver.BVV(0x1234, 32)
>>> bv
<BV32 0x1234>
>>> hex(state.solver.eval(bv))
'0x1234'
>>> bv = state.solver.BVV(0x1234, 64)
>>> bv
<BV64 0x1234>
>>> hex(state.solver.eval(bv))
'0x1234L'
```
使用 bitvectors 来设置寄存器和内存的值,当直接传入 python int 时angr 会自动将其转换成 bitvectors
```python
>>> state.regs.rsi = state.solver.BVV(3, 64)
>>> state.regs.rsi
<BV64 0x3>
>>> state.mem[0x1000].long = 4
>>> state.mem[0x1000].long.resolved # .resolved 获取 bitvectors
<BV64 0x4>
>>> state.mem[0x1000].long.concrete # .concrete 获得 python int
4L
```
初始化的 state 可以经过模拟执行得到一系列的 statessimulation 管理器的作用就是对这些 states 进行管理:
```python
>>> simgr = proj.factory.simulation_manager(state)
>>> simgr
<SimulationManager with 1 active>
>>> simgr.active
[<SimState @ 0x4013b0>]
>>> simgr.step() # 模拟一个 basic block 的执行
<SimulationManager with 1 active>
>>> simgr.active # 模拟状态被更新
[<SimState @ 0x1020e80>]
>>> simgr.active[0].regs.rip # active[0] 是当前 state
<BV64 0x404620>
>>> state.regs.rip # 但原始的 state 没有变
<BV64 0x4013b0>
```
`project.analyses` 提供了大量函数用于程序分析。
```python
>>> cfg = p.analyses.CFGFast() # 得到 control-flow graph
>>> cfg
<CFGFast Analysis Result at 0x7f4626f15090>
>>> cfg.graph
<networkx.classes.digraph.DiGraph object at 0x7f462316ef90> # 详细内容请查看 networkx
>>> len(cfg.graph.nodes())
937
>>> entry_node = cfg.get_any_node(proj.entry) # 得到给定地址的节点
>>> entry_node
<CFGNode 0x4013b0[42]>
>>> len(list(cfg.graph.successors(entry_node)))
2
```
如果要想画出图来,还需要安装 matplotlibTkinter 等。
```python
>>> import networkx as nx
>>> import matplotlib.pyplot as plt
>>> nx.draw(cfg.graph) # 画图
>>> plt.show() # 显示
>>> plt.savefig('temp.png') # 保存
```
#### 加载二进制文件
我们知道 angr 是高度模块化的,接下来我们就分别来看看这些组成模块,其中用于二进制加载模块称为 CLE。主类为 `cle.loader.Loader`,它导入所有的对象文件并导出一个进程内存的抽象。类 `cle.backends` 是加载器的后端,根据二进制文件类型区分为 `cle.backends.elf`、`cle.backends.pe`、`cle.backends.macho` 等。
加载对象文件和细分类型如下:
```python
>>> proj.loader.all_objects # 所有对象文件
[<ELF Object true, maps [0x400000:0x60721f]>, <ELF Object libc-2.26.so, maps [0x1000000:0x13b78cf]>, <ELF Object ld-2.26.so, maps [0x2000000:0x22260f7]>, <ELFTLSObject Object cle##tls, maps [0x3000000:0x300d010]>, <ExternObject Object cle##externs, maps [0x4000000:0x4008000]>, <KernelObject Object cle##kernel, maps [0x5000000:0x5008000]>]
```
- `proj.loader.main_object`:主对象文件
- `proj.loader.shared_objects`:共享对象文件
- `proj.loader.extern_object`:外部对象文件
- `proj.loader.all_elf_object`:所有 elf 对象文件
- `proj.loader.kernel_object`:内核对象文件
通过对这些对象文件进行操作,可以解析出相关信息:
```python
>>> obj = proj.loader.main_object
>>> hex(obj.entry) # 入口地址
'0x4013b0'
>>> hex(obj.min_addr), hex(obj.max_addr) # 起始地址和结束地址
('0x400000', '0x60721f')
>>> obj.segments # segments
<Regions: [<ELFSegment offset=0x0, flags=0x5, filesize=0x6094, vaddr=0x400000, memsize=0x6094>, <ELFSegment offset=0x6c10, flags=0x6, filesize=0x470, vaddr=0x606c10, memsize=0x610>]>
>>> obj.sections # sections
<Regions: [<Unnamed | offset 0x0, vaddr 0x400000, size 0x0>, <.interp | offset 0x238, vaddr 0x400238, size 0x1c>, <.note.ABI-tag | offset 0x254, vaddr 0x400254, size 0x20>,...etc
```
根据需要解析我们需要的信息:
```python
>>> obj.find_segment_containing(obj.entry) # 包含给定地址的 segments
<ELFSegment offset=0x0, flags=0x5, filesize=0x6094, vaddr=0x400000, memsize=0x6094>
>>> obj.find_section_containing(obj.entry) # 包含给定地址的 sections
<.text | offset 0x12f0, vaddr 0x4012f0, size 0x33c9>
```
#### 求解器引擎
#### VEX IR 翻译器
angr 使用了 VEX 作为二进制分析的中间表示。VEX IR 是由 Valgrind 项目开发和使用的中间表示,后来这一部分被分离出去作为 libVEXlibVEX 用于将机器码转换成 VEX IR更多内容参考章节5.2.3)。在 angr 项目中,开发了模块 [PyVEX](https://github.com/angr/pyvex) 作为 libVEX 的 Python 包装。当然也对 libVEX 做了一些修改,使其更加适用于程序分析。
一些用法如下:
```python
>>> import pyvex, archinfo
>>> bb = pyvex.IRSB('\xc3', 0x400400, archinfo.ArchAMD64()) # 将一个位于 0x400400 的 AMD64 基本块(\xc3即ret转成 VEX
>>> bb.pp() # 打印
IRSB {
t0:Ity_I64 t1:Ity_I64 t2:Ity_I64 t3:Ity_I64
00 | ------ IMark(0x400400, 1, 0) ------
01 | t0 = GET:I64(rsp)
02 | t1 = LDle:I64(t0)
03 | t2 = Add64(t0,0x0000000000000008)
04 | PUT(rsp) = t2
05 | t3 = Sub64(t2,0x0000000000000080)
06 | ====== AbiHint(0xt3, 128, t1) ======
NEXT: PUT(rip) = t1; Ijk_Ret
}
>>> bb.statements[3] # 表达式
<pyvex.stmt.WrTmp object at 0x7f38f1ef84b0>
>>> bb.statements[3].pp()
t2 = Add64(t0,0x0000000000000008)
>>> bb.statements[3].data # 数据
<pyvex.expr.Binop object at 0x7f38f1ef8460>
>>> bb.statements[3].data.pp()
Add64(t0,0x0000000000000008)
>>> bb.statements[3].data.op # 操作符
'Iop_Add64'
>>> bb.statements[3].data.args # 参数
[<pyvex.expr.RdTmp object at 0x7f38f1f77cb0>, <pyvex.expr.Const object at 0x7f38f1f77098>]
>>> bb.statements[3].data.args[0]
<pyvex.expr.RdTmp object at 0x7f38f1f77cb0>
>>> bb.statements[3].data.args[0].pp()
t0
>>> bb.next # 基本块末尾无条件跳转的目标
<pyvex.expr.RdTmp object at 0x7f38f3cb6f38>
>>> bb.next.pp()
t1
>>> bb.jumpkind # 无条件跳转的类型
'Ijk_Ret'
```
#### 体系结构信息收集
## CTF 实例
查看章节 6.2.8。
## 参考资料
- [angr.io](http://angr.io/)
- [docs.angr.io](https://docs.angr.io/)
- [angr API documentation](http://angr.io/api-doc/)
- [The Art of War:Offensive Techniques in Binary Analysis](https://www.cs.ucsb.edu/~vigna/publications/2016_SP_angrSoK.pdf)