Manticore GUI插件开发:简化符号执行工具集成

本文详细介绍如何为Manticore符号执行引擎开发图形用户界面插件,重点讨论Ghidra插件的实现、gRPC服务器架构设计以及跨反汇编器功能开发流程,帮助降低符号执行技术的使用门槛。

Manticore GUI 开发变得简单

Trail of Bits 维护着 Manticore,这是一个可以分析智能合约和原生二进制的符号执行引擎。虽然符号执行是一种可以增强漏洞发现过程的强大技术,但它需要一些基础领域知识,因此有自己的学习曲线。考虑到用户与这种引擎交互的多种方式,以及需要在反汇编器和终端或脚本编辑器之间频繁切换上下文,将符号执行集成到工作流中对初学者来说可能令人生畏。

Trail of Bits 寻求简化这一过程的方法之一是为 Manticore 制作图形用户界面(GUI),这些界面嵌入到流行的交互式反汇编器中。去年夏天,前实习生 Alan Chang 开发了第一个这样的界面,即 Binary Ninja 的 Manticore 用户界面(MUI)插件。我们发现,将 Manticore 直接与交互式反汇编器配对,为漏洞研究人员提供了更便捷的方式来实际使用(并受益于)符号执行。因此,在我于 Trail of Bits 的冬季和夏季实习期间,我的目标是通过为 Ghidra 制作 MUI 插件并构建基础设施来帮助发展 MUI 生态系统,使这些插件更易于使用、维护和开发。

基于 gRPC 服务器的 MUI 插件架构

Ghidra 插件

我们认为,鼓励更多人使用 MUI 插件的最直接方法是简单地为更多种类的反汇编器开发 MUI 插件!因此,我在冬季实习期间开发了 Ghidra 版本的 MUI 插件;我选择 Ghidra 主要是因为它是流行的,并且与商业工具 Binary Ninja 不同,它是免费和开源的。此外,Trail of Bits 的一些内部项目已经在使用 Ghidra,因此我有充足的机会探索 Ghidra 插件开发。最后,通过开发 Ghidra 插件(一个用 Java 而不是 Python 编写的插件),我们可以开发一个不局限于单一编程语言的解决方案,获得可以指导未来插件开发的见解。

这个初始的 Ghidra 插件尽可能模仿了现有的 Binary Ninja 插件。虽然熟悉 Java Swing 和 Ghidra 的小部件花了一些时间,但一旦我开始,简单地模仿现有的视觉组件和用户界面是一个相当简单的任务。

然而,由于 Ghidra 插件将用 Java 编写,它不能依赖 Manticore Python 包或直接调用 Manticore 的 Python API。我们解决这个挑战的方法是使用一个名为 shiv 的工具,将 Manticore 库及其所有依赖项无缝捆绑到一个 Python zipapp 中。这样,我们可以创建一个“包含电池”的 Manticore 二进制文件,然后将 Binary Ninja 插件与 Manticore API 的交互转换为适当的命令行参数。然后,我们将这个二进制文件放在 Ghidra 的 os 目录的相关平台特定子目录中,这有助于跨平台支持。

在冬季实习结束时,我能够为 Ghidra 插件添加额外功能,例如除了具有专用输入字段的参数外,还能够指定任意的 Manticore 参数,并在同一 Ghidra 会话中支持多个 Manticore 实例。然而,这暴露了一个额外的问题。

功能对等和跨反汇编器开发

很快就显而易见,如果我们希望扩展 MUI 项目以支持更多反汇编器,我们的插件开发方法将不可持续。对于每个新的 MUI 功能,我们首先必须确定如何实现该功能,考虑插件与 Manticore 交互的方式(例如,通过直接调用 Manticore API 或通过 Manticore 的命令行界面选项)。此外,跨插件共享的某些前端信息(例如,固定的描述字符串或合理的默认选项)必须在每个实现中重复和标准化。

为了解决这个问题,我在夏季开发了一个用于 MUI 的集中式远程过程调用(RPC)服务器二进制文件。该服务器处理与功能齐全的 Manticore Python API 的所有交互,并通过协议缓冲区中定义的单个 RPC 处理 MUI 功能。我们选择使用 gRPC 作为我们的 RPC 框架,因为它的性能、广泛采用以及对跨多种编程语言的代码生成的强大支持。因此,未来的 MUI 插件可以轻松包含并依赖它们自己的 gRPC 生成的代码。

服务器用 Python 编写,使其能够访问 Manticore Python API 的全部功能,但被捆绑到一个可以用任何语言调用的 shiv 二进制文件中。这促进了一种新的客户端-服务器架构,允许开发人员实现任何后端 Manticore 功能并仅测试一次。前端反汇编器插件的开发人员只需几行简单的代码就可以向服务器发出 RPC 请求,这意味着他们在单个插件上的工作几乎可以完全专注于前端/UI 更改。

为了减轻处理固定字符串和其他在插件之间相同的前端信息的“琐事”,我们可以将这些数据存储在 JSON 文件中,这些文件与 MUI 插件发布一起打包,并在启动时加载。通过这种方式,我们可以标准化数据,例如用于启动 Manticore 执行的运行对话框的字段、字段描述符和默认值。

演示:为 MUI 开发功能

让我们看看为 MUI 开发功能的过程。假设我们想要在 Manticore 实例运行时启用手动状态管理。具体来说,我们希望能够执行以下操作:

  • 暂停一个状态并在稍后恢复它,如果我们在未来实现执行跟踪等功能,这将非常有用。
  • 自行终止一个状态。这样,如果一个状态绕过了避免钩子或陷入无限循环,我们将能够放弃它。

首先,我们将在协议缓冲区文件中定义一个新的 RPC 及其消息格式。服务器将接收状态的数字 ID、我们正在处理的 Manticore 实例,以及一个 StateAction 枚举,指示是恢复、暂停还是终止状态。

我们还需要更新一个现有的消息——ManticoreStateList。MUI 插件有状态列表,显示所有状态和每个状态的状态;这些列表通过 GetStateList RPC 更新。因为用户看到“暂停”状态与预先存在的状态状态不同是有益的,我们将在 RPC 的响应消息中添加一个新的 paused_states 字段,其中将包含用户暂停的状态列表。

这样,我们可以继续生成 Python 服务接口代码和 mypy 类型存根!在我夏季实习期间,我使用 just 命令运行器为开发人员抽象了这项工作,因此我们可以运行 just generate 命令来…只需生成所需的代码!

现在我们可以继续实现后端功能。这段代码包含在一个单独的 Servicer 类中,其中每个方法代表一个单独的 RPC。

我们将从验证 RPC 请求数据开始。虽然 gRPC 生成的代码可以检查提供给它的字段是否类型正确,但它不能强制使用任何字段或验证字段格式良好。因此,我们将编写自己的检查来断言请求的有效性;如果请求无效,我们还将设置错误代码和错误详细信息返回给请求者。

最后,我们可以通过直接访问 Manticore 的 Python API 来实现状态暂停和状态终止功能。这种直接访问提供了比通过 Manticore 的命令行选项交互更多的控制,例如,使我们能够创建一个虚拟的繁忙状态或放弃一个特定状态。如果一切顺利,前端插件将收到一个空响应,其中填充了默认的 OK 状态码。

我们方法的一个额外好处是,我们可以为我们的 RPC 编写测试,而无需处理生成服务器和连接到它。相反,我们可以简单地直接调用 Servicer 的方法并传递一个新的 Context 对象,我们可以在以后检查错误代码。

完成后,我们可以使用我们可靠的命令运行器来“just build”shiv 二进制文件,这将给我们一个新鲜的服务器二进制文件,我们可以在我们的插件中使用。

首先,我们需要生成 Java 服务接口代码,这将基于更新的协议缓冲区。gRPC 维护一个 Java 库 grpc-java,将为我们处理这一点。

然后,我们必须编写实际执行 RPC 的函数。在我们的插件中,我们将所有这样的“连接器”函数封装在一个文件中。在 Ghidra 插件中,编写连接器函数仅涉及三个步骤。首先,我们创建一个 StreamObserver 对象来异步处理 RPC 响应。在这种情况下,我们只需要实现处理错误情况的行为,因为成功的 ControlState RPC 的“结果”将通过 GetStateList RPC 对用户可用。然后我们构建 ControlStateRequest 对象,按要求填充字段。最后,我们实际上通过 gRPC 生成的 ManticoreServerStub 暴露的方法执行 RPC,它方便地为我们处理所有与服务器的通信。

唯一剩下要做的就是进行适当的 UI 更改!对于这个功能,我们可以简单地通过在状态列表中右键单击状态来打开上下文菜单,然后用调用 controlState 方法的操作填充状态!

随着 Manticore 功能由 gRPC 服务器处理,我们必须在 Ghidra 和 Binary Ninja 插件的“前端”实现的 UI 更改相当简单。

演示中讨论的“假设”状态管理功能实际上已经实现,并现在是 MUI 的一部分!如果你有兴趣查看所有更改和提交,请查看服务器和 Ghidra 插件的拉取请求。(这些拉取请求是在代码位置重构之前进行的。代码现在位于主 Manticore 存储库的服务器目录下。)

快速分步指南

总结一下,向 MUI 添加新功能的步骤如下:

  1. 从修改 MUI 服务器二进制文件开始。
  2. 如果需要新的 RPC 或对现有消息的修改,编辑协议缓冲区(ManticoreServer.proto 文件)。
  3. 通过运行 just generate 命令为服务器脚本生成服务接口代码。
  4. 通过服务器脚本(manticore_server.py)添加功能和请求验证代码,通过它可以直接与 Manticore API 交互。
  5. 在适用的情况下,为新功能编写测试。
  6. 通过运行 just build 命令构建 shiv 服务器可执行文件。

然后,对于每个前端插件:

  1. 使用/复制新的服务器二进制文件;
  2. 生成插件所用编程语言的服务接口代码;和
  3. 进行任何相关的前端更改(并在适用的情况下,作为 MUI 公共资源的一部分共享标准化数据)。

结论

我在 Trail of Bits 的实习非常充实,我为 Manticore 用户界面(MUI)项目的进展感到自豪。在我前几次尝试在夺旗挑战中应用符号执行时,曾 struggled with the seemingly black-box nature of symbolic execution,我确信 MUI 项目将使符号执行对初学者更易访问。此外,与成熟的交互式反汇编器的集成将使符号执行成为漏洞发现过程中更自然的一部分。

我也对 MUI 开发体验的进展感到满意。插件开发并不是最顺畅的体验,部分原因是面向用户的插件安装过程不是为快速原型设计或增量更改而设计的。因此,花费我的冬季实习从头开始构建 Ghidra 插件是一次令人 harrowing 的经历。除了熟悉 Java 和掌握一个新的插件开发框架(两者都有自己的学习曲线)之外,我花了很多时间思考添加某个功能是否甚至可能!有了新的 MUI 服务器架构,我现在能够更高效地利用那段时间,只思考新功能如何有助于漏洞发现过程。

除了使开发成为一个更省时的过程之外,新的 MUI 服务器架构还提供了以开发人员为中心的功能,使其更加顺畅。这些包括 just 脚本、Gradle 方法和单元测试。在以前的项目中,我将这些功能的实现视为一种琐事;即使在我的实习期间,我也只是在导师 Eric Kilmer 的一些推动和指导下才开始将它们添加到新服务器中。然而,那项工作在我的开发速度、代码质量以及调试时的挫败感水平上产生了天壤之别!

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计