ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

ELF静态逆向分析——Ghidra从安装到反编译重建源码

2026/8/26 15:00:16 拓冰建站 浏览量
ELF静态逆向分析——Ghidra从安装到反编译重建源码 ELF静态逆向分析——Ghidra从安装到反编译重建源码适用场景你只有一个二进制可执行文件没有源代码想搞清楚这个程序干了什么甚至恢复出接近源码的逻辑。本文不涉及动态调试程序运行起来之后的分析只做静态分析。什么是静态分析不运行程序直接分析二进制文件本身。通过查看文件信息、提取字符串、反汇编、反编译等手段还原程序逻辑。Ghidra是目前最强大的开源反编译工具能把汇编代码还原成可读性很高的C伪代码。第一章 Ghidra安装与环境配置1.1 工具安装1.1.1 更新包管理器并安装基础工具sudo apt update sudo apt install binutils gdb build-essential1.1.2 安装radare2源码方式最新版git clone https://github.com/radareorg/radare2 cd radare2 sys/install.sh或使用snap安装最新版sudo snap install radare21.1.3 安装Ghidra需要Java 11下载Ghidra 10.3.2wget https://github.com/NationalSecurityAgency/ghidra/releases/download/Ghidra_10.3.2_build/ghidra_10.3.2_PUBLIC_20230711.zip unzip ghidra_10.3.2_PUBLIC_20230711.zip重要ghidra_10.3.2_PUBLIC目录必须保留这是Ghidra的实际程序文件删除后Ghidra将无法运行。1.1.4 安装OpenJDK 17推荐兼容性好sudo apt install openjdk-17-jdk1.2 环境配置1.2.1 检查JAVA_HOME环境变量echo $PATH echo $PATH | tr : \n | grep tools检查JAVA_HOME是否在环境中env | grep JAVA检查当前PATH中是否包含JVM路径echo $PATH | tr : \n | grep jvm直接测试Java是否可用java -version javac -version1.2.2 手动配置JAVA_HOME如果Java未正确配置手动执行export JAVA_HOME/usr/lib/jvm/java-17-openjdk-amd64 export PATH$JAVA_HOME/bin:$PATH立即测试配置是否生效echo $JAVA_HOME java -version1.2.3 验证当前使用的radare2版本检查当前使用的是哪个radare2which r2检查版本确认是apt安装的版本r2 -v如果输出显示版本信息和路径在/usr/bin/r2说明使用的是apt版本如果路径在/usr/local/bin/r2r2 -v显示git commit hash说明是源码安装版本1.3 脚本启动方法1添加到PATH环境变量# 将Ghidra目录添加到PATH echo export PATH$PATH:~/ghidra_10.3.2_PUBLIC ~/.rc source ~/.rc # 检查.rc文件末尾是否有设置 tail -10 ~/.rc现在可以直接运行ghidraRun方法2创建便捷启动脚本# 在主目录创建启动脚本 echo #!/bin/ cd ~/ghidra_10.3.2_PUBLIC ./ghidraRun ~/ghidra.sh chmod x ~/ghidra.sh通过脚本启动~/ghidra.sh验证当前安装查看Ghidra实际位置find ~ -name ghidraRun 2/dev/null1.4 进程检测检查Ghidra和Java进程ps aux | grep ghidra ps aux | grep java或使用jps查看Java进程jps -l | grep ghidra查看进程树检查是否有残留的Java进程pstree | grep -i java第二章 Ghidra反汇编与逆向分析2.1 反汇编工具链概览三种工具的关系工具定位适用场景objdumpLinux自带快速查看汇编初步了解程序结构radare2命令行交互式逆向框架脚本化分析、远程分析Ghidra图形化反编译工具深度分析还原C伪代码标准工作流程file/nm/strings 摸底 → objdump 看汇编 → radare2 深入探索 → Ghidra 反编译重建源码2.2 基础文件分析2.2.1 查看文件信息file your_program输出示例ELF 64-bit LSB executable, x86-642.2.2 检查是否包含调试符号nm your_program如果有输出说明包含符号信息恢复会容易很多如果没输出或显示no symbols说明符号被剥离了2.2.3 提取字符串strings your_program strings.txt less strings.txt这会提取所有可读字符串帮你了解程序可能的功能。2.3 objdump反汇编2.3.1 反汇编整个程序objdump -d -M intel your_program full_disassembly.asm2.3.2 只反汇编代码段objdump -d -M intel --disassemble.text your_program code_section.asm2.3.3 查看特定函数先找到main函数地址objdump -t your_program | grep main反汇编main函数以地址0x401050到0x401150为例objdump -d -M intel --start-address0x401050 --stop-address0x401150 your_program2.4 radare2安装与使用2.4.1 安装说明radare2已在1.1.2节完成安装可通过以下命令验证which r2 # 确认路径 r2 -v # 确认版本2.4.2 初始分析r2 -A your_program-A参数执行自动分析。2.4.3 radare2交互命令进入radare2交互环境后常用命令[0x00000000] aa # 自动分析 [0x00000000] afl # 列出所有函数 [0x00000000] s main # 跳转到main函数 [0x00000000] pdf # 反汇编当前函数 [0x00000000] pdb # 显示调试器信息 [0x00000000] iz # 列出数据段中的字符串2.4.4 详细函数分析[0x00000000] pdf * # 反汇编所有函数 [0x00000000] graph # 显示控制流图2.5 GDB动态分析辅助反汇编场景在静态分析过程中有时需要用GDB辅助查看运行时的汇编信息。启动GDBgdb your_programGDB中常用反汇编相关命令(gdb) info functions # 列出所有函数 (gdb) disas main # 反汇编main函数 (gdb) break main # 在main函数设置断点 (gdb) run # 运行程序 (gdb) stepi # 单步执行汇编指令 (gdb) info registers # 查看寄存器状态 (gdb) x/10i $pc # 查看当前指令附近10条指令 (gdb) quit # 退出2.6 Ghidra反编译最重要的一步2.6.1 启动Ghidracd ~/ghidra_10.3.2_PUBLIC ./ghidraRun2.6.2 创建项目和分析文件File → New Project → Non-shared project → 输入项目名File → Import File → 选择你的可执行文件双击导入的文件打开Code Browser点击Yes开始自动分析2.6.3 定位关键函数在左侧Symbol Tree窗口展开Functions找到并双击main函数。右侧会出现三个主要窗口左侧汇编代码窗口中间反编译窗口显示伪C代码右侧各种信息面板2.6.4 分析伪代码示例假设Ghidra显示undefined8 main(void) { int iVar1; long in_FS_OFFSET; char local_18 [8]; long local_10; local_10 *(long *)(in_FS_OFFSET 0x28); printf(Enter password: ); fgets(local_18,8,stdin); iVar1 strcmp(local_18,secret); if (iVar1 0) { puts(Access granted!); } else { puts(Wrong password!); } if (local_10 ! *(long *)(in_FS_OFFSET 0x28)) { /* WARNING: Subroutine does not return */ __stack_chk_fail(); } return 0; }2.6.5 重命名变量和函数在Ghidra中重命名变量使其更有意义。重命名变量的三种方法方法操作方法1双击重命名在反编译窗口中直接双击变量名输入新名称按Enter确认方法2右键菜单在变量上右键点击 → Rename Variable → 输入新名称 → Enter方法3快捷键选中变量后按L键小写L输入新名称按Enter确认2.7 从反编译结果重建源代码根据Ghidra的输出手动重建C代码。以上面的伪代码为例重建后的C代码#include stdio.h #include string.h int main(void) { char password[8]; printf(Enter password: ); fgets(password, sizeof(password), stdin); if (strcmp(password, secret) 0) { printf(Access granted!\n); } else { printf(Wrong password!\n); } return 0; }重要提示注意事项说明变量名会丢失Ghidra会生成类似local_18的临时名称需手动重命名注释完全丢失需要根据逻辑重新添加注释代码结构可能不同编译器优化会改变代码结构先分析简单函数从main函数开始逐步分析调用的子函数多次验证反复测试确保恢复的代码行为与原始程序一致按照这些步骤即使没有源代码也能很大程度上恢复程序的逻辑和功能。2.8 验证恢复的代码2.8.1 编译测试gcc -o recovered recovered_code.c ./recovered2.8.2 对比行为比较原始程序和恢复后程序的行为是否一致。2.9 导出整个程序的反汇编文本2.9.1 完整导出流程保存项目File → Save Project导出完整反汇编File → Export Program → 选择ASCII格式导出反编译代码在反编译窗口中逐个函数复制伪代码导出符号表使用脚本导出所有函数和变量名整理输出将导出的文件整理成有结构的文档格式选择说明选择ASCII格式当您需要反汇编代码汇编指令时不要选择C/C格式除非您需要原始二进制数据的C数组表示反编译的C代码需要通过脚本或手动复制从Decompile窗口获取2.9.2 使用文件菜单导出点击菜单栏 File → Export Program...在弹出的对话框中选择格式为 ASCII设置Output File路径和文件名Options中可选择包含地址、操作码、指令等。2.9.3 使用脚本批量导出打开脚本管理器Window → Script Manager搜索并运行以下脚本脚本名称功能ExportAssemblyScript.java导出汇编代码ExportToCSVScript.java导出为CSV格式DumpAddressInfoScript.java导出地址信息2.9.4 分函数导出反汇编单个函数导出在反汇编窗口中选择要导出的函数右键点击 → Copy Special → Copy As选择格式Assembly - 纯汇编代码Formatted Assembly - 带格式的汇编代码HTML - HTML格式批量导出所有函数打开 Window → Function Graph在函数图窗口中右键 → Export → Export Graph可选择导出为文本或图像格式。2.9.5 使用控制台命令导出打开Ghidra控制台Window → Console在控制台中输入命令java// 导出整个程序的反汇编 saveProgramText(currentProgram, /path/to/output/disassembly.txt); // 导出特定地址范围 saveAddressRangeText(currentProgram, fromAddr, toAddr, /path/to/output/range.txt);2.9.6 保存Ghidra项目File → Save Project这会保存所有分析结果、注释、重命名等项目文件可以在以后重新打开继续分析。2.9.7 导出为二进制补丁File → Export → Binary File... 可以选择导出修改后的二进制文件。2.9.8 使用第三方脚本增强导出功能安装社区脚本git clone https://github.com/NationalSecurityAgency/ghidra.git在Script Manager中导入有用的导出脚本。2.9.9 完整导出脚本示例在Script Manager中运行以下脚本可以导出带注释的完整反汇编java// 在Script Manager中运行这个脚本可以导出带注释的完整反汇编 import ghidra.app.script.GhidraScript; import ghidra.program.model.listing.*; import java.io.*; public class ExportFullDisassembly extends GhidraScript { public void run() throws Exception { File outputFile new File(/tmp/full_disassembly.asm); PrintWriter writer new PrintWriter(outputFile); Listing listing currentProgram.getListing(); InstructionIterator instructions listing.getInstructions(true); while (instructions.hasNext()) { Instruction instruction instructions.next(); String line instruction.getAddress() instruction.toString(); writer.println(line); } writer.close(); println(Disassembly exported to: outputFile.getAbsolutePath()); } }2.9.10 逐部分手动复制在反汇编窗口中按CtrlA全选CtrlC复制粘贴到文本编辑器中重复此过程覆盖所有代码段。2.10 实际案例演示假设你有一个简单的计算器程序被删除了源代码分析过程strings分析发现字符串Enter first number:、Enter second number:、Result: %dGhidra反编译显示undefined8 main(void) { int iVar1; int iVar2; printf(Enter first number: ); iVar1 get_number(); printf(Enter second number: ); iVar2 get_number(); printf(Result: %d\n,iVar1 iVar2); return 0; }重建的代码#include stdio.h int get_number(void) { int num; scanf(%d, num); return num; } int main(void) { int num1, num2; printf(Enter first number: ); num1 get_number(); printf(Enter second number: ); num2 get_number(); printf(Result: %d\n, num1 num2); return 0; }------------------------------------------------------------------- 后续补充内容优先同步至 GitHub如有疏漏欢迎指正。 全部相关技术笔记托管于 GitHub 仓库欢迎访问。 GitHub仓库kyshipit/tech‑notes---------------------------------------------------------------------