第二章 固件逆向工程基础:二进制分析工具链、固件提取技术、固件解包与打包
各位好,欢迎来到固件逆向工程的基础章节。说实话,这一章是整个课程里最“硬核”的部分之一。你可能会问:为什么要把逆向工程放在这么靠前的位置?
我的回答很简单:没有逆向能力,固件安全审计就是纸上谈兵。你拿到的可能是一个加密的.bin文件,也可能是一块焊死的Flash芯片。怎么把它变成可读的代码?怎么找到漏洞入口?这些都得靠逆向工程。
我个人习惯把固件逆向分成三个层次:工具链准备 → 固件提取 → 解包与打包。下面我们一层层拆开讲。
核心观点:固件逆向不是“破解”,而是“理解”。你只有理解了固件的结构,才能发现其中的安全缺陷。
2.1 二进制分析工具链
做固件逆向,工具就是你的武器。我见过不少新手,上来就开IDA Pro,结果面对一堆裸二进制数据完全懵了。为什么?因为选错工具比没有工具更可怕。
下面这张表是我个人常用的工具清单,按使用场景分类:
| 工具类别 | 推荐工具 | 主要用途 | 我的评价 |
|---|---|---|---|
| 反汇编器 | Ghidra / IDA Pro / Radare2 | 将机器码转为汇编代码,支持交叉引用分析 | Ghidra免费且强大,IDA Pro插件生态好 |
| 十六进制编辑器 | 010 Editor / HxD / ImHex | 查看和编辑原始二进制数据,识别文件结构 | 010 Editor的模板功能是神器 |
| 调试器 | GDB / OpenOCD / JTAG调试器 | 动态分析固件运行时的行为 | 硬件调试器配合GDB效果最佳 |
| 字符串提取 | strings / binwalk / foremost | 从固件中提取可读字符串和文件碎片 | binwalk是固件分析的瑞士军刀 |
| 二进制差异 | bindiff / diaphora | 对比不同版本固件的差异,定位补丁位置 | 漏洞挖掘时经常用到 |
我的小技巧:别只依赖一个工具。我习惯先用 binwalk 做快速扫描,再用 Ghidra 做深度分析。两个工具互相验证,能避免很多误判。
举个例子,你拿到一个路由器固件,先用 strings 跑一遍:
strings firmware.bin | grep -i "password\|admin\|root\|debug"
如果看到硬编码的凭据,恭喜你,第一个漏洞已经到手了。但更多时候,你需要把固件加载到反汇编器里,分析函数调用关系。
我记得有一次审计一个IoT摄像头固件,用 binwalk 扫描后发现有LZMA压缩段。手动解压后,再用 Ghidra 分析,发现了一个未授权的远程命令执行漏洞。整个过程,工具链的配合是关键。
2.2 固件提取技术
固件提取,说白了就是把固件从设备里“拿”出来。这步做不好,后面全是白费。
提取方式分三种:硬件提取、软件提取、网络提取。我按难度从低到高给你排个序:
- 网络提取:通过设备的Web管理界面、FTP、TFTP等网络服务下载固件。最简单,但厂商通常不会把完整固件放在这里。
- 软件提取:通过设备的命令行接口(CLI)或调试串口,使用
cat、dd等命令读取Flash分区。需要你有设备的管理员权限。 - 硬件提取:直接焊接飞线到Flash芯片的引脚,用编程器或SPI读卡器读取。最麻烦,但最可靠。
⚠ 警告:硬件提取有风险!我曾经有一次焊接SPI Flash时,不小心把引脚焊短路了,芯片直接冒烟。建议新手先用烧录座练习,别直接焊板子。
硬件提取的典型流程是这样的:
- 拆开设备外壳,找到Flash芯片(通常是SOIC-8封装)。
- 用逻辑分析仪确认芯片的供电、地、CLK、MOSI、MISO引脚。
- 焊接飞线或使用烧录夹,连接到编程器(如CH341A、FT2232H)。
- 使用
flashrom或厂商软件读取整个Flash内容。
命令示例(使用 flashrom):
# 检测Flash芯片
flashrom -p ch341a_spi -V
# 读取固件到文件
flashrom -p ch341a_spi -r firmware_dump.bin
# 验证读取是否完整
flashrom -p ch341a_spi -v firmware_dump.bin
你想想看,如果设备有防读取保护(比如读保护RDP级别),那硬件提取就失效了。这时候需要更高级的手段,比如电压故障注入。嗯,这个我们后面章节再细讲。
2.3 固件解包与打包
拿到固件文件后,下一步就是解包。固件通常不是单一的可执行文件,而是多个组件的集合:Bootloader、内核、文件系统、配置数据、应用程序等。
常见的固件结构如下:
| 组件 | 常见格式 | 说明 |
|---|---|---|
| Bootloader | U-Boot、RedBoot、Barebox | 引导加载程序,负责初始化硬件并加载内核 |
| 内核 | zImage、uImage、vmlinux | Linux内核或RTOS内核 |
| 文件系统 | SquashFS、JFFS2、UBIFS、CramFS | 存放应用程序、配置文件和库文件 |
| 配置分区 | MTD分区、NVRAM、U-Boot环境变量 | 存储设备配置参数 |
解包的第一步是识别文件系统类型。我一般用 binwalk 做自动识别:
binwalk -Me firmware.bin
这个命令会递归扫描并提取所有已知类型的文件。如果 binwalk 识别不出来,那就得手动分析了。用 hexdump 查看文件头:
hexdump -C firmware.bin | head -50
常见的文件系统魔数:
- SquashFS: 以
hsqs开头(0x68737173) - JFFS2: 以
0x1985开头 - CramFS: 以
0x28cd3d45开头 - UBIFS: 以
0x31181006开头
避坑指南:我曾经遇到一个固件,binwalk 识别出了SquashFS,但解包后文件全是乱码。后来发现厂商在文件系统前加了自定义头部偏移。用 dd 跳过前512字节后,才正常解包。
手动解包SquashFS的命令:
# 假设SquashFS从偏移0x100000开始
dd if=firmware.bin of=squashfs_part.bin bs=1 skip=$((0x100000))
# 解包
unsquashfs squashfs_part.bin
打包则是解包的逆过程。修改完固件后,需要重新打包成原始格式。注意:很多固件有校验机制,比如CRC32、SHA256或数字签名。如果你修改了内容但没有更新校验值,设备会拒绝启动。
我一般用 firmware-mod-kit 这个工具集来做解包和打包,它封装了大部分常见文件系统的操作:
# 解包
./extract-firmware.sh firmware.bin
# 修改文件系统内容后,重新打包
./build-firmware.sh firmware_new.bin
但要注意,firmware-mod-kit 对某些定制固件可能不兼容。这时候就得手动分析固件结构,写Python脚本处理了。
核心原则:解包容易,打包难。打包时一定要保留原始的对齐方式和填充字节,否则固件可能变砖。