ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

angr理论笔记(三)program state

2021-09-04 14:03:05  阅读:227  来源: 互联网

标签:callstack 状态 state program angr x00 options


program state

在之前几章中,主要学习的是simulate state也就是模拟状态的程序运行状态。接下来将要学习的是state object也就是状态这个对象的性质(包含但不限于simulate state),并且学习如何和这个对象进行交互。

基本执行

首先简单介绍一下simulator的一些性质,具体性质将在下一章介绍。这里简单介绍一些性质。

state.step()执行符号执行的一步,并返回一个simsuccessors

和一般程序流执行不同,符号执行的一步将会返回许多结果,可以分为多种类型。我们在这里只关注.successor类型,这代表的是对于一个给定的step,返回normal处理结果的步骤。注意successor包含了一个所有正常后继的list

为什么是一个list?(其实我觉得很好理解,就是分支)文档中是这么说明的

比如说一个条件判断if(x>4)

在angr中的某个位置,会被解析为<bool x_32_1 > 4>是什么意思呢,就是x>4被抽想成为一个bool类型的符号。那么我们选择的分支还是的分支呢。我们都会选择。我们会产生两种状态:一种把x>4作为约束,另一种把!(x>4)作为约束。这样就会导致程序在正确的约束下执行。

文档给出一个说明:用来介绍succ=state.step()的用处

这个binary会先检查passwd是不是SOSNEAKY,如果是就A,不是就B,在这种情况下,就产生了分支。此程序就以此为入手点来说明state,step()的用处。

>>> proj = angr.Project('examples/fauxware/fauxware')
>>> state = proj.factory.entry_state(stdin=angr.SimFile)  # ignore that argument for now - we're disabling a more complicated default setup for the sake of education
>>> while True:
...     succ = state.step()
		# 到达了检查passwd的地址
...     if len(succ.successors) == 2:
...         break
...     state = succ.successors[0]

>>> state1, state2 = succ.successors
>>> state1
<SimState @ 0x400629>
>>> state2
<SimState @ 0x400699

当程序从标准输入读取数据时,angr默认将其视为无限长的输入符号数据流。

我们查看一下输入数据流,一下api比较容易理解,可以看到就是两个state对应的输入。

>>> input_data = state1.posix.stdin.load(0, state.posix.stdin.size)

>>> state1.solver.eval(input_data, cast_to=bytes)
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00SOSNEAKY\x00\x00\x00'

>>> state2.solver.eval(input_data, cast_to=bytes)
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00S\x00\x80N\x00\x00 \x00\x00\x00\x00'

状态预设

之前讲到,我们使用的大都是entry_state,这只是许多state中的一种

stateexplain
.blank_state大多数数据未初始化,进入时返回没有约束的符号值
.entry state准备调用main之前的状态
.full_init_state共享库和预定义内容都已经加载完毕,在enter状态之前,例如刚刚加载完共享库
.call_state准备调用函数的状态

调用方式见文档,觉得目前不需要使用这么复杂的调用。

http://angr.io/api-doc/angr.html#angr.factory.AngrObjectFactory

底层内存表示

state.mem对象可以有load和store两种方法,它的用处就是直接将memory中放入data。(原文是In a flat way我觉得意思就是,看起来怎么样就怎么样的放入内存)

>>> s = proj.factory.blank_state()
>>> s.memory.store(0x4000, s.solver.BVV(0x0123456789abcdef0123456789abcdef, 128))
>>> s.memory.load(0x4004, 6) # load-size is in bytes
<BV48 0x89abcdef0123>

由上图可以看出,BVV中的数据以大端法放入了内存。

当然是可选的,可以选择解析方式为大端还是小端。这里感觉和gdb很像。

>>> import archinfo
>>> s.memory.load(0x4000, 4, endness=archinfo.Endness.LE)
<BV32 0x67452301>

对于寄存器,state.registersstate_memory的使用方法是类似的。但是对于不同架构,寄存器类型不同,大小也不同。简单看一下是怎么实现的,有一个直观印象

https://github.com/angr/archinfo/blob/master/archinfo/arch_amd64.py

中的对于x86寄存器的表示方法,如图所示

可以看出主要是设置了名称,大小,下属寄存器

在这里插入图片描述

函数的前言和后缀

在这里插入图片描述

状态选项

对于每一个simstate对象,都会有一个state.option来控制程序在后续所有步骤中的执行偏好

这样的偏好有比如将偏好分离求解;将满足偏好的分支尽量减少求解次数;避免创建有地址的符号元素等求解方式。

# Example: enable lazy solves, an option that causes state satisfiability to be checked as infrequently as possible.
# This change to the settings will be propagated to all successor states created from this state after this line.
>>> 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)

详细的状态选项列表

https://docs.angr.io/appendix/options

状态插件

之前所讨论的memory,registers,mem,regs,slover都算是一个插入在state中的一种状态。这样的设计让程序和我们的符号执行分离开来,便于模块化处理。

一些特殊的状态插件,例如abstract memory插件可以模拟独立于地址的自由浮动内存映射。

全局插件

state.globals

代表了标准python的字典,可以允许你在一个状态下任意地址储存数值

历史插件

state.history

可以用来查看历史执行的数据和选择的路径。类似于一种单链表,通过state.history.parent.parent可以访问它。

history还有许多可以访问的内容。例如

history.descriptions可以保留每一步执行的说明字符串

history.bbl_addrs可以保留每一个执行的基本块的地址

history.jump_guards可以保留每一次执行流遇到分支时的限制(constrains)的说明

等等。

函数调用插件

callstack

callstack是一个单链表结构。每一次angr遇到call函数,都会在被追诉的callstack最上层生成一个新的frame,当一个函数执行完,这样一个callstack就被pop出来,这样可以增强angr分析的健壮性。

名称说明
callstack.func_addr当前执行函数(callstack单链表栈顶函数)地址
callstack.call_site_addr调用当前函数的基本块地址
callstack.stack_ptr当前函数的call_stack的栈顶指针
callstack.ret_addr当前执行函数返回到的父函数地址

复制和合并

复制用来对一个state,修改其中的内存或者寄存器的值,得到许多状态副本

>>> proj = angr.Project('/bin/true')
>>> s = proj.factory.blank_state()
>>> s1 = s.copy()
>>> s2 = s.copy()

>>> s1.mem[0x1000].uint32_t = 0x41414141
>>> s2.mem[0x1000].uint32_t = 0x42424242

状态也可以被合并

# merge will return a tuple. the first element is the merged state
# the second element is a symbolic variable describing a state flag
# the third element is a boolean describing whether any merging was done
>>> (s_merged, m, anything_merged) = s1.merge(s2)

# this is now an expression that can resolve to "AAAA" *or* "BBBB"
>>> aaaa_or_bbbb = s_merged.mem[0x1000].uint32_t
# n that can resolve to "AAAA" *or* "BBBB"
>>> aaaa_or_bbbb = s_merged.mem[0x1000].uint32_t

这里的"可以被解释为AAAA或者BBBB的状态“的含义不是很理解,于是做一个实验尝试一下

在这里插入图片描述

从上图中就可以看出来,其实还是一个状态,如果是状态1就是aaaa,否则就是bbbb的一个叠加体

总结

这一节主要讲了在angr分析程序执行流过程中的一些底层数据处理方式。包括对不同架构的处理细节原理,有关state的一些api和一些状态插件,求解偏好,函数调用的数据结构和对这种数据结构的交互方式。从最后一个小例子可以看出,每一个状态state其实本质就是一个逻辑序列。这一节比较抽象,没有具体的例子用来学习。

标签:callstack,状态,state,program,angr,x00,options
来源: https://blog.csdn.net/weixin_46521144/article/details/120098998

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有