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.SimMemView
        registers – 状态寄存器文件的平坦存储区域
        memory – 状态内存的平坦存储区域
        solver – 状态符号求解器和变量管理器
        inspect – 断点管理器,是一个 angr.state_plugins.inspect.SimInspector
        log – 状态历史信息
        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 = project
        self.arch = arch if arch is not None else project.arch.copy() if project is not None else None

        if type(self.arch) is str:
            self.arch = arch_from_id(self.arch)

        # the options
        if 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_options
        if remove_options is not None:
            options -= remove_options
        self._options = options
        self.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 SimAbstractMemory
                sim_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 there
        self._satisfiable = True

        # states are big, so let's give them UUIDs for ANA right away to avoid
        # extra pickling
        self.make_uuid()

        self.uninitialized_access_handler = None
        self._special_memory_filler = special_memory_filler

        # this is a global condition, applied to all added constraints, memory reads, etc
        self._global_condition = None
        self.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: 调用此方法激活preset
	  deactivate: 调用此方法反激活preset
          add_default_plugin: 在preset中增加一个插件
	  list_default_plugins(): 返回preset中可用的插件
	  request_plugin(self, name): 查找名为name的插件是否存在于preset中,如果没有就返回错误;否则返回插件类。
	  copy(self): 获得PluginPreset的一个拷贝

以及register_preset方法的定义

    @classmethod
    def 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 it
    return 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_solver

	track = o.CONSTRAINT_TRACKING_IN_SOLVER in self.state.options

	if 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求解这部分我们我们目前还不清楚,等待以后阅读源码

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值