固件逆向分析基础:二进制文件格式解析与反汇编工具链入门
各位同学,欢迎来到第二章。上一章我们聊了固件安全审计的整体框架,今天要扎进去的是逆向分析的基础——说白了,就是怎么跟二进制文件打交道。
我刚开始做固件安全那会儿,拿到一个.bin文件就头大。后来发现,只要搞懂了文件格式、会用反汇编工具、会解包打包,大部分固件在你面前就是透明的。嗯,咱们一步步来。
2.1 二进制文件格式解析:ELF / PE / BIN
固件逆向的第一步,是搞清楚你手里拿的是什么格式的文件。常见的就三种:ELF、PE、以及裸二进制BIN。
2.1.1 ELF(Executable and Linkable Format)
Linux和大多数嵌入式系统(比如VxWorks、uClinux)都用ELF。它结构清晰,分段明确。
我个人习惯,拿到ELF文件先看它的段表(Section Header Table)和程序头(Program Header)。段表告诉你代码、数据、符号表在哪;程序头告诉系统怎么加载。
| 段名 | 作用 | 逆向关注点 |
|---|---|---|
| .text | 可执行代码 | 主要分析目标,包含固件逻辑 |
| .rodata | 只读数据(字符串、常量) | 硬编码密码、URL、密钥线索 |
| .data | 已初始化全局变量 | 配置参数、状态标志 |
| .bss | 未初始化全局变量 | 运行时分配,文件里不占空间 |
| .symtab | 符号表 | 函数名、变量名——调试信息没strip的话,逆向难度直接降一半 |
readelf -S firmware.elf 可以快速列出所有段。如果厂商没strip符号表,你甚至能看到函数名叫“check_password”——这基本等于把漏洞送给你了。
2.1.2 PE(Portable Executable)
PE是Windows和某些UEFI固件的标准格式。结构比ELF复杂一点,多了个DOS头、NT头、节表。
我记得有一次审计一个UEFI固件,厂商把关键配置放在PE文件的.rsrc节里。用Resource Hacker一翻,直接找到了硬编码的后门账号。嗯,这种低级错误在IoT设备里真不少见。
PE文件的关键节:
- .text — 代码段,跟ELF一样
- .rdata — 只读数据,导入表(IDT)也在这
- .data — 读写数据
- .rsrc — 资源(图标、字符串、配置)
- .reloc — 重定位表,基址随机化时用
2.1.3 裸二进制(BIN / Raw Binary)
这是最头疼的格式——没有头、没有段表、没有符号。说白了就是一段内存转储,从某个起始地址开始直接放代码和数据。
为什么会有这种格式?很多MCU(比如STM32、ESP32)的固件就是直接从Flash地址0x08000000开始执行,不需要文件系统来解析。
怎么猜基址?看中断向量表。ARM Cortex-M的固件,地址0x00000000或0x08000000处放的是栈指针,0x00000004处是复位向量。如果看到这两个值合理,基址就对了。
2.2 反汇编工具链入门:Ghidra vs IDA Pro
工具是手的延伸。我个人两个都用,但场景不同。
2.2.1 Ghidra——开源、免费、团队协作
NSA开源的Ghidra,这几年火得不行。它最大的优势是:免费、支持插件、有协作功能。
我建议初学者从Ghidra入手。原因很简单——不用破解,随便装。而且它的反编译引擎(Decompiler)质量很高,能把汇编转成伪C代码,阅读起来舒服很多。
基本操作流程:
- 创建项目(Project)→ 导入固件文件
- 自动分析(Auto-Analyze)→ 识别指令集、段、函数
- 手动修正基址(如果导入的是BIN文件)
- 反编译 → 看伪C代码
- 重命名函数/变量 → 标注注释
Ghidra的杀手锏: 它的“版本跟踪”(Version Tracking)功能,可以对比两个固件版本之间的差异。我在挖漏洞时经常用这个——厂商修了一个漏洞,但没修干净,通过对比就能发现。
2.2.2 IDA Pro——老牌、强大、但贵
IDA Pro是行业标杆。它的交互式反汇编、F5反编译、脚本支持(IDAPython)都是顶级的。
但说实话,价格劝退了很多人。个人版也要上千美元。不过如果你做商业审计,公司一般会买。
IDA Pro比Ghidra强在哪?
- 指令集支持更全 — 冷门MCU(比如Renesas RL78、TI MSP430)的插件更多
- 反汇编精度更高 — 对混淆代码、花指令的处理更成熟
- 调试器集成 — 可以动态调试,Ghidra的调试器还在完善中
我个人习惯:日常分析用Ghidra,遇到混淆严重的代码或者需要动态调试时,切到IDA Pro。
2.2.3 工具对比
| 特性 | Ghidra | IDA Pro |
|---|---|---|
| 价格 | 免费 | 商业授权(贵) |
| 反编译质量 | 优秀(伪C) | 优秀(F5) |
| 指令集支持 | ARM/x86/MIPS等主流 | 几乎全覆盖 |
| 脚本语言 | Python / Java | IDAPython / C++ |
| 协作功能 | 内置(多人同时分析) | 需插件 |
| 调试器 | 基础 | 成熟 |
2.3 固件解包与打包技术
拿到固件文件,第一件事不是反汇编,而是解包。因为固件通常是一个大包,里面塞了Bootloader、内核、文件系统、配置分区等等。
我曾经遇到一个路由器固件,厂商把文件系统用LZMA压缩了三层,每层还加了自定义魔数。解了三天才解开——但解开之后发现漏洞就在第一层配置里。你说气不气人?
2.3.1 常见固件打包格式
- SquashFS — Linux嵌入式最常用,只读压缩文件系统
- CramFS — 老牌,现在用得少了
- JFFS2 / UBIFS — 针对NAND Flash的读写文件系统
- TRX / BIN — 路由器固件常见,带头部校验
- U-Boot Image (uImage) — U-Boot引导的内核镜像
2.3.2 解包工具链
我常用的工具:
- binwalk — 神器,自动扫描固件中的文件系统、压缩数据、头部特征
- unsquashfs — 解压SquashFS
- jefferson — 解压JFFS2
- dd + 手动偏移 — 当自动工具失效时,手工切割
binwalk -Me firmware.bin — 自动扫描并递归解包如果binwalk没识别出来,用
binwalk -R "\x00\x00\x00\x00" firmware.bin 手动搜索魔数。
2.3.3 打包——修改后怎么封回去
解包容易,打包难。很多固件有校验和签名,你改了内容就启动不了。
打包的基本思路:
- 用
mksquashfs重新打包文件系统 - 用原始固件的头部模板,替换掉文件系统部分
- 重新计算校验和(CRC32 / SHA256)
- 如果固件有数字签名——嗯,那就得走漏洞利用路线了,后面章节会讲
2.4 本章知识体系
下面这张图总结了本章的核心逻辑——从拿到固件到开始逆向分析的完整流程。
这张图把今天的知识点串起来了。你拿到一个固件,先识别格式,再解包,然后反汇编分析,最后定位漏洞并尝试修改打包。每一步都有对应的工具和方法。