DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试

新闻资讯2026-04-20 23:19:16

调试 是软件分析与逆向工程中 的核心技术。 通过调试,我们可以动态检查程序执行时的内部状态,获取静态分析无法提供的深入洞察。 调试允许我们逐步执行代码、监控变量值并观察控制流,从而识别和修复那些通过静态分析难以发现的错误。 当调试与静态分析结合使用时,不仅能加速分析过程,还能加深我们对程序运行时行为的理解。 这种组合提供了更全面的软件行为视图,使我们能够发现隐藏的错误、安全漏洞和意外功能。 本章将探索 Ghidra 这款综合软件逆向工程套件强大的调试功能。 我们将学习如何利用 Ghidra 的功能,如断点、内存检查和反汇编,来更有效地剖析和分析软件,最终提升我们理解和修改复杂 软件系统的能力。

本章涵盖 以下主题:

  • Ghidra 调试器概述
  • 启动 Ghidra 调试器
  • 调试器窗口 和工具栏
  • 执行 流程控制
  • 调试 simple_encoder.exe 应用程序
  • 远程调试

本章的技术要求 如下:

  • Ghidra 11.2 版本: https://github.com/NationalSecurityAgency/ghidra/releases/tag/Ghidra_11.2_build
  • 安装在您选择的虚拟机上的 Linux 系统,或 WSL: Kali WSL | Kali Linux Documentation

Ghidra 的调试功能是其套件中不可或缺的组成部分,提供了一套强大的工具集以支持动态分析与逆向工程。 Ghidra 的调试框架基于 Ghidra 异步调试协议  GADP )构建,该协议作为 Ghidra 与各类调试后端之间的通用桥梁。 GADP 的核心功能是通过基于 JSON 的异步消息协议(通常不允许包含崩溃)为原生 API 交互提供通信通道,从而实现与主流调试器后端的集成,包括 Windows 调试器( dbgeng.dll )、 GDB  GNU 调试器 ) 以及 LLDB。

Ghidra 调试器的核心功能包括 :

  • 内存读写
  • 寄存器访问与修改
  • 断点管理
  • 堆栈 跟踪检索
  • 线程查看 与控制
  • 进程附加/分离

Ghidra 支持本地和远程调试场景。 远程调试功能允许分析人员调试运行在不同机器甚至不同操作系统上的应用程序,从而扩展了分析范围。 通过利用 Ghidra 的远程调试,用户可以设置断点、检查内存并评估程序状态, 而无需物理存在于目标系统上。 这对于分析在隔离或受控环境中运行的恶意软件或软件特别有用, 因为这些环境可能限制直接访问。

在接下来的章节中,我们将深入探讨 Ghidra 调试器,学习如何配置和使用它,设置远程调试会话,并有效利用这些工具来更深入地理解 软件行为。

在 Windows 系统上,我们将 使用 Windows 调试器 ( dbgeng.dll ),这也是 WinDbg 使用的后端引擎。 为此,让我们创建一个简单的程序,用于编码给定字符串并打印编码后的版本。 我们将 在本章尝试调试此应用程序,以了解 调试的各个概念。

以下是一个简单字符串编码器 程序( simple_encoder.exe )的代码:

#include <stdio.h>
#include <string.h>
void simple_encoder(char *input_string, char *encoded_string) {
    int val;
  __asm__ __volatile__(
        ".intel_syntax noprefix;"
        "push 1;"
        "pop rax;"
        ".att_syntax prefix;"
        : "=r" (val)
    );
    for (int i = 0; i < strlen(input_string); i++) {
        encoded_string[i] = input_string[i] + val;
    }
    encoded_string[strlen(input_string)] = '0';
}
int main() {
    char input_string[] = "Hello, world!";
    char encoded_string[100] = "";
    simple_encoder(input_string, encoded_string);
    printf("Encoded string: %s
", encoded_string);
    return 0;
}

前面的代码只是将给定字符串的每个字符递增 1,但它故意 通过使用 汇编指令来掩盖这一事实。

使用 以下命令编译前面的代码:

gcc simple_encoder.c -O0 -o simple_encoder.exe

让我们在Ghidra中创建一个新项目,并将simple_encoder.exe导入到该项目中。导入完成后,将simple_encoder.exe拖到工具箱中的调试器图标( DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第1张)上,如下图所示:

DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第2张

图 16.1 – 导入 simple_encoder.exe 并启动调试器

这将打开调试器窗口。如果系统提示您分析程序,请选择“是”。下一步是启动可执行文件。这将在调试器窗口中填充所有必要的对象和详细信息,我们将在后面的章节中进行探讨。

在启动调试器之前, 请使用以下命令安装以下 Python 包。 该启动器是基于 dbgeng.dll 实现的 Python 控制台调试器。 :

pip3 install pybag protobuf==3.20.3

现在,从菜单栏中选择 调试器 配置并使用...启动 simple_encoder.exe dbgeng :

DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第3张

图 16.2 – 使用 dbgeng 调试器启动 simple_encoder.exe

在接下来出现的对话框中, 请确保在 Python 命令 字段中 选择之前安装 Python 包时使用的正确 Python 解释器:

 DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第4张

图 16.3 – dbgeng 的调试器配置对话框

点击对话框中的 启动 按钮 ,等待调试服务器启动并加载 simple_encoder.exe 进程的反汇编代码至 动态列表 窗口(标题为 动态 ),如 下图所示:

 DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第5张

图 16.4 - 调试器中加载并暂停的 simple_encoder.exe 进程

此时,进程 已被加载到调试器中并处于暂停状态,以便我们决定后续操作方式。 但在继续调试之前,我们先来了解各个调试器窗口中的显示内容、调试工具栏的功能, 以及调试会话管理的基本概念。

调试器窗口 由以下 堆叠窗口组成:

  • 连接 :此处列出 活动的调试服务器会话或连接。 您可在此建立新会话或终止 现有连接:

 DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第6张

图16.5 – 显示活动连接的连接窗口

  • 调试控制台 :此处列出了整个工具中的问题、诊断信息、进度和建议。 部分问题会附带修复措施,可能有助于加快工作流程或进行故障排除。 在我们的案例中,它提示 项目中未找到模块 ntdll.dll 存在问题,但我们可以忽略此消息,因为这不是我们 需要调试的模块:

 DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第7张

图16.6 - 显示警告和问题的调试控制台窗口 

动态分析:这是检查正在执行的指令的主要方法。默认情况下,它会跟随程序计数器,并从程序计数器的当前位置反汇编,直到下一个控制转移指令。它支持与静态列表(静态窗口)相同的许多操作,包括补丁。顶部的选项卡列出了活动的跟踪。带有记录图标(DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第8张)的跟踪代表实时目标。 

DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第9张

图16.7 - 显示当前程序计数器处反汇编代码的动态窗口

  • 模块 :此处显示 目标进程加载的可执行映像(及适用情况下的节区)。 您还可以看到 simple_encoder.exe 被加载在 此窗口中:

 DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第10张

图16.8 - 显示当前已加载模块的模块窗口

  • 寄存器 :此窗口显示并允许编辑 当前线程 的硬件寄存器值

 DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第11张

图16.9 - 显示处理器寄存器的寄存器窗口

  • 断点 :此处列出 并管理所有打开的程序数据库及 运行目标的断点:

 DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第12张

图16.10 - 显示活动断点的断点窗口

  • 模型 :后端调试器会在跟踪数据库中填充一个对象模型,该模型会馈送到各个窗口,例如 线程  模块 和 区域 。 此窗口显示该模型并允许对对象执行通用操作。 它通常比 GUI 的其他部分功能更强大,但集成度较低,且不如 终端 强大。

 DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第13张

图16.11 - 显示后端调试器提供的调试数据模型的模型窗口

  • 堆栈 :此处列出了当前线程的堆栈帧。 与其他调试器(如 x64Dbg 或 IDA)略有不同,它们的堆栈窗口显示的是实际的堆栈内存区域,而此处显示的是调用堆栈( 又称 堆栈跟踪 ):

 DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第14张

图16.12 – 显示调用堆栈的堆栈窗口

  • 内存区域 :此处列出了当前目标的内存区域。 它与 模块 窗口的区别在于,它不仅包含映像支持的区域,还包括其他内存区域,如堆栈 和堆:

 DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第15张

图 16.13 - 显示当前进程所有内存区域的"Regions"窗口

  • 控制台  控制台 窗口是一个通用的消息输出组件。 任何插件都可以通过控制台服务向控制台发送消息。 它主要用于显示 脚本的输出:

 DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第16张

图16.14 – 控制台 – 脚本窗口

  • 监视(Watches) :此功能管理当前监视项。 这些并非断点,而是我们需要监控其值的表达式或变量。 本章后续将使用 监视(Watches) 窗口来观察程序在 编码过程中 如何修改字符串。

 DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第17张

图16.15 - 显示当前活动监视表达式的监视窗口

  • 线程(Threads) :此功能列出当前目标中的 所有线程:

 DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第18张

图16.16 - 显示当前进程中所有活动线程的线程窗口

  • 时间 :此处列出了针对 当前目标 所记录的事件和快照

 DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第19张

图16.17 - 时间窗口

  • 终端 :这是一个终端模拟器,为后端调试器和/或目标 I/O 提供命令行界面。 它适用于诊断或执行那些在图形用户界面中没有对应按钮的命令。 有些人可能更倾向于在此处而非 图形用户界面中 操作调试器。

 DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第20张

图16.18 - 显示集成后端调试器输出的终端窗口

在工具栏中,您会看到一些专门与调试相关的额外按钮:

 DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第21张

让我们了解这些按钮各自的用途: 

DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第22张启动:此操作使用合适的后端调试器启动当前程序(来自静态列表)。下拉菜单提供了一个先前使用过的启动器的选择以及所有可用启动器的子菜单,如图 16.2 所示。单击此按钮将使用最近的配置,无论它是否成功启动了可执行文件。
DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第23张模拟:这会将当前程序(来自静态列表)加载到模拟器中。模拟与调试不同,我们将在本章中不介绍它。
DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第24张控制模式:此下拉菜单设置控件和机器状态编辑的模式。默认情况下,所有操作都定向到后端调试器。
DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第25张恢复:这会恢复暂停程序的执行。此按钮将执行程序,直到遇到诸如断点或异常之类的事件。如果没有事件,这将完成程序的执行并将其带到最终状态。
DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第26张中断:这等效于 Ctrl + C。如果您想中断正常执行并想开始调试,这很重要。
DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第27张终止:这将终止或停止当前程序的执行。
DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第28张断开连接:这会与后端调试器断开连接。通常,这也会结束会话。
DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第29张步入:这将精确地执行 EIP / RIP 寄存器(也称为程序计数器)指向的地址处的一条指令,并再次暂停程序。
DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第30张步过:这与“步入”类似,除非下一条指令是 CALL 指令。在 CALL 指令的情况下,它将执行 CALL 指令指向的整个函数(子例程),并尝试在 CALL 指令之后的下一条指令处中断执行。当我们不想执行来自库函数(如 strlen)的指令或您已经知道其行为并且不想调试的调用时,这很重要。但是,不能保证程序会到达 CALL 之后的下一条指令,并且程序执行可能会完成。
DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第31张跳出:当我们已经在子例程中并且想要跳出当前子例程并在调用此子例程之后在下一条指令处中断时,可以使用此选项。与“步过”类似,不能保证程序计数器会到达目标下一条指令,并且整个执行可能已经在到达目标停止地址之前完成。

在本节中,我们探索了 Ghidra 调试器界面,并学习了工具栏中的调试工具。 这对于在调试过程中有效导航和控制程序执行流程至关重要。 下一节我们将讲解如何运用这些工具和界面,以最适合我们调试需求的方式控制执行流程。

本节将学习如何控制程序执行流程,包括选择合适的单步执行机制和设置断点。 内容涵盖如何确定断点位置以及选择使用何种类型的断点。

当您使用单步进入 / 单步跳过 / 单步跳出按钮时,您会注意到动态窗口每次按下按钮都会前进一条指令(除非是 CALL 指令)。 此外, 静态列表会随动态列表同步移动。 您可以在任一列表中进行导航,但为了简单和熟悉起见,我们将在调试时使用静态列表在程序中进行导航。 您也可以像在 CodeBrowser 中一样打开 Decompiler 窗口, 它会保持同步显示当前正在执行的具体代码行。 由于单个反编译行可能由多条汇编指令组成,有时您会注意到多条汇编指令被执行,但焦点仍停留在 Decompiler 窗口中的同一行上。

单步执行让我们了解代码中特定行在运行时的行为,当我们想要理解使用了哪些值以及这些值 如何被操作时,它也非常重要。

使用单步执行控制程序流程仅适用于您已处于程序中目标位置的情况,例如位于 main() 函数中——这是开发者编写应用程序核心逻辑的地方。 在 main() 函数之前会有数百万条指令,这些指令负责将可执行文件加载到内存并为其执行做好准备,之后才会开始运行 用户的实际代码。 要求任何人通过点击 单步跳过 或 单步进入 按钮数百万次来抵达目标代码显然是不现实的。 正因如此,我们使用断点功能:您可以在目标位置设置断点,当程序执行到 该位置 时就会自动暂停。

使用断点是调试过程中最重要的执行控制技术之一。 您可以在 需要暂停调试器执行的任意位置设置断点。 例如,您可能希望查看变量状态或 检查特定位置的调用堆栈。 为此,您需要指示调试器中断或暂停执行,以便有机会检查代码在 该点的运行时细节。

断点通常通过 两种方式实现:

  • 软件断点 :通过向目标内存中植入特殊指令来暂停程序执行。 例如,可以在 CALL 指令上设置断点,该指令的操作码通常以 0xE8 开头,但设置断点时会被替换为 0xCC ,这是 INT3 指令的操作码,也被称为 中断 或 陷阱 指令。 这种内存修改是内部操作,在反汇编窗口( 静态列表 或 动态列表 )中不可见。 当程序尝试执行 INT3 指令时,将触发异常。 调试器已设置处理程序来捕获此异常,在该处理程序中,它会将修补的操作码字节替换回原始的 0xE8 并暂停程序执行。 在 Ghidra 中,此类断点通过 SW_EXECUTE 标识 .
  • 硬件断点 :顾名思义,这类断点利用处理器提供的硬件特性实现。 具体来说,它们使用 x86 处理器提供的调试寄存器来支持这种断点,因此也具有平台特定性。 从 DR0 到 DR7 共有八个调试寄存器,但只有 DR0 至 DR3 能存储执行中断的目标地址。 这意味着任何时候我们最多只能设置四个硬件断点地址。 DR6 用于调试状态,帮助确定发生的调试条件,而 DR7 则是一个重要的寄存器,存储着断点类型的信息。 它可以是 执行 断点,在定义地址的指令执行时中断;也可以是 读取 或 写入 断点,当处理器尝试从断点地址读取或写入时触发。 它还保存着已定义断点的读写大小。 在 Ghidra 中,硬件执行断点由 HW_EXECUTE 表示,访问断点则由 HW_READ 和 HW_WRITE 表示。 .

你可能已经注意到我们只能使用四个硬件断点,而软件断点则没有这种限制,那么为什么我们还需要使用硬件断点呢? 请记住,我们无法使用软件断点在特定内存地址的读取或写入操作上中断,这正是硬件断点发挥作用的地方。 此外还有 内存断点的概念,它们通过修改 页面保护设置来工作,但仍然无法在从特定地址读取或写入特定字节大小时提供精准的中断控制。 因此,另一个问题是 硬件断点是否仅对特定内存地址的读写访问中断有用? 并非如此,许多软件程序(例如采用数字版权管理 DRM)保护的程序)能够检测程序的运行时完整性。由于软件断点通过修改字节实现断点功能,这会破坏程序在内存中的完整性,DRM 可以检测到这种破坏并终止程序执行。 这时硬件断点就能派上用场。 但需注意,DRM 还采用了其他多种复杂方法来防止程序被篡改,因此仅仅将软件断点切换为硬件断点并不能帮助你调试这类应用程序。

让我们深入探讨 Ghidra 调试器如何让您管理和 设置断点。

请查看 图 16 .10 ,其中显示了我们在 main() 函数上设置的软件断点,该函数属于 simple_encoder.exe 。 让我们看看该窗口中每一列显示的内容:

  • 最左侧的 State 列表示 断点的状态:

已启用 (DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第32张  ):逻辑断点(包括其所有位置)已启用。
已禁用 (DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第33张  ):逻辑断点(包括其所有位置)已禁用。
混合 (DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第34张  ):同一地址的两个逻辑断点具有不同的状态。
无效 ( DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第35张 ):由于目标已终止(或尚未启动),断点将不会触发。也可能是断点已在“静态”窗口中设置,该窗口未正确映射到任何已加载的模块。

  • 下一列 Name 是断点的名称。 这仅用于信息显示目的。 您可以随意重命名断点 ,这不会对目标或后端调试器产生任何影响。
  • 下一列 地址 显示断点的地址。 通常这是断点的 静态 地址;但如果模块映像尚未 加载,则会显示 动态 地址,该地址可能因重定位或 地址空间布局 随机化 ( ASLR ) 而改变。
  • 下一列 映像 显示包含 断点的程序数据库名称。
  • 下一列 长度 显示 断点的长度。
  • 下一列 Kinds 显示断点类型。 由于我们定义了软件断点,执行时会 显示 SW_EXECUTE .
  • 下一列 Locations 统计该断点的位置数量。 对于单目标会话,这个数值最 可能 .
  • 最后一列 Sleigh 仅适用于模拟器。 它表示该断点的行为已通过 Sleigh 代码 进行了自定义。

底部 面板列出了由后端调试器 报告的断点位置。 其中的 状态 、 地址 和 Sleigh 列与顶部面板相同,但针对的是各个 动态 地址:

  • 名称 列显示的是由后端 调试器( dbgeng.dll )指定的名称。
  • Trace(追踪) 列显示包含该位置的目标。 此处文本应与 Dynamic(动态) 面板上的某个标签页匹配。
  • Comment(注释) 列是用户定义的注释。 其默认值为 生成它的规范。

设置新断点有多种方法。 在任何静态动态窗口中(包括反汇编  内存 / 十六进制以及反编译器 ),右键点击并选择设置断点 ,或直接按键盘上的 K 键,亦或双击边缘区域。 您也可以从 Terminal 窗口通过输入后端调试器命令来设置断点,例如 bp <address> 或 bp <module_name>!<symbol_name> (当符号文件 .PDB 已加载时)。

让我们在 main() 函数内的 simple_encoder() 函数中,通过静态窗口设置一个新的软件断点:

DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第36张

图 16.20 - 在 simple_encoder()函数处设置断点

或者,您也可以使用 反编译器 窗口 :

 DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第37张

图 16.21 - 使用反编译器窗口在 simple_encoder()函数处设置断点

一旦选择 前述任一选项设置断点,将弹出 对话框显示即将设置的断点信息,如 下图所示:

 DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第38张

图16.22 - 显示我们即将设置的断点配置的对话框

在对话框中,请确保地址类型字段填写正确。如需为此断点命名,可在名称字段中输入(此为可选操作),然后点击确定按钮。 操作完成后,该断点将显示在断点窗口中,如图 16.10 所示。 .

到目前为止,我们已经讨论了关于调试和控制执行流程的各种概念,包括单步执行和设置断点。 同时也熟悉了 Ghidra 调试器提供的工具和窗口。 在接下来的章节中,让我们运用目前所学的知识来研究编码器的工作原理,以及字符串在运行时如何被修改为编码形式。

由于我们仅需调试和理解编码器函数,我们将直接从该函数开始调试会话,忽略之前的所有其他内容。 在前述章节( 执行流控制 )中,我们在调用该函数的位置设置了软件执行断点,地址位于 0x140001552 处,名称为 CALL simple_encoder。 现在通过点击工具栏上的 Resume 按钮(或按 F5 键)让程序正常执行,直到到达断点位置时暂停,以便我们继续调试。 当执行到达断点位置并暂停时,您会看到不同深浅的绿色高亮显示 程序计数器所在位置及即将执行的下一条指令。 在本例中,该指令是 CALL simple_encoder。 当您继续执行时, 反编译器窗口也会同步移动,并用黄色高亮显示即将执行的下一行代码。 其显示 效果如下:

 DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第39张

图16.23 - 程序执行在断点处暂停

现在,是时候使用单步调试来逐条执行指令并分析代码了。 但应该从步入  步过步出中选择哪个单步功能呢? 是的,这是 步入 ,因为 步过 会执行 simple_encoder 函数中的所有指令,并在 0x140001557 地址处中断(位于 Static 窗口),或停在第 36 行代码处(对应 prinf 语句,显示在 Decompiler 窗口)。 步出 则会尝试从 main() 函数中跳出,这将执行完整段代码并终止程序。 当我们从工具栏选择 步入 或按下 F8 键时,就会进入 simple_encoder 函数内部。

现在,我们需要确定该函数内部的哪些代码段对于理解编码过程至关重要。 在源代码中,我们尝试混淆用于递增每个字符的值,并在关闭优化的情况下进行编译( -O0 ),以防止编译器优化我们的代码或移除混淆处理。 我们成功实现了这一目标,通过查看为其生成的汇编代码可以明显看出;然而,Ghidra 反编译器仍然识破了这个简单的技巧,并对 代码进行了简化处理。

以下是编码器代码核心部分的汇编列表,起始于 地址 0x140001443 :


140001443 6a 01         PUSH   0x1
140001445 58            POP    RAX
140001446 89 45 f8      MOV    dword ptr [RBP + local_20],EAX
140001449 c7 45 fc      MOV    dword ptr [RBP + local_1c],0x0
       00 00 00 00
140001450 eb 2d         JMP    LAB_14000147f
                       LAB_140001452
140001452 8b 45 fc      MOV    EAX,dword ptr [RBP + local_1c]
140001455 48 63 d0      MOVSXD param_2,EAX
140001458 48 8b 45 20   MOV    RAX,qword ptr [RBP + local_res
14000145c 48 01 d0      ADD    RAX,param_2
14000145f 0f b6 00      MOVZX  EAX,byte ptr [RAX]
140001462 89 c2         MOV    param_2,EAX
140001464 8b 45 f8      MOV    EAX,dword ptr [RBP + local_20]
140001467 8d 0c 02      LEA    param_1,[param_2 + RAX*0x1]
14000146a 8b 45 fc      MOV    EAX,dword ptr [RBP + local_1c]
14000146d 48 63 d0      MOVSXD param_2,EAX
140001470 48 8b 45 28   MOV    RAX,qword ptr [RBP + local_res
140001474 48 01 d0      ADD    RAX,param_2
140001477 89 ca         MOV    param_2,param_1
140001479 88 10         MOV    byte ptr [RAX],param_2
14000147b 83 45 fc 01   ADD    dword ptr [RBP + local_1c],0x1

从前面的代码中,我们只关注高亮显示的部分。 从 0x140001443 到 0x140001446 的指令负责将源代码中的 val 变量设置为整数值 。 接下来的两行 0x140001464 和 0x140001467 ,将字符的 ASCII 值加 1 并存储到 param_1 中,这相当于源代码中的 input_string[i] + val 。 最后,位于 0x140001477 和 0x140001479 的指令行将加法结果存储到 encoded_string 内存位置,这相当于源代码中的 encoded_string[i] = input_string[i] + val 。 您可以在 反编译器 窗口中查看代码的反编译版本以获得 更清晰的理解

现在,为了查看运行时数值如何变化,我们可以为一些关键代码行设置监视表达式。 要正确设置这些表达式,需要确定汇编代码中的变量 [RBP + local_20]  param_1 和 param_2 分别映射到哪些寄存器或内存位置。 下面我们将说明如何查找 这些映射关系。

如果查看 simple_encoder 函数的开头部分,其声明段定义了函数参数 到寄存器的映射方式:

 DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第40张

图16.24 – 函数参数及其寄存器映射关系

这表明 param_1 被映射到 RCX  param_2 被映射到 RDX ,而对于 [RBP + local_20] ,要查明 local_20 相对于基址指针寄存器的具体偏移量,可以右键点击该指令,从上下文菜单中选择 Instruction Info ,如 下图所示:

 DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第41张

图16.25 - 检查指令信息

这将显示有关该指令的详细信息,您将能够确定 local_20 所使用的确切偏移量,如下图所示:

 DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第42张

图16.26 - 详细指令信息

另一种简便方法是右键点击指令,然后从上下文菜单中选择转换 。 在此处,您可以记录有符号十进制字段中显示的值。

进入 simple_encoder 函数时,param_1 或 RCX 寄存器保存着原始字符串 "Hello, world!" 的地址,而 param_2 或 RDX 寄存器则保存着新编码字符串的写入地址。 由于 RCX 和 RDX 是寄存器,其值在函数执行过程中会不断变化,因此我们需要在步入 simple_encoder 函数时立即获取这些寄存器指向的确切内存地址。 在该函数的首条指令 PUSH RBP 处,打开 寄存器 窗口并记录 RCX 和 RDX 的值,它们分别作为 param_1 和 param_2 使用。 当前调试会话中,这两个值分别为 0x6d97ff8d2 和 0x6d97ff860 ,如 下图所示:

 DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第43张

图16.27 - 记录函数入口处映射到寄存器中的参数值

现在我们已经准备好设置观察表达式,但在开始之前,让我们先深入了解它们。 观察变量使用 Sleigh 语言表达,并在当前线程的上下文中 进行实时评估。 可以为观察变量指定数据类型,以便原始数据以有意义的方式呈现。 观察窗口 通过颜色变化来提供关于显示值变更和时效性的信息。 默认情况下,变更的值显示为红色,过时的值显示为深灰色。 过时值是指依赖于任何内容未知的寄存器或内存的值。 观察表达式的 书写格式如下:

  • 要显示寄存器的值,请使用大写的寄存器名称。 例如,RSP 将显示寄存器 RSP 的值 .
  • 若要显示内存地址或寄存器的内容,我们需要以 *: 开始表达式,后跟要显示的字节大小 s,然后是寄存器名或内存地址。 对于内存地址,还需要使用 :n 指定地址大小,其中 n 表示大小(对于 4 字节地址如 0xAABBCCDD,其值为 8)。 完整表达式格式为 *:s < 寄存器名 | 内存地址:n> :
    • *:8 RSP : 从寄存器 RSP 指定的偏移地址开始显示 8 字节内存内容,例如读取栈上的 long 类型数据
    • *:4 0xaabbccdd:8 : Display 4 bytes starting at memory address 0xaabbccdd , for example, to read the int at address 0xaabbccdd
  • We can also use an athematic operator on a memory address and register to display value at a given offset with respect to the original memory address or register:   
    • *:4 (RSP+8) : 显示从寄存器 RSP 给定偏移量后 8 字节开始的内存地址处的 4 字节数据,例如读取栈上的一个 int 类型数据。
    • *:4 ((*:8 RSP)+4) : 显示从寄存器 RSP 给出的偏移量开始读取 8 字节内存后,再偏移 4 字节的内存地址处的 4 字节数据。这可能用于读取栈上基指针数组中的第二个 int 型数据。

基于此知识,我们现在可以添加监视表达式来监控编码过程。

此处我们提供表达式及其监控对象:

监视 ID

表达式

类型 

监控内容

1

*: 14 0x6d97ff8d2:8

字符串

原始字符串 "Hello, World!" 的 长度为 14。

2

*: 14 0x6d97ff860:8

字符串

新建 编码字符串。

3

*: 8 (RBP-8)

整型

对于 [RBP + local_20] ,它包含增量值(原代码中的 val )。 只有当我们执行过 0x140001446 行后才会设置该值: MOV dword ptr [RBP + local_20],EAX .

4

RDX + ( RAX*1)

字符型

对于 [param_2 + RAX*0x1] ,其中包含来自行 0x140001467 的给定原始字符的新编码字符: LEA param_1,[param_2 + RAX*0x1] .

5

DL

字符

对于 param_2 ,这里包含正在被编码的 旧字符。

表16.1 - 监视表达式及其用途

请注意 ,当我们使用通用寄存器如 RAX 或 RDX/DL 时,其值只有在程序计数器位于正确位置时才会被设置为我们预期的值。 让我们看看在不同位置时的监视情况:

  1. 在函数入口处,只有 Watch ID 1 和 2(参见 表 16.1 中的 Watch ID 定义) 是正确的:

 DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第44张

图16.28 - 函数入口点处的监视表达式及其值

  1. 当我们执行到 0x140001449 行时(该行已正确将 val 变量值设置为整数 0x1 ), 0x140001446 行的 MOV dword ptr [RBP + local_20],EAX 指令执行后,监视 ID 1、2 和 3 将显示正确结果:

 DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第45张

图 16.29 - 0x140001449 行处的监视表达式及其值

现在,继续单步跳过(不要单步进入,因为我们不希望深入当前函数之外的任何更深层次函数 。从这里开始的所有下一级函数都是诸如 strlen 之类的库函数,这些我们不需要调试),直到抵达 0x140001467 行: LEA param_1,[param_2 + RAX*0x1] (暂时不要执行该行)。 你也可以使用断点而非单步操作来定位到 这些位置。

此行代码从 param_2(关联监视器 ID 为 5)中选取原始字符,然后对其加 1(关联监视器 ID 为 4),并将结果赋值给 param_1,即编码后的字符。 如下图所示,现在可以看到所有监视器 ID 都具有正确的值,并且可以观察到来自 "Hello, world!" 的原始字符 H 显示在监视器 ID 5 中,而编码后的字符 I(即 H 的下一个字母)则位于监视器 ID 4 中:

 DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第46张

图 16.30 - 位于 0x140001467 行处的监视表达式及其值

到目前为止,监视 ID 2 所监控的内存位置(即 encoded_string 变量的地址)尚未存储任何内容。 这是因为编码值尚未被写入该地址,这个值只有在执行完代码行 0x140001479 处的指令 MOV byte ptr [RAX],param_2 后才会被写入。 因此,让我们使用 Step Over 功能执行到该行之后。 完成后,我们可以在下图中看到第一个字符 "I" 已被写入 encoded_string 地址:

 DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第47张

图 16.31 - 执行到地址 0x140001479 行后观察表达式及其值

如果我们再重复 14 次这样的操作,所有编码后的字符都将被写入 encoded_string 变量中。 您只需在 0x140001479 旁边的行设置断点,然后不断按 F5 键,就能看到它将每个编码字符写入监视 ID 2 所监控的地址。 下图展示了我在行 0x14000147b 设置断点后多执行几次的结果。 :

 DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第48张

图 16.32 - 执行 0x140001479 行代码 8 次重复后,监视表达式及其对应的值

除了使用监视表达式监控变化外,我们还可以通过调试器内存视图窗口来观察这些变化。 具体操作是:在菜单栏中点击窗口菜单,然后选择调试器 | 新建内存视图 。 请参考下图:

 DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第49张

图16.33 - 打开内存查看窗口

这将打开一个新的内存查看窗口,允许您检查内存内容并进行编辑。 通过监视表达式无法编辑内存内容。 新的内存窗口如下图所示。 我们可以自定义它同时显示 ASCII 值,这样就能轻松识别在逆向工程过程中可能对我们有用的字符串和文本。 要进行自定义,点击扳手图标打开字节查看器选项对话框,勾选 Ascii 复选框,然后点击 OK 按钮:

 DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第50张

图16.34 - 内存视图窗口配置

完成此配置后,若要导航至想要查看的内存地址,请先激活内存窗口,然后从菜单栏中选择导航 转到…(或直接按 G 键),输入要查看的地址。 我们将跳转到由监视表达式 ID 2 监控的 encoded_string 变量的内存地址 0x6d97ff860。 如下图所示。 您还可以通过内存视图窗口中的编辑按钮,或使用快捷键组合 Ctrl+Alt+E 来启用/禁用字节编辑功能。 :

 DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第51张

图 16.35 - 并排显示十六进制和 ASCII 视图的内存查看窗口

至此,您应该对 Ghidra 调试器有了良好的基础理解, 并掌握了如何使用它来控制程序执行及在运行时监控其内部状态(如变量和内存)。 这使得我们在逆向工程时能更深入地理解应用程序的行为。 建议您查阅本章末尾提供的学习资源, 以强化使用 Ghidra 调试器进行调试的技能。

现在,您可能无法直接在目标机器上安装 Ghidra 来调试应用程序,或者您可能需要在无网络连接的隔离虚拟机中调试恶意软件。 当然,您可以在这个隔离虚拟机中安装 Ghidra 进行恶意软件调试分析,但这样会失去许多重要功能,例如 BSim ,该功能 可能需要使用本地网络托管的数据库,其中存储了大量函数定义来帮助识别恶意软件中已知的函数和代码模式。 又或者,您可能需要在 Linux 虚拟机上调试 Linux 可执行文件,但您的 Ghidra 环境搭建在 Windows 系统上,此时在 Linux 机器上重新配置一个与 Windows 主安装环境(包含所有插件和自定义设置)功能相同的 Ghidra 实例会非常不便。 在上述及更多场景中,您都可以利用远程调试功能:通过在远程机器上运行的调试服务器配合本地 Ghidra,实现对不同机器上应用程序的调试。 当调试服务器与本地 Ghidra 实例成功建立连接后,其余调试操作与 本章节 所述内容完全一致。

在本节中,我们深入探讨了执行流控制的复杂机制,研究了单步执行和断点等多种调试方法。 我们讲解了如何通过监视表达式和内存 视图功能来观察及编辑内存内容。 至此,您应当能够熟练运用这些技术,在调试过程中精准导航程序执行,实现对运行状态的精确监控与控制,从而加深对程序运行时行为的理解。 下一节我们将研究 远程目标调试这一高级主题。

本节我们将学习如何远程调试应用程序。 之前我提供了一些 远程调试可能派上用场的示例场景。 从这些场景中,我们选择使用远程调试来调试 Linux 可执行文件。 为此,我们需要准备一台运行任意 Linux 发行版的虚拟机。 首先要做的是在 Linux 上使用 gcc 编译 simple_encode.c 以生成 Linux 可执行文件(ELF)。 在 Linux 机器上,只需运行以下命令即可构建 simple_encoder 可执行文件:

gcc simple_encoder.c -O0 -o simple_encoder

下图展示了在 Linux 上构建并执行 simple_encoder 可执行文件的输出结果,这与 Windows 平台上的表现完全一致:

 DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第52张

图 16.36 - 在 Linux 系统上编译并执行 simple_encoder

如果您的 Linux 机器上未安装 gdbserver ,请使用 以下命令进行安装:

sudo apt-get install gdbserver

现在我们将 启动 simple_encoder 程序,通过 gdbserver 在 Linux 机器上运行。 服务器 IP 和端口将分别使用 0.0.0.0 和 12345 。 其中, 0.0.0.0 表示监听 本网络 (即 本主机 )上 Linux 机器的 12345 端口。 完整的执行命令 如下:

gdbserver 0.0.0.0:12345 simple_encoder

命令输出将如 下图所示:

 DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第53张

图 16.37 - 执行 gdbserver 及其输出

从前面的输出中可以看到,系统已为 simple_encoder 创建了一个进程,其 pid = 1128

在继续 Windows 机器上的操作之前,我们需要满足一些必要前提条件,才能调试运行在 Kali 虚拟机上的 Linux 可执行文件(在 当前场景下 )。

Ghidra 使用集成的 Python 解释器配合 GDB 来支持调试,这与它使用 Windows 调试器( dbgeng.dll )的方式非常相似。 如果您还记得之前的内容,我们当时必须安装几个 Python 包( pybag 和 protobuf )才能使用 Windows 调试器进行调试。 同样地,在本例中我们也需要安装几个 Python 包来 使用 GDB

由于我们将 在 Windows 上使用 MingW64 GDB ,因此需要在 MingW64 的 Python 环境中安装这些 Python 包。 为此,请打开 MSYS2 MINGW64 终端并运行 以下命令。

假设您已在 MingW64 中安装了 C/C++编译器及构建调试工具( gcc  gdb 等),因前文多章均有此要求。但为确保完整性,请确认已安装 gdb-multiarch 。 若未安装,请运行以下命令 进行安装:

pacman -S mingw-w64-x86_64-gdb-multiarch

如果在 MingW64 中 尚未安装 Python,请使用 以下命令进行安装:

Pacman -S mingw-w64-x86_64-python

安装 pip (Python 包管理工具):

python3 -m ensurepip

接下来, 安装 python-psutil :

pacman -S mingw-w64-x86_64-python-psutil

现在,GDB 所需的一些重要包已随 Ghidra 公开版本发布在 以下路径中:

  • 安装路径]ghidra_11.2_PUBLICGhidraDebugDebugger-rmi-tracepypkgdist
  • 安装路径]ghidra_11.2_PUBLICGhidraDebugDebugger-agent-gdbpypkgdist

安装 ghidratrace  ghidragdb 和 protobuf ,使用 以下命令从上述位置安装:

python3 -m pip install <Install_Dir>/Ghidra/Debug/Debugger-agent-gdb/pypkg/dist/ghidragdb-11.2-py3-none-any.whl
python3 -m pip install <Install_Dir>/Ghidra/Debug/Debugger-rmi-trace/pypkg/dist/protobuf-3.20.3-py2.py3-none-any.whl
python3 -m pip install <Install_Dir>/Ghidra/Debug/Debugger-rmi-trace/pypkg/dist/ghidratrace-11.2-py3-none-any.whl

后端调试器(GDB)和远程机器上的 dbgserver 已全部准备就绪,但 Ghidra 11.2 公开版本中意外移除了远程 GDB 调试的启动器。 因此,我们需要将这些启动器(Windows 批处理文件)复制到 debugger-launchers 目录中。

这些调试启动器被意外删除的问题在此处跟踪 ( https://github.com/NationalSecurityAgency/ghidra/issues/7069?form=MG0AV3 ),临时解决方案是手动复制这些 .bat 文件。

前往 Ghidra GitHub 仓库的以下位置 ( https://github.com/NationalSecurityAgency/ghidra/tree/patch/Ghidra/Debug/Debugger-agent-gdb/data/debugger-launchers ) 并复制或下载 ssh-gdbserver.bat 和 remote-gdb.bat 到 [install_dir]ghidra_11.2_PUBLICGhidraDebugDebugger-agent-gdbdatadebugger-launchers .

以下截图显示了这些文件在 GitHub 在线仓库和 磁盘上的位置。

在 GitHub 上,您可以找到 它们的位置:

 DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第54张

图 16.38 – GitHub 上用于远程 gdb 的调试启动器

在 磁盘上,这是 位置:

 DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第55张

图 16.39 – 磁盘上手动复制的远程 gdb 调试启动器

此时,我们已经准备好调试这个 Linux 可执行文件。 将该 Linux 可执行文件复制到 Windows 机器上,导入 Ghidra 项目,然后像之前一样通过将其拖拽到调试器上来启动调试器,这个选项位于工具面板中。 :

DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第56张

图 16.40 - 导入 simple_encoder Linux 可执行文件并启动调试器工具

调试器菜单中,选择配置并启动 simple_encoder 使用... | 远程 gdb,如下图所示:

DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第57张

图 16.41 - 使用远程 gdb 启动器启动调试进程

这将打开一个远程 gdb 配置对话框。 在此,我们需要指定运行 gdbserver 的 Linux 虚拟机的 IP 地址和端口( 图 16.37)。 您可以通过在 Linux 主机的终端中运行 ifconfig 命令查看 Linux 虚拟机的 IP 地址。

在此对话框中我们需要做的一个重要更改是 gdb 命令 字段。 需要将其从 gdb 改为 gdb-multiarch 。 原因是 MingW64 Windows GDB 专为理解 Windows 内部机制设计,但当它尝试连接到运行在 Linux(Kali 虚拟机)上的 gdbserver 时,无法识别 GNU/Linux 操作系统 ABI,从而导致失败。 而 gdb-multiarch 能够识别 GNU/Linux ABI。 以下截图显示了包含 所有这些更改的配置对话框:

 DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第58张

图 16.42 - 远程 gdb 启动器配置对话框

这将连接到 gdbserver ,在 Ghidra 调试器中加载 simple_encoder 进程并暂停它。 现在我们可以按照之前 调试 Windows 程序的方式 来调试该程序。

在下面的 截图中可以看到, gdbserver 已连接到 192.168.0.1 ,这是运行 Ghidra 的 Windows 虚拟机的 IP 地址: DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第59张

图 16.43 – Linux 上的 gdbserver 显示其已连接到 192.169.0.1 上的调试客户端(Ghidra)

以下截图显示 simple_encoder 已加载到调试器中并处于暂停状态。 我们可以从此处开始调试会话,操作方式与本章前几节 描述的一致:

 DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第60张

图 16.44 - simple_encoder 进程在调试器窗口中启动并暂停

在本节中,我们学习了如何使用远程调试来调试运行在远程 Linux 虚拟机上的 Linux 可执行文件。 类似的概念可以扩展到其他类型的操作系统和远程目标。 我们只需为不同类型的远程目标选择不同的调试服务器,但一旦完成这些设置,调试的核心概念保持不变。

到目前为止,我们已经探讨了用户模式应用程序的本地和远程调试。 同样可以利用远程调试功能来调试内核,这将在下一节中讨论。

要调试远程计算机的 Windows 内核,我们将使用 dbgeng-kernel 启动器。 该调试器的配置选项与本地 Windows 调试器 ( dbgeng ) 相同,除了连接字符串参数外,这些参数将 在后文说明。

要使用 Ghidra 调试 Windows 内核,我们需要修改目标机器的启动模式设置,该机器的内核正是我们想要调试的。 我们将使用 bcdedit 工具,通过 以下命令来修改这些设置:

bcdedit /debug ON
bcdedit /dbgsettings NET HOSTIP:IP PORT:54321 KEY:1.1.1.1

IP 是运行 Ghidra 的机器的 IP 地址。 在本例中, 它是 192.168.0.1 .

以下截图显示了在嵌套 Windows 虚拟机中运行上述命令的输出(注意,这不是运行 Ghidra 的同一台 Windows 机器):

DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第61张

图16.45 – 修改启动设置命令以启用内核模式调试的输出结果

执行完上述命令后,您应当重启 Windows 虚拟机。

现在,从 Ghidra 的菜单栏中选择 Debugger dbgeng-kernel 。 在此调试器启动器的对话框中的 Arguments 字段里,输入 net:port=54321,key=1.1.1.1 连接字符串,该字符串指定了与远程目标通信的传输选项。 具体操作请参考以下截图 :

DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第62张

图 16.46 – dbgeng-kernel 配置对话框

此时,Windows 虚拟机应已完成重启。 如果点击 启动 按钮,将连接到虚拟机的 Windows 内核。 由于我们正在调试内核,您会注意到虚拟机卡住,这 是正常现象。

在 Ghidra 的 终端 窗口中,您将看到 dbgeng 已成功连接到 Windows 虚拟机内核(位于 IP 地址 192.168.0.100 ),并暂停了执行以便我们可以按需检查内核。 详情请参阅以下截图 :

 DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第63张

图 16.47 – 调试 IP 地址为 192.168.0.100 的机器上的 Windows 内核

您还可以查看模块动态窗口,其中会列出 ntkrnlmp.exe 作为已加载且当前正在调试的模块。ntkrnlmp.exe 是 Windows 内核的一部分,其名称表示 Windows NT 内核多处理器版 .

 DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第64张

图 16.48 - 动态调试窗口和模块窗口显示正在调试 Windows 内核(ntkrnlmp.exe)

终端窗口中,您可以输入常规的 WinDbg 命令来检查内核对象、模块以及其他感兴趣的内容。 例如,您可以输入 lm 查看当前所有已加载和未加载的模块,输出结果将如下所示:

 DR拍片机器怎么调Ghidra 软件逆向工程入门 第 16 章 调试_https://www.jmylbn.com_新闻资讯_第65张

图 16.49 - lm 命令的输出结果

关于内核调试的话题,我们将在此告一段落,留待您自行探索,因为这需要对 Windows 内核有更深入和高级的理解,这已超出 本章范围 。

本章中,我们涵盖了一系列调试技术和方法。 我们从 Windows 可执行文件的本地调试入门开始,详细介绍了启动 调试流程所需的配置和步骤。

接着我们探讨了利用调试控制执行流程的概念,研究了如何跟踪和操纵程序的执行流程以理解其 运行时行为 。

我们调试并理解了一个简单字符串编码器的工作原理,最后深入探讨了远程调试,全面指导如何设置和使用远程 gdb 和 gdbserver 来调试远程 Linux 虚拟机 上的 Linux 可执行文件。

我们还简要介绍了内核调试及其 设置方法。

在接下来的章节中,我们将把这里学到的概念应用到实际场景中,例如解包和分析真实的 恶意软件样本。