省流量直接看逻辑图:https://app.diagrams.net/#Hchenaotian%2Fangr-work%2Fmain%2Fangr.drawio
angr就是angr,这是它的logo:

逻辑结构:

代码目录中一些比较关键的部分:
├── concretization_strategies:内存符号化策略
├── exploration_techniques:用于传给SimulationManager.use_technique,各种不同的探索方法
├── misc:一些初始 类
├── procedures:函数摘要
├── simos:初始化类状态的,linux java啥的,state_entry就在这
├── state_plugins:状态的插件,内存、寄存器等的基础类
├── storage:内存插件(memory_mixin)、文件、网络类等
├── sim_state.py :程序状态类,主要类,负责注册各种功能插件
├── factory.py:定义程序初始状态/入口的类
├── project.py:主要类,angr加载程序
└── sim_manager.py:模拟执行管理类,step、run等方法
不是本文的重点知识,但感觉还必须有这一节,可以参考:
开始读源码,前面的部分快速看一眼干了啥就行,主要分析一下angr如何模拟内存的读写,还有遇到符号化内存地址寻址的时候的处理方法。详细的类之间的关系以及实现的功能请看逻辑图
一般用angr初始化一个二进制程序分为一下几步骤:
import angr
p = angr.Project("test.out")
state = p.factory.entry_state()
simgr = p.factory.simgr(state)#simgr是simulation_manager的别名
simgr.explore(find=xxxx)
前面的这些简单过一遍,不详细分析了,主要后面分析内存相关。
angr的入口,负责启动解析一个二进制文件,根据二进制文件的类型(如ELF、PE)去解析其中的一些 信息 ,如程序段信息、程序入口点、程序导入导出函数表、程序架构、所属平台(Linux、Windows)信息等,其实就是将程序的基本信息获取一下,然后再初始化一些接下来会用到的其他类的对象,如factory等。
AngrObjectFactory类就是上面代码中的p.factory,在Project类中被初始化,主要功能其实就是获取一个程序状态作为初始化状态,这里提供了不同的选择:
其实获取上面这些状态也不是factory 自己来实现的,他也是调用的SimOs类:
class AngrObjectFactory:
··· ···
def entry_state(self, **kwargs) -> SimState:
return self.project.simos.state_entry(**kwargs)
··· ···
SimOs是模拟操作系统的基本类,会根据操作系统的类型不同拍生成对应操作系统的类,比如SimLinux,上述的很多获得初始化状态的方法,都来自于SimOs中的state_blank方法:
class SimOS:
def state_blank(self, addr=None, initial_prefix=None, brk=None, stack_end=None, stack_size=1024*1024*8, stdin=None,
thread_idx=None, permissions_backer=None, **kwargs):
#基本初始化状态函数
# TODO: move ALL of this into the SimState constructor
··· ···
if kwargs.get('permissions_map', None) is None: #初始化段权限
··· ···
state = SimState(self.project, stack_end=stack_end, stack_size=stack_size, stack_perms=stack_perms, **kwargs)#在这里初始化SimState类
if stdin is not None and not isinstance(stdin, SimFileBase):#stdin 指定了文件的话
··· ···
last_addr = self.project.loader.main_object.max_addr
actual_brk = (last_addr - last_addr % 0x1000 + 0x1000) if brk is None else brk
state.register_plugin('posix', SimSystemPosix(stdin=stdin, brk=actual_brk))
#posix 是 state中的插件,用于表示一些文件交互比如stdin等
#后面初始化了寄存器、栈之类的一堆东西
总之就模拟操作系统执行程序之前,初始化了一堆东西。
该类主要可以通过一个输入程序状态来进行继续的单步执行(step)或run等操作,也是通过该类拉设置探索方式(ExplorationTechnique)。比较重要的类,但不在这里详细分析了。
本篇重点分析的这一部分。
首先了解一下angr里无处不在的Mixin设计模式:
Mixin是angr 的一个设计模式,从一个基类根据不同的小功能模块派生出一堆扩展类根据某个大功能需要的若干小功能,将这些小功能的扩展类一起用 python 多继承的方法派生出一个最终的实现大功能的类。在调用该大功能中某个实现方法的时候,会依次使用super 调用下一个小功能中的该方法。就相当于多个小功能接力棒的形式依次完成自己的工作。有些功能之间的关系是平行的(前后顺序无所谓),有些功能之间的关系是依赖的(前后顺序不能颠倒)。

如图所示,class2、class3、class4都是继承的class1,而class5 由class2、class3、class4共同多继承。而他们都实现了method1方法。并且在每个method1中都调用super().method1(),这样调用class5.method1()方法的时候就会按顺序调用52341的method1方法。根据这种思路,可以将一个复杂的逻辑拆成好几个小部分分别实现。在组合的时候也可以根据场景需要组合出适应不同场景功能的最终类。
插件主要涉及三个类:
angr根据不同的大功能方向(如寄存器插件、内存插件)来划分插件,而不同种类的插件却拥有不止一种,如内存插件有默认的内存插件、快速内存插件、java的内存插件等等。而单个插件的实现使用了上面介绍的Mixin设计方法,将一个当个方向的大功能拆分成很多小功能,最后再多继承的方式组合成该大功能,这样几个相似功能的插件就可以实现部分小功能的复用。
PluginHub类
PluginHub类用作插件管理器,它通常会被派生成实际的插件管理器,比如程序状态类SimState就是派生自PluginHub,也就是说程序状态实际就是一个管理不同小状态插件的插件管理器。
class PluginHub:
def __init__(self):
super(PluginHub, self).__init__()
self._active_plugins = {
} # type: Dict[str, SimStatePlugin] 正在使用的插件
self._active_preset = None # type: Optional[PluginPreset] 正在使用的插件池
self._provided_by_preset = [] # type: List[int]
_presets = None # 类属性,插件池,可以有多个插件池,主要使用默认插件池,里面都是PluginPreset类型对象
@classmethod
def register_default(cls, name, plugin_cls, preset='default'):
#类方法,用来给默认插件池注册一个插件类。
if cls._presets is None or preset not in cls._presets:
l.error("Preset %s does not exist yet...", preset)
return
cls._presets[preset].add_default_plugin(name, plugin_cls)
@classmethod
def register_preset(cls, name, preset):
#类方法,给指定插件池注册一个插件类
if cls._presets is None:
cls._presets = {
}
cls._presets[name] = preset
@property
def plugin_preset(self):
#可以直接获得插件池
return self._active_preset
··· ···
@property
def has_plugin_preset(self) -> bool:#查看是否存在指定名称的插件池
"""
Check whether or not there is a plugin preset in use on this hub right now
"""
return self._active_preset is not None
def use_plugin_preset(self, preset):
#将一个插件池设置为启用状态
if isinstance(preset, str):#如果通过字符串指定,查看是否有该名称的插件池,获取具体插件池对象
try:
preset = self._presets[preset]
except (AttributeError, KeyError):
raise AngrNoPluginError("There is no preset named %s" % preset)
elif not isinstance(preset, PluginPreset):
raise ValueError("Argument must be an instance of PluginPreset: %s" % preset)
if self._active_preset:
l.warning("Overriding active preset %s with %s", self._active_preset, preset)
self.discard_plugin_preset()
preset.activate(self) #调用插件池的激活函数
self._active_preset = preset #表示启用该插件池
def get_plugin(self, name: str) -> 'SimStatePlugin':
#从当前激活的插件中获取指定名称的插件
if name in self._active_plugins: #如果查询到直接返回
return self._active_plugins[name]
elif self.has_plugin_preset: #没找到的话要从插件池中寻找该插件类,并且初始化成一个对象再返回
plugin_cls = self._active_preset.request_plugin(name) #获取插件类
plugin = self._init_plugin(plugin_cls) #初始化该插件
# Remember that this plugin was provided by preset.
self._provided_by_preset.append(id(plugin))
self.register_plugin(name, plugin)
return plugin
else:
raise AngrNoPluginError("No such plugin: %s" % name)
def register_plugin(self, name: str, plugin):
#将一个初始化好的插件添加到激活列表,也就是注册一个插件
if self.has_plugin(name): #如果已经存在该插件,先释放然后重新注册
self.release_plugin(name)
self._active_plugins[name] = plugin
setattr(self, name
免责声明:本文系网络转载或改编,未找到原创作者,版权归原作者所有。如涉及版权,请联系删