angr

边学angr,边记笔记。这也是临时想着干脆写个笔记再写个博客,有空一边把前面的补上,一边学后面的

目前是照着这位大师傅的文章学的angr:https://xz.aliyun.com/u/11261

常规操作

基本操作

1
2
3
4
5
6
7
8
9
10
11
12
p = angr.Project('filename',auto_load_libs=True)#or False

state = p.factory.full_init_state()
#entry_state()
#call_state(addr=)
#blank_state(addr=)

simgr = p.factory.simulation_manager(state)

simgr.explore(find=,avoid=)

simgr.found[0].posix.dumps(0)

寄存器,内存地址操作

1
2
3
4
5
6
>>> state.regs.rip        # get the current instruction pointer
<BV64 0x401670>
>>> state.regs.rax
<BV64 0x1c>
>>> state.mem[proj.entry].int.resolved # interpret the memory at the entry point as a C int
<BV32 0x8949ed31>

bitvector

1
2
3
4
>>> bv = state.solver.BVV(0x1234, 32)       # create a 32-bit-wide bitvector with value 0x1234
<BV32 0x1234>
>>> state.solver.eval(bv) # convert to python int
0x1234

explore的小操作

一般来说 explore必指定find,可选avoid

通常来说这里都是设定为地址,即sm.explore(find=FIND_ADDR,avoid=AVOID_ADDR)

这里可以有另外一种操作方式,即我不需要知道具体该选择什么地址,我可以以回显作为条件进行find和avoid的设定,例:(example/sym-write)

1
2
3
4
5
6
7
8
9
10
def correct(state):
try:
return b'win' in state.posix.dumps(1)
except:
return False
def wrong(state):
try:
return b'lose' in state.posix.dumps(1)
except:
return False

对应的explore写为sm.explore(find=correct, avoid=wrong)

Hook

这东西有点难,先搁着,国赛出题和毕设要紧

跨平台

dll

example/mma_howtouse

1
2
p = angr.Project('howtouse.dll', load_options={'main_opts': {'base_addr': 0x10000000}})
howtouse = p.factory.callable(0x10001130)

对于具体的值,不需要用solver.eval求值

1
claripy.backends.concrete.convert(howtouse(i)).value

输入

命令行输入

1
state = p.factory.entry_state(args={"elf",argv},add_options={angr.options.LAZY_SOLVES})

程序内交互输入

1
state = p.factory.blank_state(addr=START, stdin=flag)

参数传递

有时候可以选择绕过程序的输入函数,直接把参数传进内存和寄存器里

flareon2015_2

因为是windows下的PE文件,为了避免调用windows API,所以需要从0x401084这个函数地址开始分析。此时需要人工设置起始状态,即传参。

1
2
3
s.mem[s.regs.esp+8:].dword = 0x402159
s.mem[s.regs.esp+4:].dword = 0x4010e4
s.mem[s.regs.esp:].dword = 0x401064

这个可以通过动态调试查看栈情况得知

1556771212213
1556771212213

claripy.BVS

angr中可以使用claripy.BVS作为程序的输入。

以输入20位字符串flag为例。

1
2
3
4
# type 1
flag = claripy.BVS('flag',20*8)#BVS以bit位单位,因此这里需要length*8bit
# type 2
flag = [claripy.BVS('flag_i',8) for i in range(20)]

比较以上两种方式,type1将flag作为一个整体,而type2将flag以字符位单位进行分割,其实没太大差别,但是在后续添加约束等操作上稍有不同

写入内存

在设置state时需要申明add_options={"SYMBOLIC_WRITE_ADDRESSES"}

1
2
3
4
5
# type 1
state.memory.store(addr,flag,endness="Iend_BE")#大端模式
# type 2
for i in range(20):
state.memory.store(addr+i,flag[i],endness="Iend_BE")

添加约束

1
2
3
4
5
6
# type 1
for flag_chr in flag.chop(8):# 将flag以8bit分割
state.add_constraints(state.solver.And(flag_chr >=0x20,flag_chr <=0x7f))# 限制flag_chr为可打印字符
# type 2
for flag_chr in flag:# 因为flag本身就是以char为单位的一个list,所以就不需要chop()
state.add_constraints(state.solver.And(flag_chr >=0x20,flag_chr <=0x7f))

小结

说到底区别就在于一个是整体,一个是分割的小块。而有的时候整体也需要

state.options

根据angr的官方文档/doc/states.md,state既可以add_options也可以remove_options

1
2
3
4
5
6
# 添加一个lazy solves的选项
s.options.add(angr.options.LAZY_SOLVES)
# Create a new state with lazy solves enabled
s = proj.factory.entry_state(add_options={angr.options.LAZY_SOLVES})
# Create a new state without simplification options enabled
s = proj.factory.entry_state(remove_options=angr.options.simplification)

/doc/options里列出了所有的options,太多了,不搬运过来了,边学边看呗~

TODO

有必要把stateadd_options=参数研究一下