基本用法·
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| import angr
def Go(): def is_good(state): output = state.posix.dumps(1) if b'Right' in output: return True else: return False def is_bad(state): output = state.posix.dumps(1) if b'Fail' in output: return True else: return False path = './file_path' p = angr.Project(path, auto_load_libs=False) initial_state = p.factory.entry_state() sm = p.factory.simgr(initial_state, veritesting=True) sm.explore(find=is_good, avoid=is_bad) if sm.found: for i in sm.found: solved_state = i solution = solved_state.posix.dumps(0) print(f'[+]Success! Solution is {solution.decode("utf-8", "replace")}') else: print('[-]Failed! No solution found') if __name__ == '__main__': Go()
|
进阶用法·
符号化·
创建向量·
对于程序中未知的量,我们可以通过符号化变量求解,可以把他理解为设未知数,可以符号化寄存器、栈、内存、动态内存、文件
首先是创建符号化向量的用法
1 2
| x1 = claripy.BVS('x1', x1_size_in_bits)
|
寄存器·
对于符号化寄存器,可以在创建initial_state
后,使用以下代码
1 2
| initial_state.regs.eax = x1
|
其中除了BVS,还有BVV。BVV(value, size)
即BitVector Value,表示有确定值的位向量,而BVS(name, size)
则是BitVector Symbol,开始时值未知
栈空间的处理,我们首先需要恢复ESP和EBP的状态,以便后续计算
1
| initial_state.regs.ebp = initial_state.regs.esp
|
接着就是推入符号化向量至栈帧中的指定位置,但是我们需要的位置不一定是栈顶,所以我们需要进行填充,比如从ida看见一个变量int i; // [esp+8h] [ebp-10h]
位于ebp-10h
处,为了将符号化向量推入此处,我们就需要padding来填充
L_addr |
|
rbp-30h |
i |
rbp-29h |
i |
rbp-28h |
i |
rbp-27h |
i |
… |
… |
rbp-1h |
padding |
rbp-0h |
padding |
所以我们需要填充后再符号化
1 2
| initial_state.regs.esp -= 0x2C initial_state.stack_push(x1)
|
其实就是将内存地址符号化
1 2
| x1_addr = 0x114514 initial_state.memory.store(x1_addr, x1)
|
动态内存·
动态内存就是用malloc分配的,其实和内存的区别就在于中间多了一步将虚假的堆地址存入malloc()函数返回的变量的步骤
1
| buffer0 -> fake_heap_addr0 -> symbolic_BVS0
|
所以我们的可以这样符号化
1 2 3 4 5 6 7
| initial_state.memory.store(buffer0, fake_heap_addr0, endness=project.arch.memory_endness)
initial_state.memory.store(fake_heap_addr0, symbolic_BVS0, endness=project.arch.memory_endness)
|
1 2 3 4 5 6 7 8 9
| filename = 'OJKSQYDP.txt' symbolic_file_size_bytes = 64 passwd0 = claripy.BVS('password', symbolic_file_size_bytes * 8) passwd_file = angr.storage.SimFile(filename, content=passwd0, size=symbolic_file_size_bytes)
initial_state.fs.insert(filename, passwd_file)
simulation = project.factory.simgr(initial_state)
|
创建了向量后当然需要求解,使用eval
方法
1 2
| solved_x1 = solved_state.solver.eval(x1, cast_to=bytes) print(f'[+] Success! Solution is: {solved_x1.decode('utf-8')}')
|
约束求解·
当angr遇到循环加上分支路线路线时,就容易发生路径爆炸,即使是非常简单的功能也可能无法求解,这时候可以我们自己化简逻辑
这方面angr和z3差不多,同样是.add()
添加约束,接着用.eval()
求解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| >>> state = proj.factory.entry_state() >>> state.solver.add(x - y >= 4) >>> state.solver.add(y > 0) >>> state.solver.eval(x) 5 >>> state.solver.eval(y) 1 >>> state.solver.eval(x + y) 6
|
所以我们的思路大概如下:
-
设置符号化向量
-
找到我们需要简化的地址
-
写出我们的约束条件
-
根据条件求解
这里主要记新的东西
1 2 3
| cpr_addr = 0x114514 sm.explore(find=cpr_addr)
|
接着添加约束条件并求解
1 2 3 4 5 6 7 8 9
| symbolic_bitvector0 = solved_state.memory.load(x0_BVS_addr, BVS_size_in_bytes)
desired_value = 'XXXXXXXXXX'
solved_state.solver.add(symbolic_bitvector0 == desired_value) solution0 = solved_state.solver.eval(x0, cast_to=bytes)
|
Hook函数·
Hook地址·
和约束求解类似,我们的思路依然是用自己的函数逻辑替换原函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @p.hook(compare_addr, length=len_to_skip) def skip_check_equals(state): input_addr = 0x114514 input_len = 16 input_string = state.memory.load(input_addr, input_len)
desired_value = 'XXXXXXXXXXXXXX'
register_size_in_bit = 32 state.regs.eax = claripy.If( input_string == desired_value, claripy.BVV(1, register_size_in_bit) claripy.BVV(0, register_size_in_bit) )
|
Hook函数名·
假如一个函数被反复调用,我们又需要hook时,显然hook函数名是更好的选择,使用方法如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| class ReplaceCheckEquals(angr.SimProcedure): def run(self, to_check, length): input_addr = to_check input_len = length input_string = state.memory.load(input_addr, input_len)
desired_value = 'XXXXXXXXXXXXXX'
register_size_in_bit = 32 state.regs.eax = claripy.If( input_string == desired_value, claripy.BVV(1, register_size_in_bit) claripy.BVV(0, register_size_in_bit) )
check_equals_symbol = 'check_equals_XXXXXXXXXXXX' p.hook_symbol(check_equals_symbol, ReplaceCheckEquals())
|