学习Angr的进一步用法
Block:块
将二进制文件用Project进行装载后,Project对象可以通过Block来查看文件的代码块
>>> p.factory.block(p.entry).pp
<bound method Block.pp of <Block for 0x400610, 41 bytes>>//Block的起始位置,以及有多少个字节
>>> a = p.factory.block(p.entry)
>>> a.pp()
0x400610: xor ebp, ebp
0x400612: mov r9, rdx
0x400615: pop rsi
0x400616: mov rdx, rsp
0x400619: and rsp, 0xfffffffffffffff0
0x40061d: push rax
0x40061e: push rsp
0x40061f: mov r8, 0x400900
0x400626: mov rcx, 0x400890
0x40062d: mov rdi, 0x4007e8
0x400634: call 0x4005d0//在遇见跳转指令前停下,然后上面就是一个代码块
>>> a.instructions
11//有多少条指令
>>> a.instruction_addrs
[4195856L, 4195858L, 4195861L, 4195862L, 4195865L, 4195869L, 4195870L, 4195871L, 4195878L, 4195885L, 4195892L]//每条指令的起始地址,可以根据这个算出长度
>>> for i in range(12):
... a = p.factory.block(p.entry + i)
... a.pp()
... print('-------')
...
0x400610: xor ebp, ebp
0x400612: mov r9, rdx
0x400615: pop rsi
0x400616: mov rdx, rsp
0x400619: and rsp, 0xfffffffffffffff0
0x40061d: push rax
0x40061e: push rsp
0x40061f: mov r8, 0x400900
0x400626: mov rcx, 0x400890
0x40062d: mov rdi, 0x4007e8
0x400634: call 0x4005d0
-------
0x400611: in eax, dx//如果不确定好代码入口地址就会出现指令错误
0x400612: mov r9, rdx
0x400615: pop rsi
0x400616: mov rdx, rsp
0x400619: and rsp, 0xfffffffffffffff0
0x40061d: push rax
0x40061e: push rsp
0x40061f: mov r8, 0x400900
0x400626: mov rcx, 0x400890
0x40062d: mov rdi, 0x4007e8
0x400634: call 0x4005d0
-------
0x400612: mov r9, rdx
0x400615: pop rsi
0x400616: mov rdx, rsp
0x400619: and rsp, 0xfffffffffffffff0
0x40061d: push rax
0x40061e: push rsp
0x40061f: mov r8, 0x400900
0x400626: mov rcx, 0x400890
0x40062d: mov rdi, 0x4007e8
0x400634: call 0x4005d0
-------
>>> a.vex
IRSB <0x1e bytes, 7 ins., <Arch AMD64 (LE)>> at 0x40061b//也能把汇编代码转换成为VEX IR
>>> print(a.vex)
IRSB {
t0:Ity_I32 t1:Ity_I32 t2:Ity_I32 t3:Ity_I64 t4:Ity_I64 t5:Ity_I64 t6:Ity_I64 t7:Ity_I64 t8:Ity_I64 t9:Ity_I64 t10:Ity_I64 t11:Ity_I64 t12:Ity_I64 t13:Ity_I64 t14:Ity_I64 t15:Ity_I32 t16:Ity_I64 t17:Ity_I32 t18:Ity_I64 t19:Ity_I64 t20:Ity_I64 t21:Ity_I64 t22:Ity_I64 t23:Ity_I64 t24:Ity_I64 t25:Ity_I64 t26:Ity_I64 t27:Ity_I64 t28:Ity_I64 t29:Ity_I64 t30:Ity_I64 t31:Ity_I64
00 | ------ IMark(0x400610, 2, 0) ------//这个应该是对应的汇编代码的地址
01 | PUT(rbp) = 0x0000000000000000//一一对比发现还是很好懂的
02 | ------ IMark(0x400612, 3, 0) ------
03 | t21 = GET:I64(rdx)
04 | PUT(r9) = t21
05 | PUT(pc) = 0x0000000000400615
06 | ------ IMark(0x400615, 1, 0) ------
07 | t4 = GET:I64(rsp)
08 | t3 = LDle:I64(t4)
09 | t22 = Add64(t4,0x0000000000000008)
10 | PUT(rsi) = t3
11 | ------ IMark(0x400616, 3, 0) ------
12 | PUT(rdx) = t22
13 | ------ IMark(0x400619, 4, 0) ------
14 | t5 = And64(t22,0xfffffffffffffff0)
15 | PUT(cc_op) = 0x0000000000000014
16 | PUT(cc_dep1) = t5
17 | PUT(cc_dep2) = 0x0000000000000000
18 | PUT(pc) = 0x000000000040061d
19 | ------ IMark(0x40061d, 1, 0) ------
20 | t8 = GET:I64(rax)
21 | t24 = Sub64(t5,0x0000000000000008)
22 | PUT(rsp) = t24
23 | STle(t24) = t8
24 | PUT(pc) = 0x000000000040061e
25 | ------ IMark(0x40061e, 1, 0) ------
26 | t26 = Sub64(t24,0x0000000000000008)
27 | PUT(rsp) = t26
28 | STle(t26) = t24
29 | ------ IMark(0x40061f, 7, 0) ------
30 | PUT(r8) = 0x0000000000400900
31 | ------ IMark(0x400626, 7, 0) ------
32 | PUT(rcx) = 0x0000000000400890
33 | ------ IMark(0x40062d, 7, 0) ------
34 | PUT(rdi) = 0x00000000004007e8
35 | PUT(pc) = 0x0000000000400634
36 | ------ IMark(0x400634, 5, 0) ------
37 | t28 = Sub64(t26,0x0000000000000008)
38 | PUT(rsp) = t28
39 | STle(t28) = 0x0000000000400639
40 | t30 = Sub64(t28,0x0000000000000080)
41 | ====== AbiHint(0xt30, 128, 0x00000000004005d0) ======
NEXT: PUT(rip) = 0x00000000004005d0; Ijk_Call
}
>>> a = p.factory.block(p.entry)
>>> print(a.capstone)
0x400610: xor ebp, ebp//同样也能调用capstone反汇编引擎,十分方便
0x400612: mov r9, rdx
0x400615: pop rsi
0x400616: mov rdx, rsp
0x400619: and rsp, 0xfffffffffffffff0
0x40061d: push rax
0x40061e: push rsp
0x40061f: mov r8, 0x400900
0x400626: mov rcx, 0x400890
0x40062d: mov rdi, 0x4007e8
0x400634: call 0x4005d0
State:状态
project对象只表示一个程序的“初始镜像”,当你用angr执行程序的时候,你实际操作的是一个代表程序的模拟状态(simulated program state)的特定对象——一个Simstate。
>>> a = p.factory.entry_state()
>>> a
<SimState @ 0x400610>
>>> state.regs.rip//可以通过regs命令来查看寄存器的值
<BV64 0x400610>
>>> state.regs.rax
<BV64 0x1c>
>>> state.regs.ax
<BV16 0x1c>
>>> state.mem[p.entry].int.resolved//也能通过mem来直接查看内存中的值
<BV32 0x8949ed31>
>>> state.mem[p.entry].byte.resolved
<BV8 49>
然后这里面获得的值都是位向量,如果要转化为int类型还要用到自带的claripy库
>>> state.solver.eval(state.regs.rip)
4195856L
>>> hex(state.solver.eval(state.regs.rip))
'0x400610L'
同样mem接口还支持对文件内存的修改
>>> state.mem[0x5000].short = 0x1234
>>> state.mem[0x5002].int = 0x12345678
>>> state.mem[0x5006].long = 0x1234567887654321
>>> state.mem[0x5000].short.resolved
<BV16 0x1234>
>>> state.mem[0x5002].int.resolved
<BV32 0x12345678>
>>> state.mem[0x5006].long.resolved
<BV64 0x1234567887654321>
使用array[index]符号来指定一个地址。
使用type来指定一个内存应该被作为什么类型的数据处理(常用类型:char,short,long,size_t,uint8_t,uint16_t……)
存储一个值到内存中,可以是一个位向量也可以是一个python整型
使用.resolved以位向量的形式获取内存中的值
使用.concrete以python整型的形式获取内存中的值
Analyses:分析器
分析器就是分析程序执行的流程啥的,比如CFGAccurate算法和CFGFast算法
>>> cfg = p.analyses.CFGFast()
..........
>>> cfg
<CFGFast Analysis Result at 0x7fb8e80bd2d0>
>>> cfg.graph
<networkx.classes.digraph.DiGraph object at 0x7fb8e6402a90>
>>> cfg.graph.nodes()
NodeView((<CFGNode 0x200e15cL[13]>, <CFGNode 0x200b000L[8]>,.........
>>> len(cfg.graph)
13651
能看到程序的节点和长度。想把图打出来的。。但是试了下networkx模块一直报错。。