| name | reverse-engineering-malware-with-ghidra |
| description | 使用 NSA 的 Ghidra 反汇编器和反编译器对恶意软件二进制文件进行逆向工程,在汇编和伪 C 代码层面理解其内部逻辑、密码学例程、C2 协议和规避技术。适用于恶意软件逆向工程、反汇编分析、反编译、二进制分析或理解恶意软件内部机制等请求。
|
| domain | cybersecurity |
| subdomain | malware-analysis |
| tags | ["malware","reverse-engineering","Ghidra","disassembly","decompilation"] |
| version | 1.0.0 |
| author | mahipal |
| license | Apache-2.0 |
使用 Ghidra 对恶意软件进行逆向工程
适用场景
- 静态和动态分析已发现可疑功能,需要更深入的代码级理解时
- 需要对 C2 通信协议、加密算法或自定义混淆进行逆向工程时
- 理解恶意软件样本所针对的确切漏洞利用机制或漏洞时
- 从编译代码中提取硬编码配置数据(C2 地址、加密密钥、活动 ID)时
- 根据唯一代码模式开发精确的 YARA 规则或检测签名时
请勿使用:对未知样本进行初步鉴别;请先使用 PEStudio 进行静态分析,使用 Cuckoo 进行行为分析。
前置条件
- Ghidra 11.x(从 https://ghidra-sre.org/ 下载)及 JDK 17+
- 与生产网络隔离的分析虚拟机(Windows 或 Linux 主机)
- 熟悉 x86/x64 汇编语言和 Windows API 调用约定
- Windows 系统 DLL 的 PDB 符号文件(提高反编译准确性)
- Ghidra 脚本仓库(ghidra_scripts),用于自动化分析任务
- 二级参考:IDA Free 或 Binary Ninja,用于交叉验证分析结果
工作流程
步骤 1:创建项目并导入二进制文件
创建 Ghidra 项目并导入恶意软件样本:
1. 启动 Ghidra: ghidraRun(Linux)或 ghidraRun.bat(Windows)
2. File -> New Project -> Non-Shared Project -> 选择目录
3. File -> Import File -> 选择恶意软件二进制文件
4. Ghidra 自动检测格式(PE、ELF、Mach-O)和架构
5. 接受默认导入选项(如已知则指定基地址)
6. 双击导入的文件在 CodeBrowser 中打开
7. 当提示时,运行启用默认分析器的 Auto Analysis
无头模式(用于自动化):
/opt/ghidra/support/analyzeHeadless /tmp/ghidra_project MalwareProject \
-import suspect.exe \
-postScript ExportDecompilation.py \
-scriptPath /opt/ghidra/scripts/ \
-deleteProject
步骤 2:识别关键函数和入口点
在二进制文件中定位关键代码段:
导航策略:
━━━━━━━━━━━━━━━━━━━
1. 从入口点(OEP)开始,追踪 _start/WinMain 的执行流程
2. 检查 Symbol Tree 中的导入函数(Window -> Symbol Tree)
3. 搜索对可疑 API 的交叉引用(XREF):
- VirtualAlloc/VirtualAllocEx(注入用内存分配)
- CreateRemoteThread(远程线程注入)
- CryptEncrypt/CryptDecrypt(加密操作)
- InternetOpen/HttpSendRequest(C2 通信)
- RegSetValueEx(注册表持久化)
4. 使用 Search -> For Strings 查找嵌入的 URL、IP 和路径
5. 检查按大小排序的 Functions 窗口(较大的函数通常包含核心逻辑)
Ghidra 高效导航快捷键:
G - 跳转到地址
Ctrl+E - 搜索字符串
X - 显示当前位置的交叉引用
Ctrl+Shift+F - 搜索字节模式
L - 重命名标签/函数
; - 添加注释
T - 重新定义变量类型
Ctrl+L - 重新定义返回值类型
步骤 3:分析反编译代码
使用 Ghidra 反编译器理解函数逻辑:
void decrypt_config(BYTE *encrypted_data, int data_len, BYTE *key, int key_len) {
for (int i = 0; i < data_len; i++) {
encrypted_data[i] = encrypted_data[i] ^ key[i % key_len];
}
return;
}
步骤 4:追踪 C2 通信逻辑
跟踪网络通信代码路径:
C2 协议逆向工程的分析步骤:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. 找到 InternetOpenA/WinHttpOpen 调用 -> 追踪到封装函数
2. 跟踪从加密配置到 URL 构建的数据流
3. 识别 HTTP 方法(GET/POST)、请求头和请求体格式
4. 定位响应解析逻辑(JSON 解析、自定义二进制协议)
5. 绘制 C2 命令分发器(switch/case 或跳转表)
6. 记录命令集(下载、执行、外泄、更新、卸载)
提取 C2 配置的 Ghidra 脚本:
from ghidra.program.model.data import StringDataType
from ghidra.program.model.symbol import SourceType
listing = currentProgram.getListing()
memory = currentProgram.getMemory()
symbol_table = currentProgram.getSymbolTable()
for symbol in symbol_table.getExternalSymbols():
if "InternetOpen" in symbol.getName():
refs = getReferencesTo(symbol.getAddress())
for ref in refs:
print("C2 初始化位于: {}".format(ref.getFromAddress()))
步骤 5:分析加密和混淆
识别并记录密码学例程:
常见恶意软件加密模式:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
XOR 加密: 带 XOR 操作的循环,通常为单字节或滚动密钥
RC4: 两个循环(KSA + PRGA),256 字节 S-box 初始化
AES: 查找 S-box 常量(0x63、0x7C、0x77...)或 CryptEncrypt 调用
Base64: 包含 A-Za-z0-9+/= 字符的查找表
自定义: 算术操作组合(ADD、SUB、ROL、ROR 与 XOR 混合)
识别技巧:
- 搜索常量: AES S-box、CRC32 表、MD5 初始值
- 查找对字节数组进行操作的循环结构
- 检查 Windows Crypto API 的使用(CryptAcquireContext -> CryptCreateHash -> CryptEncrypt)
- FindCrypt Ghidra 插件可自动识别加密常量
步骤 6:记录发现结果并创建检测签名
从逆向工程中产出可操作的威胁情报:
cat << 'EOF' > malware_family_x.yar
rule MalwareFamilyX_Decryptor {
meta:
description = "检测 MalwareX 解密例程"
author = "analyst"
date = "2025-09-15"
strings:
// 带硬编码密钥的 XOR 解密循环
$decrypt = { 8A 04 0E 32 04 0F 88 04 0E 41 3B CA 7C F3 }
// 解密后的 C2 URL 模式
$c2_pattern = "/gate.php?id=" ascii
condition:
uint16(0) == 0x5A4D and $decrypt and $c2_pattern
}
EOF
核心概念
| 术语 | 定义 |
|---|
| 反汇编(Disassembly) | 将机器码字节转换为可读汇编语言指令;Ghidra 的 Listing 视图显示反汇编代码 |
| 反编译(Decompilation) | 将汇编代码提升为伪 C 表示以便于分析;Ghidra 的 Decompile 窗口提供此视图 |
| 交叉引用(XREF,Cross-Reference) | 显示函数或数据地址被调用或使用位置的引用;对追踪代码执行流至关重要 |
| 控制流图(CFG,Control Flow Graph) | 函数中所有可能执行路径的可视化表示;展示分支逻辑和循环结构 |
| 原始入口点(OEP,Original Entry Point) | 解包后恶意软件代码的实际起始地址;加壳程序先通过解包桩重定向执行 |
| 函数签名(Function Signature) | 函数的返回类型、名称和参数类型;应用正确的签名可提高反编译器输出质量 |
| Ghidra 脚本(Ghidra Script) | 在 Ghidra 内执行的 Python 或 Java 自动化脚本,用于批量分析、模式搜索或数据提取 |
工具与系统
- Ghidra:NSA 的开源软件逆向工程套件,支持多架构的反汇编器、反编译器和脚本功能
- IDA Pro/Free:行业标准交互式反汇编器;IDA Free 提供基于云的 x86/x64 反编译
- Binary Ninja:商业逆向工程平台,具有现代 UI 和用于插件开发的丰富 API
- x64dbg:开源 x64/x32 Windows 调试器,与 Ghidra 配合用于恶意软件的动态调试
- FindCrypt(Ghidra 插件):在二进制代码中识别密码学常量和算法的插件
常见场景
场景:逆向自定义 C2 协议
背景:行为分析显示存在发往外部 IP 非标准端口的加密流量。由于协议为私有协议,网络签名无法检测变体。需要深入逆向工程来理解协议结构。
分析方法:
- 将解包后的样本导入 Ghidra 并运行完整的 Auto Analysis
- 定位 socket/WinHTTP API 调用,向上追溯到调用函数
- 识别数据发送前调用的加密例程(从 send/HttpSendRequest 向前追踪数据流)
- 逆向加密算法(XOR 密钥提取、RC4 密钥派生、AES 密钥定位)
- 通过分析响应解析函数(对命令 ID 进行 switch/case)绘制命令结构
- 记录协议格式(头部结构、命令字节、加密方法)
- 为网络监控工具创建协议解码脚本
常见陷阱:
- 未运行完整 Auto Analysis 就开始手动分析(会遗漏函数边界和类型传播)
- 忽略通过函数指针或虚函数表的间接调用(使用 XREF 追踪保存函数地址的数据)
- 在 Ghidra 的 Function ID(FID)或 FLIRT 签名本应识别的库代码上浪费时间
- 未频繁保存 Ghidra 项目进度(崩溃后分析状态可能丢失)
输出格式
逆向工程分析报告
=====================================
样本: unpacked_payload.exe
SHA-256: abc123def456...
架构: x86(32 位 PE)
Ghidra 项目: MalwareX_Analysis
函数映射
0x00401000 main() - 入口点,初始化配置
0x00401200 decrypt_config() - 使用 16 字节密钥进行 XOR 解密
0x00401400 init_c2() - WinHTTP 初始化,URL 构建
0x00401800 c2_beacon() - 携带系统信息的 HTTP POST 心跳
0x00401C00 cmd_dispatcher() - 对 12 个命令码进行 Switch 分发
0x00402000 inject_process() - 进程空洞注入 svchost.exe
0x00402400 persist_registry() - HKCU Run 键持久化
0x00402800 exfil_data() - 文件收集和加密上传
C2 协议
方法: HTTPS POST 到 /gate.php
加密: RC4,密钥由 MD5(bot_id + campaign_key) 派生
Bot ID 格式: MD5(hostname + username + volume_serial)
心跳间隔: 60 秒,带 10% 抖动
命令集:
0x01 - 下载并执行文件
0x02 - 执行 Shell 命令
0x03 - 上传文件到 C2
0x04 - 更新配置
0x05 - 卸载并清除痕迹
加密详情
算法: RC4
密钥派生: MD5(bot_id + "campaign_2025_q3")
硬编码种子: "campaign_2025_q3" 位于偏移量 0x00405A00
提取的 IOC
C2 URL: hxxps://update.malicious[.]com/gate.php
hxxps://backup.evil[.]net/gate.php(故障转移)
活动 ID: campaign_2025_q3
RC4 密钥材料: [参见加密详情]