Quantcast
Channel: CodeSection,代码区,Python开发技术文章_教程 - CodeSec
Viewing all articles
Browse latest Browse all 9596

angr学习笔记

$
0
0
前言

angr是一个基于符号执行和模拟执行的二进制框架,可以用在很多的场景,比如逆向分析,漏洞挖掘等。本文对他的学习做一个总结。

安装

这里介绍ubuntu下的安装,其他平台可以看 官方文档

首先安装一些依赖包

sudoapt-getinstallpython-devlibffi-devbuild-essentialvirtualenvwrapper

然后使用

mkvirtualenvangr&&pipinstallangr

即可安装

建议使用virtualenv来安装,因为angr用到的一些库和正常下的不一样,直接pip安装可能会安装不上去

angr常用对象及简单使用

使用angr的大概步骤

创建project

设置state

新建 符号量 :BVS (bitvector symbolic )或BVV (bitvector value)

把符号量设置到内存或者其他地方

设置Simulation Managers, 进行路径探索的对象

运行,探索满足路径需要的值

约束求解,获取执行结果

Project对象 介绍与简单使用

载入二进制文件使用angr.Project函数,它的第一个参数是待载入文件的路径,后面还有很多的可选参数,具体可以看 官方文档

p=angr.Project('./issue',load_options={"auto_load_libs":False})

auto_load_libs设置是否自动载入依赖的库,如果设置为True的话会自动载入依赖的库,然后分析到库函数调用时也会进入库函数,这样会增加分析的工作量,也有能会跑挂。

载入文件后,就可以通过project对象获取信息以及进行后面的操作

In[11]:proj=angr.Project('/bin/true') In[12]:proj.loader.shared_objects Out[12]:OrderedDict([('true',<ELFObjecttrue,maps[0x400000:0x6063bf]>),(u'libc.so.6',<ELFObjectlibc-2.23.so,maps[0x1000000:0x13c999f]>),(u'ld-linux-x86-64.so.2',<ELFObjectld-2.23.so,maps[0x2000000:0x2227167]>)]) In[13]:proj=angr.Project('/bin/true',load_options={"auto_load_libs":False}) In[14]:proj.loader.shared_objects Out[14]:OrderedDict([('true',<ELFObjecttrue,maps[0x400000:0x6063bf]>)]) In[15]:

可以看到在使用{"auto_load_libs": False}后一些动态链接库没有被载入。

有两个小点还需要了解一下

如果auto_load_libs为true, 那么程序如果调用到库函数的话就会直接调用真正的库函数,如果有的库函数逻辑比较复杂,可能分析程序就出不来了~~。同时angr使用python实现了很多的库函数(保存在angr.SIM_PROCEDURES里面),默认情况下会使用列表内部的函数来替换实际的函数调用,如果不在列表内才会进入到真正的library.

如果auto_load_libs为false, 程序调用函数时,会直接返回一个 不受约束的符号值。

hook

我们可以在angr中使用hook来把指定地址的二进制代码替换为python代码。angr在模拟执行程序时,执行每一条指令前会检测该地址处是否已经被hook,如果是就不执行这条语句,转而执行hook时指定的python处理代码。

下面看实例

目标程序地址

https://github.com/angr/angr-doc/tree/master/examples/sym-write

示例脚本

#!/usr/bin/envpython #coding=utf-8 importangr importclaripy defhook_demo(state): state.regs.eax=0 state.regs.ebx=0xdeadbeef p=angr.Project("./examples/sym-write/issue",load_options={"auto_load_libs":False}) p.hook(addr=0x08048485,hook=hook_demo,length=2) state=p.factory.blank_state(addr=0x0804846B,add_options={"SYMBOLIC_WRITE_ADDRESSES"}) u=claripy.BVS("u",8) state.memory.store(0x0804A021,u) sm=p.factory.simgr(state) sm.explore(find=0x080484DB) st=sm.found[0] printhex(st.se.eval(st.regs.ebx))

介绍一下脚本的流程

首先 使用angr.Project载入文件, 设置auto_load_libs为false则不加载依赖的lib

然后 使用p.hook把0x08048485处的2字节的指令 为hook_demo,之后执行0x08048485就会去执行hook_demo

然后创建一个state,因为要往内存里面设置 符号量 (BVS),设置SYMBOLIC_WRITE_ADDRESSES

然后新建一个8位长度的符号量,并把它存到0x0804A021(全局变量u的位置)


angr学习笔记

然后开始探索路径,最后求解出使得 程序执行到you win代码块的符号量的解。


angr学习笔记

这里主要讲p.hook的处理, 这里使用了hook函数的三个参数

p.hook(addr=0x08048485,hook=hook_demo,length=2)

addr为待hook指令的地址

hook为hook的处理函数,在执行到addr时,会执行 这个函数,同时把 当前的state对象作为参数传递过去

length为 待hook指令的长度,在 执行完hook函数以后,angr需要根据length来跳过这条指令,执行下一条指令

在上面的示例中,hook了0x08048485处的指令


angr学习笔记

这是一条xor eax, eax的指令,长度为2.

defhook_demo(state): state.regs.eax=0 state.regs.ebx=0xdeadbeef

为了做示范,这里就是把eax设置为0(xor eax,eax的作用), 然后 设置ebx为0xdeadbeef, 因为后续不会用到ebx, 修改它可以在路径探索完后查看这个值是否符合预期。


angr学习笔记

可以看到ebx被修改成了0xdeadbeef。

SimState对象

这个对象保存着程序运行到某一阶段的状态信息。

通过这个对象可以操作某一运行状态的上下文信息,比如内存,寄存器等

创建state In[8]:p=angr.Project("./hello_angr") In[9]:st=p.factory.entry_state() In[10]:st.regs.rsp Out[10]:<BV640x7fffffffffeff98> In[11]:st Out[11]:<SimState@0x4004a0> In[12]:

首先加载二进制分析文件,创建project对象,然后创建一个entry_state, 之后就可以通过 这个state对象,获取或者修改此时程序的运行状态

entry_state: 做一些初始化工作,然后在 程序的 入口停下


angr学习笔记

还有一个用的比较多的是

st=p.factory.blank_state(addr=0x4004a0)

这会创建一个blank_state对象,这个对象里面很多东西都是未初始化的,当程序访问未初始化的数据时,会返回一个不受约束的符号量

基本操作

state对象一般是作为符号执行开始前创建用来 为 后续的执行 初始化一些数据,比如栈状态,寄存器值。

或者在路径探索结束后 ** 返回一个state对象供用户提取需要的值或进行 **约束求解,解出到达目标分支所使用的符号量的值。

访问寄存器

通过state.regs对象的属性访问以及修改寄存器的数据

In[12]:state.regs.r state.regs.r10state.regs.r14state.regs.raxstate.regs.rdistate.regs.rip state.regs.r11state.regs.r15state.regs.rbpstate.regs.rdxstate.regs.rsi state.regs.r12state.regs.r8state.regs.rbxstate.regs.register_defaultstate.regs.rsp state.regs.r13state.regs.r9state.regs.rcxstate.regs.rflags #获取rip的值 In[12]:state.regs.rip Out[12]:<BV640x400470> #获取rsp的值 In[13]:state.regs.rsp Out[13]:<BV640x7fffffffffeff78> #获取rbp的值 In[14]:state.regs.rbp Out[14]:<BV64reg_38_36_64{UNINITIALIZED}> #设置rbp=rsp+0x40 In[15]:state.regs.rbp=state.regs.rsp+0x40 In[16]:state.regs.rbp Out[16]:<BV640x7fffffffffeffb8> #对于BVV和BVS都需要通过solver进行求解得到具体的值 In[26]:hex(state.se.eval(state.regs.rbp)) Out[26]:'0x7fffffffffeffb8L' In[27]:hex(state.solver.eval(state.regs.rbp)) Out[27]:'0x7fffffffffeffb8L' 访问内存

有两种方式访问内存,一个是通过state.mem使用数组索引类似的方式进行访问

In[64]:state.mem[state.regs.rsp].qword Out[64]:<uint64_t<BV640x2>at0x7fffffffffeff78> In[65]:state.mem[state.regs.rsp].qword=0xdeadbeefdeadbeef In[66]:state.mem[state.regs.rsp].qword Out[66]:<uint64_t<BV640xdeadbeefdeadbeef>at0x7fffffffffeff78> In[67]:m=state.mem[state.regs.rsp] In[68]:m. m.STRONGREF_STATEm.doublem.int32_tm.register_defaultm.ssizem.uint32_tm.wstring m.arraym.dwordm.int64_tm.resolvablem.ssize_tm.uint64_t m.bytem.examplem.int8_tm.resolvedm.statem.uint8_t m.charm.floatm.longm.set_statem.storem.ui

Viewing all articles
Browse latest Browse all 9596

Trending Articles