【Angr源码分析】3. 后继状态探索
上一篇记录了 explore 功能是如何实现的,其实就是对一个 simstate 一直调用 step 探索后继状态,然后不断对状态分类,知道找到指定的状态。但是留了一个最重要的问题:怎么对一个状态找到后继状态。这个工作由引擎(Engine)负责。
官方文档的内容
早知道官方文档已经这么详细了我就应该先看文档的
Engine
参考链接:
通常情况下,一个 engine 的入口点是 SimEngine.process()
。
对于 simgr 来说,引擎必须使用 SuccessorsMixin
,它提供了 process()
方法可以创建一个 SimSuccessors
对象,然后调用 process_successors()
方法为的是其他 mixins 可以充填它。
其实看代码的时候就注意到 mixin 这个概念了,但一直都没懂。
Angr 默认的引擎是 UberEngine,它包含了若干个提供 process_successors()
方法的 mixin:
SimEngineFailure
- handles stepping states with degenerate jumpkinds英语水平有限,degenerate jumpkind 指的是不是失败/错误的跳转类型?
SimEngineSyscall
- handles stepping states which have performed a syscall and need it executed (处理系统调用有关的执行)HooksMixin
- handles stepping states which have reached a hooked address and need the hook executed (处理 hook 有关的代码)SimEngineUnicorn
- executes machine code via the unicorn engine (通过 unicore engine(qemu) 执行机器码,这里也需有我最感兴趣的部分)SootMixin
- executes java bytecode via the SOOT IR (JAVA 相关)HeavyVEXMixin
- executes machine code via the VEX IR (通过 VEX IR 执行机器码,同 SimEngineUnicorn)
遇到一个 state 时,这些 mixin 会挨个检查当前 state 自己能否处理。如果不能处理,就会调用 super()
让下一个栈中的下一个类处理。
听起来像是有一个栈结构存放这些 mixin,然后依次调用。
Engine Mixins
SimEngineFailure
负责处理错误。只有在前一个跳转类型 (jumpkind) 是 Ijk_EmFail
, Ijk_MapFail
, Ijk_Sig*
, Ijk_NoDecode
(but only if the address is not hooked), 或者 Ijk_Exit
时才有用。
这里我查了一下 jumpkind 的资料:https://github.com/angr/vex/blob/master/pub/libvex_ir.h#L2294
Ijk_EmFail
:emulation critical (FATAL) error; give upIjk_MapFail
: Vex-provided address translation failedIjk_Sig*
:current instruction synths SIG* (SIGSEGV, SIGTRAP …)Ijk_NoDecode
: current instruction cannot be decoded
对于上述四种情况,会抛出异常;对 Ijk_Exit
这种情况,则会返回没有后继状态。
SimEngineSyscall
负责处理系统调用,对应 IJK_Sys*
类型的 Jumpkind。它的原理是,调用 SimOS
获得一个 SimProcedure 来处理系统调用相关操作。
HooksMixin
提供 angr 中的 hook 功能。当状态到达一个被 hook 的地址,且 jumpkind 不是 Ijk_NoHook
时,该模块生效。它会查看和该 hook 相关的 SimProcedure 并在当前状态上执行该 SimProcedure。It also takes the parameter procedure
, which will cause the given procedure to be run for the current step even if the address is not hooked.
SimEngineUnicorn
负责具体执行。
SimEngineVEX
负责符号执行。它负责将当前地址的机器码转换为 IRSB,然后对 IRSB 符号执行。它有大量的参数用于控制符号执行的过程,相关文档在 http://angr.io/api-doc/angr.html#angr.engines.vex.engine.SimEngineVEX.process
源码分析
还是用之前的那个例子。explore 函数会从程序的入口点开始执行,下面是二进制入口点的代码和 angr 的入口代码。因此 simstate.pc == 0x8048450
1 | .text:08048450 public _start |
1 | # 入口 |
最终调用了 SuccessorsMixin.process 方法。
1 | class SuccessorsMixin(SimEngine): |
这个 self.process_successors(self.successors, **kwargs)
是重点,前面官方文档的 Engine 部分提到了一系列 Mixin。在调用到这里的时候,因为 self 是 UberEngine 类型,所以调用 self.process_successors 的时候会依次调用下面几个类的 process_successors 方法:
1. SimEngineFailure
如官方文档所说,只处理前一个跳转类型 (jumpkind) 是 Ijk_EmFail
, Ijk_MapFail
, Ijk_Sig*
, Ijk_NoDecode
(but only if the address is not hooked), 或者 Ijk_Exit
时。
对于 0x8048450 来说 jumpkind 是 Ijk_Boring
,不归该函数处理。结尾的 return super().process_successors(successors, **kwargs)
将会转到下一个子类处理。
1 | # venv/lib/python3.8/site-packages/angr/engines/failure.py |
2. SimEngineSyscall
仅负责处理系统调用。这里暂时不关心系统调用是如何执行的,只知道在这里处理的就好。
1 | # venv/lib/python3.8/site-packages/angr/engines/syscall.py |
3. HooksMixin
处理 hook 函数。同样不关心。
1 | # venv/lib/python3.8/site-packages/angr/engines/hook.py |
4. SimEngineUnicorn
负责具体执行。和这里也没关系,不过我也比较好奇到底什么情况下会调用 Unicorn 进行具体执行。
1 | # venv/lib/python3.8/site-packages/angr/engines/unicorn.py |
5. SootMixin
JAVA 相关,不关心。
1 | # venv/lib/python3.8/site-packages/angr/engines/soot/engine.py |
6. HeavyVEXMixin
参考链接:
HeavyVEXMixin 用于对 SimState 提供完全的符号执行支持,它用来提供一个解析 VEX 基本块的基本框架。它的主要用途是初步解析 VEX IRSB,并将其具体的处理分发给各个其他 mixin。
1 | # venv/lib/python3.8/site-packages/angr/engines/vex/heavy/heavy.py |
这里的 lift_vex 函数是 VEXLifter 提供的。本质上是调用了 pyvex 的 lift 方法,返回的结果是一个 IRSB 对象 (venv/lib/python3.8/site-packages/pyvex/block.py),不再展示了。
最重要的地方在 self.handle_vex_block(irsb)
这里,是如何对 VEX 进行符号执行的。