开启辅助访问 切换到宽版

精易论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

用微信号发送消息登录论坛

新人指南 邀请好友注册 - 我关注人的新帖 教你赚取精币 - 每日签到


求职/招聘- 论坛接单- 开发者大厅

论坛版规 总版规 - 建议/投诉 - 应聘版主 - 精华帖总集 积分说明 - 禁言标准 - 有奖举报

查看: 526|回复: 0
收起左侧

[技术文章] 逆向工程系列(五):Ghidra 实战:从零开始分析一个可执...

[复制链接]
发表于 2025-7-25 15:30:46 | 显示全部楼层 |阅读模式   河北省石家庄市

逆向工程系列(五):Ghidra 实战:从零开始分析一个可执行文件


在上一篇中,我们概述了静态分析的重要性以及 Ghidra 和 IDA Pro 这两大工具。现在,是时候将理论付诸实践了!本篇我们将聚焦于 Ghidra,通过一个具体的案例,从零开始演示如何使用 Ghidra 的反编译、交叉引用、重命名等核心功能来分析一个未知的可执行文件。

1. 准备目标文件

为了更好地进行实战,我们首先需要一个用于分析的目标文件。这里我们创建一个简单的 C 语言程序,它包含一些常见的控制流和字符串操作,足以展示 Ghidra 的强大之处。

创建 target_program.c 文件:

#include <stdio.h>
#include <string.h>
#include <stdbool.h> // 包含布尔类型头文件

// 简单的校验函数
bool check_key(const char* key) {
    char correct_prefix[] = "ABC-";
    char correct_suffix[] = "-XYZ";
    int magic_number = 0x1A2B3C4D; // 一个魔法数字

    if (strlen(key) != 12) {
        return false;
    }

    // 检查前缀
    if (strncmp(key, correct_prefix, strlen(correct_prefix)) != 0) {
        return false;
    }

    // 检查后缀
    if (strcmp(key + 8, correct_suffix) != 0) { // key + 8 指向 "-XYZ" 的开始
        return false;
    }

    // 检查中间的数字部分是否符合某种逻辑
    // 假设中间是 "1234"
    char middle_part[5];
    strncpy(middle_part, key + 4, 4); // 从key的第5个字符开始取4个
    middle_part[4] = '\0'; // 确保字符串以空字符结尾

    if (strcmp(middle_part, "1234") != 0) {
        return false;
    }

    printf("Magic number check: %x\n", magic_number); // 打印魔法数字

    return true; // 所有检查通过
}

int main(int argc, char* argv[]) {
    if (argc < 2) {
        printf("用法: %s <序列号>\n", argv[0]);
        return 1;
    }

    printf("正在检查序列号: %s\n", argv[1]);

    if (check_key(argv[1])) {
        printf("序列号正确!欢迎。\n");
    } else {
        printf("序列号错误!请重试。\n");
    }

    return 0;
}

编译程序 (使用 MinGW-w64 GCC,不带优化和调试信息,模拟未知二进制):

在命令行中,导航到文件所在的目录,然后执行以下命令:

gcc target_program.c -o target_program.exe -O0 -g0

  • -O0: 禁用所有优化,这会使生成的汇编代码更直接地反映 C 语言的结构,便于学习。
  • -g0: 不包含调试信息,模拟我们在实际逆向中通常会遇到的情况——没有符号信息。

现在,你得到了 target_program.exe,这就是我们要用 Ghidra 分析的目标。

2. Ghidra 初体验:导入与初步分析

  1. 启动 Ghidra: 打开 Ghidra 运行时,它会首先显示项目管理器。
  2. 创建新项目: 点击 File -\> New Project...
    • 选择 Non-Shared Project (非共享项目)。
    • 为项目命名(例如 TargetProgramAnalysis)并选择一个项目目录。点击 Finish
  3. 导入可执行文件:
    • 在项目管理器中,点击 File -\> Import File...
    • 浏览并选择你刚刚编译的 target_program.exe 文件,点击 Select File
    • Ghidra 会显示导入选项。通常,保持默认设置即可,它会自动识别文件类型、CPU 架构(例如 x86:LE:64:default:gcc 表示 64 位 Little Endian GCC 编译的 x86 程序)和格式(PE)。点击 OK
  4. 初步分析:
    • 导入成功后,文件会出现在项目管理器中。双击 target_program.exe
    • Ghidra 会询问你是否进行“初步分析 (Analyze)”。选择 Yes
    • 在分析选项弹窗中,确保所有推荐的分析选项都被选中(特别是 Decompiler Parameter IDStackVarRecoveryDWARF 等,尽管我们没有调试信息,但一些分析器依然有用)。点击 Analyze
    • 等待分析完成。这可能需要一些时间,具体取决于文件大小和你的电脑性能。

3. Ghidra 界面导览与核心功能实践

分析完成后,Ghidra 的主分析界面会打开。让我们逐一探索其主要区域并进行分析。

3.1 探索 main 函数

  1. Symbol Tree (符号树) 窗口: (通常在左侧)
    • 展开 Functions 节点。你会看到 Ghidra 识别出的一些函数。
    • 找到并双击 main 函数。
  2. Listing (列表) 窗口: (通常在中间)
    • 这里显示 main 函数的反汇编代码。你会看到机器码、地址和汇编指令。
    • 注意指令前的注释,CALL 指令通常会有目标函数名。
  3. Decompile (反编译) 窗口: (通常在右侧)
    • 这是最重要的窗口!它显示了 Ghidra 尝试将汇编代码反编译成的 C 语言伪代码。
    • 你会看到 main 函数的伪代码,包括 argcargv 的使用、printfcheck_key 的调用。

3.2 深入 check_key 函数

main 函数的伪代码中,你会看到调用了一个名为 FUN_00xxxxxxxx 的函数(具体名称可能不同),这就是我们的 check_key 函数。

  1. 重命名函数:Decompile 窗口中,选中这个未知函数名(如 FUN_00xxxxxxxx),按下 L (或者右键 Rename Function)。
    • 将其重命名为 check_key。你会发现 Listing 窗口和 Symbol Tree 窗口中的函数名也会同步更新。
  2. 进入 check_key 函数: 双击 Decompile 窗口中重命名后的 check_key 函数名,或者在 Symbol Tree 中双击 check_key
    • 现在,你在 Decompile 窗口中看到的是 check_key 函数的伪代码。

3.3 分析 check_key 的伪代码

check_key 的伪代码中,Ghidra 会尽可能地还原 C 语言的逻辑。

  1. 识别局部变量:
    • 你会看到类似 param_1param_2local_c 等变量名。
    • 根据其上下文(例如,strlen(param_1)),我们可以推断 param_1 就是 const char* key
    • 选中 param_1,按 L重命名为 key
    • local_c 可能是我们的 correct_prefix 数组。观察其如何被赋值和使用。
  2. 识别字符串常量:
    • 你会看到像 s_ABC-_00xxxxxxxx 这样的字符串引用。
    • 双击这些字符串引用,Ghidra 会跳转到数据段中对应的字符串位置。
    • Listing 窗口中,你可以看到这些字符串的实际内容(ABC--XYZ1234 等)。
    • 回到 Decompile 窗口,你可以根据字符串内容重命名伪代码中的相关变量(如将 s_ABC-_00xxxxxxxx 相关的变量重命名为 correct_prefix 等)。
  3. 理解控制流:
    • 观察 if 语句和 return false (return 0) 的对应关系。
    • strlen(key) != 12 的判断会在伪代码中清晰地显示出来。
    • strncmpstrcmp 函数的调用也会被 Ghidra 识别出来。如果你不确定这些标准库函数的原型,可以在 Ghidra 中查看其导入函数的交叉引用,或者在网上cha询其定义。
  4. 发现“魔法数字”:
    • 留意伪代码中的常量。你会发现一个 0x1A2B3C4D 的十六进制常量。这个就是我们源代码中的 magic_number。这表明 Ghidra 成功识别并保留了这个常量。

3.4 使用交叉引用 (Cross-References)

交叉引用是理解数据和代码如何相互关联的强大工具。

  1. 查找字符串引用:
    • Symbol Tree 窗口中,展开 Strings 节点。你会看到程序中所有的字符串常量。
    • 双击字符串 恭喜!序列号正确!\n
    • Listing 窗口中,右键该字符串地址,选择 References -\> Show References to Address(es)
    • 你会看到哪些指令引用了这个字符串。通常,这会带你到 main 函数中打印成功信息的 printf 调用处。
  2. 查找函数调用:
    • Symbol Tree 中,找到 check_key 函数。
    • 右键 check_key,选择 References -\> Show References to
    • 你会看到 main 函数调用了 check_key。这验证了我们的分析。

4. 结合动态调试验证静态分析

静态分析为你提供了程序的“地图”,动态调试则是按图索骥,验证你的猜测,并观察运行时行为。

  1. 设定目标: 通过 Ghidra 的分析,我们已经知道正确的序列号是 "ABC-1234-XYZ",长度为 12。
  2. 启动 x64dbg: 使用 x64dbg 打开 target_program.exe
  3. 设置断点:main 函数的 check_key 调用处设置一个断点。
  4. 命令行参数: 在 x64dbg 中,通过 File -\> Change Commandline,在命令行中输入你的测试序列号,例如:target_program.exe ABC-1234-XYZ
  5. 运行并观察:
    • 运行程序,它会在 check_key 调用前暂停。
    • F7 单步进入 check_key 函数。
    • 观察寄存器(尤其是 RCX,它应该包含你输入的序列号字符串地址)、栈和标志位的变化。
    • 对照 Ghidra 的伪代码,单步执行,验证 Ghidra 的反编译是否准确。你会看到 strlenstrncmpstrcmp 的返回值如何影响程序流程。
    • 如果输入了错误的序列号,观察是哪个条件判断导致函数返回 false

5. 总结与展望

通过本篇的实战,你已经初步掌握了 Ghidra 的核心功能,包括导入文件、初步分析、重命名、识别数据、理解控制流以及利用交叉引用。更重要的是,你体验了静态分析与动态调试结合的强大威力,它们是逆向工程中不可或缺的互补工具。

在下一篇中,我们将探讨在逆向工程中常用的其他工具和技术,如 PE 文件结构分析、脱壳、以及一些自动化分析工具。


您需要登录后才可以回帖 登录 | 注册

本版积分规则 致发广告者

发布主题 收藏帖子 返回列表

sitemap| 易语言源码| 易语言教程| 易语言论坛| 易语言模块| 手机版| 广告投放| 精易论坛
拒绝任何人以任何形式在本论坛发表与中华人民共和国法律相抵触的言论,本站内容均为会员发表,并不代表精易立场!
论坛帖子内容仅用于技术交流学习和研究的目的,严禁用于非法目的,否则造成一切后果自负!如帖子内容侵害到你的权益,请联系我们!
防范网络诈骗,远离网络犯罪 违法和不良信息举报QQ: 793400750,邮箱:wp@125.la
网站简介:精易论坛成立于2009年,是一个程序设计学习交流技术论坛,隶属于揭阳市揭东区精易科技有限公司所有。
Powered by Discuz! X3.4 揭阳市揭东区精易科技有限公司 ( 粤ICP备2025452707号) 粤公网安备 44522102000125 增值电信业务经营许可证 粤B2-20192173

快速回复 返回顶部 返回列表