angr文档中文翻译Hooks and SimProcedures部分

Hooks and SimProcedures

angr中的Hooks非常强大!您可以使用它们以您可以想象的任何方式修改程序的行为。然而,您可能不知道如何对特定hook进行编程。本章应作为 SimProcedures 编程时的指南。(对一些特定的库函数或api函数进行重写)

快速入门

下面是一个可以删除任何程序中所有错误的示例:

from angr import Project, SimProcedure
project = Project('examples/fauxware/fauxware')

class BugFree(SimProcedure):
   def run(self, argc, argv):
       print('Program running with argc=%s and argv=%s' % (argc, argv))
       return 0

# this assumes we have symbols for the binary
project.hook_symbol('main', BugFree())

# Run a quick execution!
simgr = project.factory.simulation_manager()
simgr.run()  # step until no more active states
Program running with argc=<SAO <BV64 0x0>> and argv=<SAO <BV64 0x7fffffffffeffa0>>
<SimulationManager with 1 deadended>

现在,每当程序执行到主函数时,它都会执行这个过程,而不是执行实际的主函数!它只是打印出一条消息,然后返回。

现在,我们来谈谈调用这个函数的过程发生了什么!当进入函数时,进入参数的值从哪里来?您可以使用run()任意数量的参数来定义函数,SimProcedure 运行时将通过调用约定自动从程序状态中提取这些参数(通过调用约定获取到被hook前函数的参数),并使用它们调用您的运行函数。类似地,当您从 run 函数返回一个值时,它会被置于状态中(同样,根据调用约定将其放入被hook前函数的返回值中),并执行从函数返回的实际控制流操作,这取决于体系结构可能涉及跳转到链接寄存器或跳转到堆栈弹出的结果。

此时应该清楚的是,我们刚刚编写的 SimProcedure 旨在完全替换它所挂钩的任何函数。事实上,SimProcedures 的最初用例是替换库函数。稍后会详细介绍。

(疑问:1,调用约定一定正确吗?是否存在参数赋值错误?2,如果参数的值是地址,那么是否可以通过这个地址在angr运行过程中获取地址所指地方具体的值?)

 使用场景

Project类上,字典project._sim_procedures是从地址到SimProcedure实例的映射。当执行流程到达该字典中存在的地址(即被挂钩的地址)时,它将执行 project._sim_procedures[address].execute(state). 这将参考调用约定来提取参数,制作自身的副本以保持线程安全,然后运行该run()方法。每次运行时生成 SimProcedure 的新实例非常重要,因为运行 SimProcedure 的过程必然涉及 SimProcedure 实例上的状态变化,因此我们需要为每个步骤单独创建一个实例,以免遇到竞争条件多线程环境。

关键字参数

此层次结构意味着您可能希望在多个挂钩中重用单个 SimProcedure。如果您想在多个地方挂接相同的 SimProcedure,但每次都稍作调整,该怎么办?angr 对此的支持是,您传递给 SimProcedure 的构造函数的任何附加关键字参数最终都会作为关键字参数传递给 SimProcedure 的run() 方法。

数据类型

如果您注意前面的示例,您会注意到当我们打印出函数的参数时run(),它们作为一个奇怪的类<SAO <BV64 0xSTUFF>>出现 。这是一个SimActionObject. 基本上,您不需要太担心它,它只是普通位向量的一个薄包装。它会跟踪您在 SimProcedure 中到底用它做了什么——这对于静态分析很有帮助。

您可能还注意到,我们直接从过程中返回了 Python int 0。这将自动提升为字大小的位向量!您可以返回本机​​数字、位向量或 SimActionObject。

当您想要编写处理浮点数的过程时,您需要手动指定调用约定。这并不太难,只需为钩子提供一个cc:cc = project.factory.cc_from_arg_kinds((True, True), ret_fp=True) and project.hook(address, ProcedureClass(cc=mycc)) 这种传递调用约定的方法适用于所有调用约定,因此如果angr的自动检测的方法不正确,您可以修复它。(用这种方法解决调用约定识别不准的问题)

控制流程

如何退出 SimProcedure?我们已经讨论了执行此操作的最简单方法,即从 run()中返回一个值。这实际上是 call 的简写 self.ret(value)self.ret()是真正的返回返回值的函数。

SimProcedures 可以使用很多像这样的不同函数!

  • ret(expr):从函数返回

  • jump(addr):跳转到二进制中的某个地址

  • exit(code):终止程序

  • call(addr, args, continue_at):调用二进制文件中的函数

  • inline_call(procedure, *args):内联调用另一个SimProcedure并返回结果

倒数第二个值得一看。我们绕了一小段路后就到达了…

有条件退出

如果我们想从 SimProcedure 中添加条件分支怎么办?为此,您需要直接使用当前执行步骤的 SimSuccessors 对象。

其接口是self.successors.add_successor(state, addr, guard, jumpkind). 如果您到目前为止已经了解,所有这些参数都应该具有明显的含义。请记住,您传入的状态不会被复制并且会发生变化,因此如果还有更多工作要做,请务必提前进行复制!

SimProcedure恢复执行

我们如何调用二进制文件中的函数并在 SimProcedure 中恢复执行?有一大堆称为“SimProcedure Continuation”的基础设施可以让您做到这一点。当您使用self.call(addr, args, continue_at) 时,预计addr是您要调用的地址, args是您要调用它的参数元组,并且 continue_a​​​​​​​t是 SimProcedure 类中您希望继续执行的另一个方法的名称当它返回时。该方法必须与该方法具有相同的签名。此外,您可以传递关键字参数作为用于与被调用者通信的调用约定。​​​​​​​run()cc

当您执行此操作时,您将完成当前步骤,并且将在您指定的函数的下一步中再次开始执行。当该函数返回时,它必须返回某个具体地址!该地址由 SimProcedure 运行时指定:在 angr 的 externs 段中分配一个地址,用作返回给定方法调用的返回站点。然后,它与经过调整的过程实例的副本挂钩,以运行指定的 continue_at函数而不是run(),并具有与第一次相同的 args 和 kwargs。

为了正确使用延续子系统,您需要将两部分元数据附加到 SimProcedure 类:

  • 设置类变量IS_FUNCTION = True

  • 将类变量设置local_vars为字符串元组,其中每个字符串都是 SimProcedure 上的实例变量的名称,您希望在返回时保留其值。局部变量可以是任何类型,只要不改变它们的实例即可。

您现在可能已经猜到存在某种辅助存储来保存所有这些数据。你是对的!状态插件 state.callstack有一个名为 SimProcedure 运行时的条目.procedure_data,用于存储当前调用帧的本地信息。angr 跟踪堆栈指针,以使当前顶部成为 state.callstack有意义的本地数据存储。它应该存储在堆栈帧的内存中,但数据无法序列化和/或内存分配很困难。

作为一个例子,让我们看一下 angr 在内部使用的 SimProcedure 来运行full_init_stateLinux 程序的所有共享库初始值设定项:

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值