【angr源码分析】7. exploration_techniques

参考:Angr源码分析——Explorer 技术及ExplorationTechnique机制简单解析

Technique

个人理解:technique也是插件的一种,特用于sim_manager。已知simgr中提供了若干执行SimState的方法,包括step,run,explore等。在simgr中,也可以配置类似插件的装置(technique)。angr提供了很多techenique,比如Explorer, DFS等,调用simgr的use_technique方法就可以加载一个technique。加载这个technique后,就会对simgr中原有的step,run等方法hook,真正执行时执行的是technique里的step等方法。也就是说,technique里实现的方法,才是simgr中真正执行的方法。

在sim_manager中,使用 tech = self.use_technique(Explorer(find, avoid, find_stash, avoid_stash, cfg, num_find))加载了Explorer插件。use_technique的定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def use_technique(self, tech):
if not isinstance(tech, ExplorationTechnique):
raise SimulationManagerError

tech.project = self._project
#调用setup方法
tech.setup(self)

def _is_overriden(name):
return getattr(tech, name).__code__ is not getattr(ExplorationTechnique, name).__code__

overriden = filter(_is_overriden, ('step', 'filter', 'selector', 'step_state', 'successors'))

#安装钩子,如果这个tech里提供了step, filter等方法,则执行simgr中的对应方法时,用钩子技术实际执行technique里的这些方法
hooks = {name: getattr(tech, name) for name in overriden}
HookSet.install_hooks(self, **hooks)

#记录这个technique
self._techniques.append(tech)
return tech

下面分析Explorer类。Explorer类是ExplorationTechnique类的子类,依次进行分析。

ExplorationTechnique

angr\exploration_techniques\_init_.py

注释:

一个otiegnqwvk (真的不知道是什么) 是为 sim_manager提供的钩子集合,用来帮助在符号执行中实现新的的techniques。

TODO:为功能性选择实际的name(??? choose actual name for the functionality (techniques? strategies?))

子类可以重载父类的任何一个方法。通过调用simgr.use_technique(instance_of_technique)来添加一个technique。

ExplorationTechnique方法提供了若干空方法,留给子类重载。

  1. setup方法:在sim_manager上执行一些你需要的初始化操作。
  2. step方法:只是调用了sim_manager的filter方法,具体功能看子类的实现
  3. filter方法:
  4. selector方法:
  5. step_state方法:
  6. complete方法:

这里要单独提一个方法:_condition_to_lambda

def _condition_to_lambda():

我们知道,在调用simgr.explore()时,可以设置avoid和find参数。这两个参数支持使用int型的地址,也可以传入一个方法判断当前state是否加入。

注释:

将一个整形,集合,列表或者lambda转换为lambda类型,来将当前状态地址和给定的地址对照,state的地址从block中读出。

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
def _condition_to_lambda(self, condition, default=False):
#condition可以是int, list, set, lambda
#default:默认返回值是什么(condition是空时返回什么)

if condition is None:
condition_function = lambda p: default
#添加了一个属性addrs,暂时不知道用来做什么
condition_function.addrs = set()

#如果传入的是整数,就先转换成list再操作
elif isinstance(condition, (int, long)):
return self._condition_to_lambda((condition,))

#如果传入的是列表,元组,集合,就统一转换成集合
elif isinstance(condition, (tuple, set, list)):
addrs = set(condition)

#定义condition_function,传入一个state(意义是什么?)
def condition_function(p):
if p.addr in addrs:
# returning {p.addr} instead of True to properly handle find/avoid conflicts(???啥意思)
return {p.addr}

#检查engine是否有效
if not isinstance(self.project.engines.default_engine, engines.SimEngineVEX):
return False

try:
# If the address is not in the set (which could mean it is
# not at the top of a block), check directly in the blocks
# (Blocks are repeatedly created for every check, but with
# the IRSB cache in angr lifter it should be OK.)
"""
如果addr不在集合中(可能意味着这不是block的起始地址),直接检
查blocks(虽然blocks每次检查时都被重复创建,但是有IRSB cache
在angr lifter中存在,应该大丈夫)
个人理解:即使p.addr不在addrs中,也不能保证这个块中没有包含给
定的地址,因为p.addr指的是块首地址,addrs指出的地址可能在块中
间。所以调用block获得地址集合,再求交集。
"""
return addrs.intersection(set(self.project.factory.block(p.addr).instruction_addrs))
except (AngrError, SimError):
return False

condition_function.addrs = addrs

#如果传入的condition已经是一个函数了,就使用这个函数
elif hasattr(condition, '__call__')
condition_function = condition
else:
raise AngrExplorationTechniqueError(

#返回生成的函数
return condition_function

下面就是具体的插件了。最常见的是Explorer,基本什么也没做。其他的插件如果用到再研究。

Explorer

angr\exploration_techniques\explorer.py

注释:

搜索路径,直到找到了 “num_find” 数量的满足”find”条件的路径,并且避免掉”avoid”条件的路径。找到的路径默认加入 “found_stash”,避免的路径默认加入”avoid_stash”。

“find” 和 “avoid” 参数可以是:整数地址,地址数组、列表、集合,或者一个方法。

如果cfg参数被设置了,而且”find”是整数或者列表类型,那么任何不可能到达成功状态的路径(除了经过一个失败状态???)都会被先行避免。

如果 “find” 或者 “avoid” 参数是一个返回bool类型的函数,而且一个路径触发了这两个状态,就将会被加入到find stash,除非”avoid_priority”被设置为True

def _init_():

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
def __init__(self, find=None, avoid=None, find_stash='found', avoid_stash='avoid', cfg=None, num_find=1, avoid_priority=False):
super(Explorer, self).__init__()

#调用_condition_to_lambda,把find和avoid参数统一转换为lambda
self.find = self._condition_to_lambda(find)
self.avoid = self._condition_to_lambda(avoid)

self.find_stash = find_stash
self.avoid_stash = avoid_stash
self.cfg = cfg

#神奇的命名
self.ok_blocks = set()

self.num_find = num_find
self.avoid_priority = avoid_priority

find_addrs = getattr(self.find, "addrs", None)
avoid_addrs = getattr(self.avoid, "addrs", None)

#看不懂的操作
self._extra_stop_points = (find_addrs or set()) | (avoid_addrs or set())

#下面的内容是和CFG相关的。需要再研究
from .. import analyses

#CFGFast是不被支持的。
if isinstance(cfg, analyses.CFGFast):
... ...

def filter():

在其父类ExplorationTechnique中定义filter的目的是:根据传入的filter_func,判断当前的state,返回true or false.

***这里的filter应该是和CFG结合,根据CFG额外删除掉一些没有意义的状态。