Angr源码分析——SimState类
本博客由闲散白帽子胖胖鹏鹏胖胖鹏潜力所写,仅仅作为个人技术交流分享,不得用做商业用途。转载请注明出处,未经许可禁止将本博客内所有内容转载、商用。
0x00 官方API说明和SimState的构建函数
在前面一篇博客中,我们介绍了Angr的一种简单地使用方法。再接下来的几天中,我阅读了Angr的部分源码,包括factory类、Angr.Project类(新建工程的过程)、cle.Loader类(二进制文件的加载过程),这部分内容我推荐Conquer病毒写的博客。接下来我对求解引擎比较感兴趣,想要分析一下Claripy的源码,可是一直有个疑问,在factory类中反复提到了SimState、SimulationManager类,我就比较好奇这两个类的实现。所以这次我们先跟着官方API学习下SimState的源码,源码位置在angr/sim_state.py。
根据官方API的定义
SimState翻译:
class angr.sim_state.SimState(project=None, arch=None, plugins=None, memory_backer=None,permissions_backer=None, mode=None, options=None, add_options=None, remove_options=None, special_memory_filler=None, os_name=None)
SimState代表着程序运行状态,包括内存、寄存器、等等。
参数: regs – 状态的寄存器的方便表示方法,每个寄存器都是一个属性 mem – 状态的内存的方便表示,是这样一个类angr.state_plugins.view.SimMemViewregisters – 状态寄存器文件的平坦存储区域memory – 状态内存的平坦存储区域solver – 状态符号求解器和变量管理器inspect – 断点管理器,是一个 angr.state_plugins.inspect.SimInspectorlog – 状态历史信息scratch – 当前执行步骤的信息posix – MISNOMER: 操作系统或者环境模型的表示(一般用于stdin,stdout)libc – 仿真时使用的标准库信息cgc – cgc 环境的相关信息uc_manager – 控制约束内符号执行unicorn – 控制 Unicorn引擎
但是这只是官方API,代码中有了更新,而之前Conquer病毒写的SimState部分的源码分析已经不再适用。更新流程可以参考此issue。我们首先来看一下SimState的init函数。
class SimState(PluginHub, ana.Storable):#继承自PluginHub类,稍后介绍def __init__(self, project=None, arch=None, plugins=None, memory_backer=None, permissions_backer=None, mode=None, options=None,add_options=None, remove_options=None, special_memory_filler=None, os_name=None, plugin_preset='default'):#调用PluginHub的init方法super(SimState, self).__init__()#设置project、架构、选项等等self.project = projectself.arch = arch if arch is not None else project.arch.copy() if project is not None else Noneif type(self.arch) is str:self.arch = arch_from_id(self.arch)# the optionsif options is None:if mode is None:l.warning("SimState defaulting to symbolic mode.")mode = "symbolic"options = o.modes[mode]if isinstance(options, (set, list)):options = SimStateOptions(options)if add_options is not None:options |= add_optionsif remove_options is not None:options -= remove_optionsself._options = optionsself.mode = mode#检查预加载插件if plugin_preset is not None:self.use_plugin_preset(plugin_preset)#如果参数提供了新的plugin就把他加入我们的SimState里if plugins is not None:for n,p in plugins.iteritems():self.register_plugin(n, p, inhibit_init=True)for p in plugins.itervalues():p.init_state()if not self.has_plugin('memory'):# Angr不指定内存的地址上下线,因为内存地址的范围不像寄存器一样容易构造# 如果给Angr提供内存插件或者预加载插件的话,Angr默认使用default插件 if self.plugin_preset is None:self.use_plugin_preset('default')#根据options里面的memory选线加载不同的memory插件if o.ABSTRACT_MEMORY in self.options:# We use SimAbstractMemory in static mode.# Convert memory_backer into 'global' region.if memory_backer is not None:memory_backer = {'global': memory_backer}# TODO: support permissions backer in SimAbstractMemorysim_memory_cls = self.plugin_preset.request_plugin('abs_memory')sim_memory = sim_memory_cls(memory_backer=memory_backer, memory_id='mem')elif o.FAST_MEMORY in self.options:sim_memory_cls = self.plugin_preset.request_plugin('fast_memory')sim_memory = sim_memory_cls(memory_backer=memory_backer, memory_id='mem')else:sim_memory_cls = self.plugin_preset.request_plugin('sym_memory')sim_memory = sim_memory_cls(memory_backer=memory_backer, memory_id='mem',permissions_backer=permissions_backer)self.register_plugin('memory', sim_memory)if not self.has_plugin('registers'):# 寄存器插件也和内存插件的情况相同if self.plugin_preset is None:self.use_plugin_preset('default')if o.FAST_REGISTERS in self.options:sim_registers_cls = self.plugin_preset.request_plugin('fast_memory')sim_registers = sim_registers_cls(memory_id="reg", endness=self.arch.register_endness)else:sim_registers_cls = self.plugin_preset.request_plugin('sym_memory')sim_registers = sim_registers_cls(memory_id="reg", endness=self.arch.register_endness)self.register_plugin('registers', sim_registers)# 设置系统名self.os_name = os_name# This is used in static mode as we don't have any constraints thereself._satisfiable = True# states are big, so let's give them UUIDs for ANA right away to avoid# extra picklingself.make_uuid()self.uninitialized_access_handler = Noneself._special_memory_filler = special_memory_filler# this is a global condition, applied to all added constraints, memory reads, etcself._global_condition = Noneself.ip_constraints = []
这里印证了官方API说明中memory和register的字符串,那么剩下的功能在哪里提供呢?代码里面反复提到的Plugin是什么呢?
0x02Angr的插件机制
这里说一些无关的内容,最开始看的时候,我以为官方API和写错了,而且跟着代码阅读的时候在我觉得报错的地方正常运行。我在这里卡了两天。直到我意识到,Python的类和实例是有区别的。从我的角度来看,Python中类也是一个对象;实例可以继承类的属性;在Java中,类不是一个对象,只是对象的生成的模板。实例的属性完全来自于类的定义。Python中类的属性,是类似于Java 中的静态变量,是可以进行修改的。这一点要记住,很重要。
好说回源码,我们先看SimState的定义,他继承自PluginHub类。PluginHub类官方说明是这样的。
“Plugin Hub对象包括了很多插件,同时有一个preset概念,preset能够提供插件的基础实现,并且能够直接附加到某一环境中。
在Angr中,像SimState、Analyses hub、SIMEngine selector等都是用这个模型,用来统一自动搜集和选择插件。采用了可配置的策略模式(设计模式)。
每个PluginHub子类若想要提供比默认插件更丰富的功能,则要有至少一个对应的plugin子类,或者一个PluginPreset子类。”
也就是说,PluginHub就像是插槽一样,在SimState上面加载了一个插槽,我们想要扩展、增加SimState的功能,只需要专心于开发插件即可,PluginHub类会自动为我们插上插件。我们再来看看是怎么插上去的。先抛开SimState的定义部分,查看官方代码的842-843行(这部分是类定义之外的代码,这部分代码在import的时候就执行了)。
default_state_plugin_preset = PluginPreset()
SimState.register_preset('default', default_state_plugin_preset)
这种代码的方法,SimState就是我提到的Python和Java的区别,这里调用的是类方法,修改的是类属性。以后再实例化任何该类的时候,我们修改的属性就会保留修改的状态。PluginPreset类的定义和PluginHub同在angr/misc/plugins.py下。饭一口一口吃,先来学习下PluginPreset类的定义。
class PluginPreset(object):"""plugin preset对象包含着插件名和插件类的字典。preset在hub上面激活之后,就可以处理插件的相关请求(激活、反激活、增、删等)与Plugin和PluginHub类不同,每一个单独的PluginPreset实例都应定义在模组级别。开发者应该讲preset实例添加到hub里面,并且允许插件方便将自己添加到preset列表里,而不需要显示引用preset。"""属性:_default_plugins :默认的插件列表方法: activate: 调用此方法激活presetdeactivate: 调用此方法反激活presetadd_default_plugin: 在preset中增加一个插件list_default_plugins(): 返回preset中可用的插件request_plugin(self, name): 查找名为name的插件是否存在于preset中,如果没有就返回错误;否则返回插件类。copy(self): 获得PluginPreset的一个拷贝
以及register_preset方法的定义
@classmethoddef register_preset(cls, name, preset):#注册一个preset实例,以及他所属的hub。它允许单个插件使用preset的classmethod进行自动化的注册,注册时仅使用插件的名字。if cls._presets is None:cls._presets = {} cls._presets[name] = preset
说白了Angr一次性将你可能需要的所有的插件都放到preset里面,当你需要使用此插件时,只要提供插件的名字,PluginPreset以及PluginHub类会自动为你管理。以及SimState的一些功能(比如memory、register、unicorn等等API提供的功能)都是通过Preset进行管理的。那842-843行就是为我们的SimState所使用的PluginHub注册了一个preset实例。代码看到这里,应该有所了解了,但是,我刚才说了他提供了我所需要的所有插件,可是我只看到了一个default插件,没看到其他的东西呀。这里还是通过修改类属性完成的。在state_plugins包中包含了我们所需要的所有SimState插件,那怎么加载的?请看代码。
###angr/state_plugins/solver.py###
#随便打开一个solver插件,这也是我们所关心的
#还是跳过定义查看最后几行
892:from angr.sim_state import SimState
893:SimState.register_default('solver', SimSolver)
#跳转到register_default函数定义
###angr/misc/plugins.py###
34:@classmethod
35:def register_default(cls, name, plugin_cls, preset='default'):
36: if cls._presets is None or preset not in cls._presets:
37: l.error("Preset %s does not exist yet...", preset)
38: return
39: cls._presets[preset].add_default_plugin(name, plugin_cls)
#add_default_plugin在PluginPreset类中有定义
227:def add_default_plugin(self, name, plugin_cls):"""Add a plugin to the preset."""
231: self._default_plugins[name] = plugin_cls
#这里大家应该明白了单个的Plugin应该怎么加入的了,
#紧接着看看这些插件怎么一起加进去的
###angr/state_plugins/__init__.py ###
from .plugin import *
from .libc import *
from .posix import *
from .inspect import *
from .solver import *
from .symbolic_memory import SimSymbolicMemory
from .abstract_memory import *
from .fast_memory import *
from .log import *
from .history import *
from .scratch import *
from .cgc import *
from .gdb import *
from .uc_manager import *
from .unicorn_engine import Unicorn
from .sim_action import *
from .sim_action_object import *
from .sim_event import *
from .callstack import *
from .globals import *
from .preconstrainer import *
from .loop_data import *
from .view import *
from .filesystem import *
emmmm,这里就分析完了插件的加载过程。总结一下,Angr通过PluginHub和PluginPreset预加载所有的需要用到的插件,而SimState通过继承PluginHub类在预加载的插件中选择自己需要的进行使用。我们在扩展Angr功能时,只需要关心插件本身的功能逻辑即可,Angr会自动进行插件的添加。
0x03 solver插件
我开头说了,我比较好奇求解的过程,所以我们就看下solver的代码。代码位置:angr/state_plugins/solver.py。目前在求解的时候,用到eval这个函数比较多,关于solver的API可以移步官方,提供了很多求解方法,我们从源码角度分析一下。以eval为例。
def eval(self, e, **kwargs):# eval_upto already throws the UnsatError, no reason for us to worry about itreturn self.eval_upto(e, 1, **kwargs)[0]
#函数调用了eval_upto,继续跟进
def eval_upto(self, e, n, cast_to=None, **kwargs):#value的正确性检查先不管concrete_val = _concrete_value(e)if concrete_val is not None:#调用了_cast_to指定输出格式return [self._cast_to(e, concrete_val, cast_to)]#使用_eval求解cast_vals = [self._cast_to(e, v, cast_to) for v in self._eval(e, n, **kwargs)]if len(cast_vals) == 0:raise SimUnsatError('Not satisfiable: %s, expected up to %d solutions' % (e.shallow_repr(), n))return cast_vals
#我们继续跟进_eval函数
def _eval(self, e, n, extra_constraints=(), exact=None):return self._solver.eval(e, n, extra_constraints=self._adjust_constraint_list(extra_constraints), exact=exact)
#在这里我们发现他使用_solver进行求解,我们跟进这个定义
@property
def _solver(self):"""根据state的选项,创建或者获取一个Claripy的求解器"""if self._stored_solver is not None:return self._stored_solvertrack = o.CONSTRAINT_TRACKING_IN_SOLVER in self.state.optionsif o.ABSTRACT_SOLVER in self.state.options:self._stored_solver = claripy.SolverVSA()elif o.SYMBOLIC in self.state.options and o.REPLACEMENT_SOLVER in self.state.options:self._stored_solver = claripy.SolverReplacement(auto_replace=False)elif o.SYMBOLIC in self.state.options and o.CACHELESS_SOLVER in self.state.options:self._stored_solver = claripy.SolverCacheless(track=track)elif o.SYMBOLIC in self.state.options and o.COMPOSITE_SOLVER in self.state.options:self._stored_solver = claripy.SolverComposite(track=track)elif o.SYMBOLIC in self.state.options and o.approximation & self.state.options:self._stored_solver = claripy.SolverHybrid(track=track)elif o.SYMBOLIC in self.state.options:self._stored_solver = claripy.Solver(track=track)else:self._stored_solver = claripy.SolverConcrete()return self._stored_solver
从以上代码可以看出,实际上的求解工作,还是Claripy进行的,我们使用的solver实际上是对Claripy的封装,以便于更好的和Angr兼容。最后,我更正一下Angr求解的工作流程。
Angr求解的流程:
1.创建Claripy.BVS或者Claripy.BVV格式的符号化变量
2.使用factory获取程序的state,可以是程序入口状态(entry_state,也可以从任意函数开始的state,甚至是blank_state)
3.state加载memory、register、solver等插件
4.solver插件根据state的参数设置,创建Claripy求解器
5.使用step等方式获得程序的最终状态
6.获取从初始state到最终state之间的约束条件
7.使用Claripy.Solver.eval进行求解
关于如何提取约束条件、Claripy求解这部分我们我们目前还不清楚,等待以后阅读源码
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
