动若脱兔(二):深入浅出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用于读取数据.。

扫描二维码关注公众号,回复: 2166221 查看本文章

二: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之前,首先

猜你喜欢

转载自www.cnblogs.com/0xJDchen/p/9314613.html