跳转至

开发者指南

环境

有关所需版本和下载链接的具体信息,请参阅 README.md 文件。

快速入门

按照 README.md 文件中的高级开发说明,即可快速搭建你的开发环境。

在开发 Ghidra 时,请尽可能坚持使用 Apache License 2.0 许可证。有时可能有必要将其他兼容许可证并入 Ghidra。任何 GPL 代码都必须位于顶层 GPL/ 目录中,作为一个完全独立、可独立构建的 Ghidra 模块。

如果你为 Ghidra 项目贡献代码,获得署名/认可的首选方式是通过 Git 提交作者信息。请确保你的 Git 凭据已正确关联到你的 GitHub 账户,以便你在 GitHub 上显示为 Ghidra 贡献者。我们没有将作者姓名直接放入源代码的标准,因此不鼓励这样做。

常用 Gradle 任务

下载非 Maven Central 依赖。这会在仓库根目录创建一个 dependencies 目录。

gradle -I gradle/support/fetchDependencies.gradle

下载 Maven Central 依赖并设置仓库以进行开发。默认情况下,这些依赖将存储在 $HOME/.gradle/。

gradle prepdev

清理仓库构建文件。在少数情况下,git pull 后可能需要执行此操作以修复不明原因的编译错误。

gradle clean

生成嵌套的 Eclipse 项目文件,之后可将这些文件作为“现有项目”导入 Eclipse。

gradle cleanEclipse eclipse

为当前平台构建原生组件。需要存在原生工具链。

gradle buildNatives

手动编译 sleigh 文件。必要时 Ghidra 也会在运行时执行此操作。

gradle sleighCompile

构建 Javadoc:

gradle createJavadocs

为 PyGhidra 和调试器构建 Python3 包:

gradle buildPyPackage

以未压缩形式将 Ghidra 构建到 build/dist。这将是一个仅旨在所构建平台上运行的发行版。

gradle assembleAll

以压缩形式将 Ghidra 构建到 build/dist。这将是一个仅旨在所构建平台上运行的发行版。

gradle buildGhidra

提示: 你可能希望跳过某些 Gradle 任务以加快构建速度,或稍后再处理某个问题。例如,也许你添加了一些新的源文件,但构建因未解决的 IP 头问题而失败。你可以使用 Gradle 的 -x <task> 命令行参数来阻止特定任务运行:

gradle buildGhidra -x ip

PyGhidra 开发

开发和调试 PyGhidra 的受支持方式是使用 Eclipse 的 PyDev 插件。当 PyDev 安装并配置完成后,将出现多个新的 Eclipse 运行配置,从而支持在 GUI 和 解释器 模式下运行和调试 PyGhidra。

要为开发和/或调试准备 PyGhidra,请首先执行以下 Gradle 任务:

gradle prepPyGhidra
这会在 build/venv/ 下设置一个 Python 虚拟环境,并向其中安装一个可编辑的 PyGhidra 模块(及其依赖)。应将 PyDev 指向此虚拟环境,以便其能够访问可编辑的 PyGhidra 模块以及类型/桩信息。在 Eclipse(已安装 PyDev)中:

  1. 设置 -> PyDev -> 解释器 -> Python 解释器
  2. 点击 新建...
  3. 点击 浏览 python/pypy exe
  4. 选择 build/venv/bin/python3
  5. 为 解释器名称 输入一个值
  6. 勾选 全选 并点击 确定
  7. 点击 预定义 选项卡,然后点击 新建...
  8. 选择 build/typestubs/pypredef
  9. 点击 应用并关闭

GhidraDev Eclipse 插件开发

开发 GhidraDev Eclipse 插件需要 Eclipse PDE(插件开发环境),可通过 Eclipse 市场安装。它也包含在 Eclipse IDE for RCP and RAP Developers 中。要生成 GhidraDev Eclipse 项目并准备必要的依赖,请执行:

gradle prepGhidraDev eclipse -PeclipsePDE

将新生成的 GhidraDev 项目导入到支持此类项目的 Eclipse 中。

注意: 如果遇到与 PyDev 和 CDT 相关的编译错误,请进入 Eclipse 的首选项,在 目标平台 下激活 /Eclipse GhidraDevPlugin/GhidraDev.target。

有关如何构建 GhidraDev 插件的说明,请参阅构建 GhidraDev。

离线开发环境

有时你可能希望将 Ghidra 仓库移动到离线网络并在那里进行开发。以下是推荐的步骤,以确保你不仅移动源代码仓库,还一并移动所有已下载的依赖:

  1. gradle -I gradle/support/fetchDependencies.gradle
  2. gradle -g dependencies/gradle prepdev
  3. 将 ghidra 目录移动到不同的系统
  4. gradle -g dependencies/gradle buildGhidra(在离线系统上)

注意:-g 标志指定 Gradle 用户主目录。默认是用户主目录下的 .gradle 目录。将其覆盖为 Ghidra 仓库内部,可确保在 prepdev 任务期间获取的所有 Maven Central 依赖都会随仓库的其余部分一起移动。

运行测试

要运行单元测试,请执行:

gradle unitTestReport

对于更复杂的集成测试,请执行:

gradle integrationTest

要同时运行单元测试和集成测试并生成报告,请执行:

gradle combinedTestReport

在 CI 中设置构建

要在 Linux 上的无头模式、CI 环境或 Docker 中运行测试,请先执行:

Xvfb :99 -nolisten tcp &
export DISPLAY=:99
这是为了让 AWT 正常工作所必需的。

在干净的用户环境中,GUI 启动也可能会在 FrontEnd 可用之前,阻塞在首次运行的用户协议对话框上。对于 CI 中的非交互式 GUI 启动,请设置:

export JAVA_TOOL_OPTIONS="-DUSER_AGREEMENT=ACCEPT"
这允许基于 GUI 的自动化避免阻塞在初始协议提示上。

构建支持数据

Ghidra 的某些功能需要整理相当庞大的数据库。这包括数据类型档案和函数 ID 数据库,两者都需要为相关的 SDK 和平台收集头文件和库。这项工作大部分是手工完成的。我们官方构建中包含的档案可以在 ghidra-data 仓库中找到。

构建数据类型档案

这项任务通常通过 Ghidra GUI 手动完成,我们官方构建中包含的档案需要相当多的微调。 1. 在 CodeBrowser 中,选择 File -> Parse C Source 2. 在这里你可以创建和配置解析配置文件,其中列出了头文件和预处理器选项。 3. 点击 Parse to File 来创建数据类型档案。 4. 通过将结果复制到 Ghidra/Features/Base/data/typeinfo,可以将其添加到安装目录或源代码树中。

构建 FID 数据库

这项任务通常通过 Ghidra GUI 手动完成,我们官方构建中包含的档案需要相当多的微调。你首先需要导入相关库,并从中生成 FID 数据库。这通常是一组来自某个 SDK 的库。我们在官方构建中包含了多种 Visual Studio 平台。官方的 .fidb 文件可以在 ghidra-data 仓库中找到。

  1. 在 CodeBrowser 中,选择 File -> Configure
  2. 启用 “Function ID” 插件,然后关闭对话框。
  3. 在 CodeBrowser 中,选择 Tools -> Function ID -> Create new empty FidDb。
  4. 选择一个目标文件。
  5. 从程序中选择 Tools -> Function ID -> Populate FidDb。
  6. 适当地填写选项,然后点击 OK。

如果你想了解我们微调的一些细节,请查看 building_fid.txt。

调试器开发

我们最近更改了调试器的后端架构。我们不再使用 JNA 来访问原生调试器 API。我们仅在伪终端访问时使用它。取而代之,我们使用 Python3 和基于 protobuf 的 TCP 连接进行后端集成。

附加依赖项

除了 Ghidra 的正常依赖项之外,你可能还需要以下内容:

  • 用于 Windows x64 的 WinDbg
  • 用于 Linux 的 GDB 13 或更高版本
  • 用于 macOS 的 LLDB 10 或更高版本

其他的(例如 JNA)由 Gradle 通过 Maven Central 处理。

架构概述

有几个 Eclipse 项目,每个都契合更大的架构图景。它们目前都位于 Ghidra/Debug 目录中,但之后可能会被重构到 Framework 和 Feature 目录中。以下按“自底向上”的顺序列出每个项目,并附有简要说明和状态。

  • ProposedUtils - 提议移动到其他相应项目的工具集合。
  • AnnotationValidator - 用于数据库访问对象的实验性注解处理器。
  • Framework-TraceModeling - 用于随时间存储机器状态的数据库模式和一组接口。
  • Framework-AsyncComm - 用于异步通信的工具集合(数据包格式和 CompletableFuture 便利功能)。
  • Debugger-api - 用于与调试器 UI 交互的接口。
  • Debugger - 构成调试器 UI 实现的 Ghidra 插件和服务集合。
  • Debugger-isf - 通过 ISF 提供对 Ghidra DataTypes 访问的服务。
  • Debugger-rmi-trace - 用于 Trace RMI(新的后端架构)的线上协议、客户端、服务和 UI 组件。
  • Debugger-agent-dbgeng - 在 Windows x64 上用于 WinDbg 的连接器(通过 dbgeng.dll 和 dbgmodel.dll)。
  • Debugger-agent-gdb - 在 UNIX 和 Windows 上用于 GDB(建议 13 或更高版本)的连接器。
  • Debugger-agent-lldb - 在 macOS、UNIX 和 Windows 上用于 LLDB(建议 10 或更高版本)的连接器。
  • Debugger-jpda - 正在开发中的连接器,用于通过 JDI(即 JDWP)进行 Java 和 Dalvik 调试。此连接器已弃用,且尚未被替代。

Trace Modeling 模式会随时间记录机器状态和标记。它与 Programs 使用相同的数据库框架,因此,如果需要,可以将 trace 记录存储在 Ghidra 项目中,并通过服务器共享。Trace “记录” 是在 Ghidra 的 UI 中显示信息的实际要求。后端连接器通过使用 Trace RMI 对记录内容拥有完全决定权。通常,只有用户(或脚本)实际观察到的机器状态才会被记录。对于大多数用例,Trace 是小型且临时的,仅用于在 UI 组件和目标模型之间进行中介。除了跟踪活动线程、已加载模块、断点等之外,它还支持许多与 Programs 相同的标记(例如反汇编、数据类型)。

每个后端(或 “适配器” “连接器” “代理”)都使用 Trace RMI 客户端来填充 trace 数据库。作为 Ghidra 中的一般规则,任何组件都不允许访问原生 API 并与 Ghidra UI 位于同一 JVM 中。这样我们可以控制崩溃,防止数据丢失。为了满足这一要求——考虑到调试原生应用几乎肯定需要访问原生 API——我们开发了 Trace RMI 协议。这也使我们能够更好地弥合 Java 和 Python 之间的语言鸿沟,而大多数原生调试器都支持 Python。该协议与 Framework-TraceModeling 松散耦合,本质上通过 RMI 暴露其方法,以及一些用于控制 UI 的方法。该协议使用 Google 的 Protobuf 库构建,为用其他语言实现后端提供了潜在路径。我们提供 Trace RMI 服务器作为用 Java 实现的 Ghidra 组件,并提供 Trace RMI 客户端作为 Python3 包。客户端也有 Java 版本,但严重依赖 Ghidra 的代码库。后端实现可以是访问原生调试器 API 的独立可执行文件或脚本,也可以是原生调试器的脚本或插件。然后它通过 Trace RMI 连接到 Ghidra,用从该 API 收集的信息填充 trace 数据库。它应该提供一组诊断命令来控制并监视该连接。它还应该使用原生 API 来检测会话和目标变化,以便 Ghidra 的 UI 始终如一地反映调试会话。发现会话中的目标并将它们映射到具有适当 Ghidra 语言的 trace 中,是后端的责任。通常,它会检查目标的架构,并在连接时立即创建 trace。

开发新连接器

那么,Ghidra 尚不支持你最喜欢的调试器? 我们相信新系统比旧系统要容易得多。 不过,请先读完本指南,并仔细查看我们已有的连接器,也可以询问我们是否已在开发某个连接器。 当然,随着时间推移,你也可以上网搜索是否有人在开发其他连接器。 这里有不少注意事项和陷阱,最值得注意的是这个接口仍在变动之中。 当问题出现时,可能的原因包括但不限于:

  1. 你方的错误
  2. 我方的错误
  3. 接口设计缺陷
  4. 你所适配的调试器/API 中的错误

我们仍在(是的,仍然在)编写这份文档。 在此期间,我们建议以 GDB 和 dbgeng 代理作为示例。 务必查看 Python 代码 src/main/py! Eclipse 不太容易直接展示这些代码。

你还需要提供启动器(launcher),以便 Ghidra 知道如何配置并启动你的连接器。 这些只是 shell 脚本。 我们在 Linux 和 macOS 上使用 bash 脚本,在 Windows 上使用批处理文件。 启动器的理想目标是:在一次性配置之后,用户只需单击即可启动并开始调试。 尽量包含对调试器而言有意义的尽可能多的常见用例。 这为用户提供了最大的灵活性,也为可能创建衍生启动器的高级用户提供了示例。 请查看现有的启动器以获取示例。

对于测试,请遵循 GDB 的示例。 我们不再提供规定需求的抽象类。 相反,我们仅将 GDB 作为示例或模板。 通常,我们将测试分为三类:

  • 命令(Commands)
  • 方法(Methods)
  • 钩子(Hooks)

命令测试检查用户 CLI 命令(通常实现在 commands.py 中)是否正常工作。 一般来说,进行最小的连接设置,执行命令,并检查它是否产生预期输出并引起预期效果。

方法测试检查远程方法(通常实现在 methods.py 中)是否正常工作。 许多方法只是 CLI 命令的包装器,其中一些由原生调试器提供,另一些由 commands.py 提供。 这些测试与命令测试类似,只不过它们调用方法而不是执行命令。 检查返回值(很少适用)以及它是否引起预期效果。

钩子测试检查后端是否能够监听会话和目标变化,例如知道目标何时停止。 测试不应通过执行本应由钩子触发的命令或调用方法来“作弊”。 它应执行最少的命令来设置测试,然后触发一个事件。 然后应检查该事件是否反过来触发了预期效果,例如在目标停止时更新 PC。

每当你对 Python 代码进行更改时,都需要重新组装包的源代码。

gradle assemblePyPackage

这是必需的,以防你的包包含生成的源代码,例如 Debugger-rmi-trace 就是这种情况。 如果你想为你的连接器创建一个新的 Ghidra 模块(推荐),请使用现有模块的 build.gradle 作为模板。 关键部分是应用 hasPythonPackage.gradle 脚本。

添加新平台

如果对于目标平台,某个合适的调试器已存在连接器,那么添加该平台可能非常简单。 例如,GDB 支持许多平台,因此尽管我们目前专注于 x86-64(并在一定程度上支持 arm64),我们已为许多平台提供了映射。 这些映射通常保存在每个连接器的 arch.py 文件中。

一般来说,要更新 arch.py,你需要知道:

  1. 调试器如何称呼该平台(包括变体名称)
  2. Ghidra 如何称呼该处理器语言
  3. 如果适用,目标地址空间到 Ghidra 地址空间的映射
  4. 如果适用,目标寄存器名称到 Ghidra 处理器语言中寄存器名称的映射

在大多数情况下,(3) 和 (4) 已由附带的映射器实现。 自然,你需要测试特殊情况,最好通过自动化测试。

仿真

对于第三方仿真器,最明显的集成方式是编写一个“连接器”。 然而,p-code 仿真是 Ghidra UI 的一项核心功能,并且它有一个相当易用的 API。 也就是说,它可以在 trace 中记录的机器状态之间进行插值,并外推未来的机器状态。 集成此类仿真器对你可能仍然有用,但我们建议在着手集成其他仿真器之前,先尝试 p-code 仿真器,看看它是否满足你在 Ghidra 中进行仿真的需求。 我们还通过 GDB 提供了开箱即用的 QEMU 集成。

贡献

在提交帮助工单和拉取请求时,请将那些与调试器相关的标记为“Debugger”,以便我们能更快地进行分类处理。

故障排除与帮助

Eclipse 问题

在拉取或同步最新的 Ghidra 源代码仓库后,你可能会在 Eclipse 中遇到以下问题:

  • 问题: 我遇到了不知道如何处理的 Eclipse 编译错误……我放弃了!
  • 解决方案:
    • 在 Eclipse 中,通过点击该框架中的 ⊟ 图标,折叠 Package Explorer 或 Project Explorer 中的所有项目
    • 在 Package Explorer 或 Project Explorer 中,找到带有小 ? 图标的项目(这些项目不应再受源代码控制)
    • 仅右键单击__它们__,然后点击 删除。
    • 勾选“删除磁盘上的项目内容”复选框。
    • 点击 确定(确认 git 中没有需要删除的新的未暂存文件)
    • 选择 Package Explorer 或 Project Explorer 中的所有项目
    • 右键单击它们,然后点击 删除(如果项目未折叠,此操作可能无效)
    • 保持“删除磁盘上的项目内容”复选框__未选中__
    • 点击 确定。 现在你应该有一个空的 Package Explorer 或 Project Explorer。
    • gradle -I gradle/support/fetchDependencies.gradle
    • gradle prepdev cleanEclipse eclipse buildNatives
    • 在 Eclipse 中,选择 文件 -> 导入...
    • 常规 | 将现有项目导入工作区
    • 选择根目录为你下载或克隆的 ghidra 源代码仓库
    • 勾选“搜索嵌套项目”
    • 点击 完成

这应该能让 Eclipse 恢复到全新状态。永远不应该需要重新克隆该仓库。

  • 问题: 缺少 Ghidra 运行配置(启动器)。
  • 解决方案: Ghidra 运行配置保存在源代码控制中,位于各个模块的 .launch/ 目录(例如 Ghidra/Features/Base/.launch/)。只要将相应的模块项目导入 Eclipse(例如 Features Base),运行配置就应该可以在 Eclipse 的 Run -> Run Configurations 下找到。如果它们不在那里,并且项目已导入,请尝试关闭并重新打开 Eclipse。

    NOTE: 有时你需要通过 Run -> Run Configurations... 窗口启动一次 Ghidra,运行配置才会显示在主 Eclipse 按钮栏的收藏夹菜单下。

    NOTE: 切勿通过手动导入来修复缺失的运行配置,例如通过 File -> Import... -> Run/Debug -> Launch Configurations. 这会回避真正的问题,并且最终不可避免地导致某天出现重复的运行配置,从而造成额外混乱。

已知问题

  • Gradle 中存在一个已知问题,当使用非英语系统区域设置时,它可能无法在 Linux 上发现本机工具链。作为解决方法,在运行你的 Gradle 任务之前,请设置以下环境变量:LC_MESSAGES=en_US.UTF-8
  • 如果 Ghidra 构建仅找到无法访问 pip 的 Python 版本,则可能需要从 Python 虚拟环境 执行构建。

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 gh/ghidra