动若脱兔(二):深入浅出angr--angr小实践和过程探究
Posted 0xjdchen
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了动若脱兔(二):深入浅出angr--angr小实践和过程探究相关的知识,希望对你有一定的参考价值。
一:angr的输入输出
一般来说,命令行程序主要有两种数据输入的方式,第一种是利用api(get,read),第二种是放在argc上,其它的方法有很多,最后也会提供一种通用的解法。
当数据输入在argc上时,一般使用claripy库,将输入的数据符号化,具体代码如下:
import angr import claripy p = angr.Project("test") args = claripy.BVS(‘args‘, 8*16) initial_state = prog.factory.entry_state(args=["./vul", args])
for i in range(0,8):
initial_state.add_constraints(argc.get_byte(0) >= argvc.get_byte(1)) pg = p.factory.path_group(initial_state) pg.explore(find=(0x4005d1,)) print pg # <PathGroup with 18 deadended, 4 active, 1 found> print pg.found[0] # <Path with 64 runs (at 0x4005d1)> print pg.found[0].state.posix.dumps(0)
claripy库是求解器引擎,绝大部分只是用来做z3的前端,而在这里起到的作用主要是将参数符号化,核心代码为第四行。
当利用api时,主要通过对st.posix.files[0]进行符号化,具体代码如下:
p = angr.Project(‘wyvern‘) st = p.factory.full_init_state(args=[‘./wyvern‘], add_options=angr.options.unicorn) for _ in xrange(28): k = st.posix.files[0].read_from(1) st.solver.add(k != 0) st.solver.add(k != 10) k = st.posix.files[0].read_from(1) st.solver.add(k == 10) st.posix.files[0].seek(0) st.posix.files[0].length = 29
state.pix在angr中是 angr.state_plugins.posix.SimSystemPosix类,该类的主要作用是用于模拟符合posix环境的数据存储和输入输出。其中files[0]代表着数据的输入,read_from表示读取输入的数据。第3到第8行对输入的数据进行限制,
最后两行将指针重新指向开头并设置长度。
第三种是最通用的方式,直接访问并修改内存,无论程序是通过何种方式进行输入,输入的数据总是在内存中,可以通过对内存进行符号化。具体代码示例如下:
import angr p = angr.Project(‘./vul‘) s = p.factory.blank_state(addr=0x80485c8) bvs = s.se.BVS(‘to_memory‘, 8*4) s.se.add(bvs > 1000) s.memory.store(0x08049b80, bvs, endness=‘Iend_LE‘) pg = p.factory.path_group(s, immutable=False)
其中 endness有三个值,分别为
Variables: LE – little endian, least significant byte is stored at lowest address BE – big endian, most significant byte is stored at lowest address ME – Middle-endian. Yep.
关于内存操作还可以多说一下,s.memory.store可以用于存储数据,s.memory.load用于读取数据.。
二:angr解题步骤:
这里本篇主要利用simulation_manager(老版本为factory_group)求解
#!/usr/bin/env python # -*- coding: utf-8 -*- import angr import claripy angr.l.setLevel(‘DEBUG‘) p = angr.Project(‘./vul‘, load_options={"auto_load_libs": False}) args = claripy.BVS(‘args‘, 8*100) initial_state = p.factory.entry_state(args=[p.filename, args], add_options={‘BYPASS_UNSUPPORTED_SYSCALL‘}) #pg = p.factory.path_group(initial_state, immutable=False),在新版本被代替,和simlation_manager等效 pg = p.factory.simulation_manager(initial_state) find_addrs = (0x400546, ) avoid_addrs = () pg.explore(find=find_addrs, avoid=avoid_addrs) print pg print ans = pg.found[0].state.se._solver.result.model
print pg.found[0].state.posix.dumps(0) //代表该状态程序的所有输入
print pg.found[0].state.posix.dumps(1) //代表该状态程序的所有输出
simualtion_manager初始化运行之后,一般有以下几种状态
step()表示向下执行一个block(42bytes),step()函数产生active状态,表示该分支在执行中;
run()表示运行到结束,run()函数产生deadended状态,表示分支结束;
explore()可以对地址进行限制以减少符号执行遍历的路径。例如 sm.explore(find=0x400676,avoid=[0x40073d]) explore()产生found状态,表示探索的结果等等。
而其中explore中的参数不仅可以是地址,也可以是函数(lambda表达式),例如:
pg.explore(find=lambda s: "Congrats" in s.posix.dumps(1))
三:小谈simulation_manager的路径探究过程
simulation_manager式angr最重要的控制接口,模拟管理器的最基本功能是利用step()通过一个基本块将给定存储中的所有状态向前推进。当然,如果不对路径探究过程进行细致研究,只需要使用run()和explore()就好了,在简述run和explore之前,首先
以上是关于动若脱兔(二):深入浅出angr--angr小实践和过程探究的主要内容,如果未能解决你的问题,请参考以下文章
深入理解二叉索引树:从LeetCode实践出发(题号307315327493)