固件逆向基础:提取、解包与文件系统分析
各位同学,欢迎来到第二章。上一章我们聊了固件安全的整体威胁模型,今天咱们要动真格的了——把固件从芯片里“请”出来,拆开看看里面到底装了些什么。
我经常跟团队里新人说一句话:你连固件都拿不到,谈什么逆向? 提取是第一步,也是最容易踩坑的一步。我自己早年做第一个路由器漏洞挖掘时,就因为没注意SPI Flash的电压问题,连续烧了三块开发板……嗯,从那以后我养成了先看datasheet再动手的习惯。
本章核心目标:掌握从硬件和软件两个维度获取固件的方法,学会解包/打包常见固件格式,并能独立分析文件系统结构。
2.1 固件提取技术
固件提取,说白了就是把二进制镜像从存储介质里读出来。根据你的目标设备不同,手段也完全不一样。我把它分成两大类:硬件提取和软件提取。
2.1.1 硬件提取:直接与芯片对话
当设备没有开放调试接口、固件更新包又加密时,硬件提取往往是唯一出路。最常见的场景就是嵌入式设备上的SPI Flash或NAND Flash。
常用工具清单:
| 工具 | 适用场景 | 个人评价 |
|---|---|---|
| CH341A编程器 | SPI Flash(8脚/16脚) | 便宜好用,入门首选,但速度慢 |
| 树莓派 + flashrom | SPI Flash在线读取 | 灵活,适合现场快速提取 |
| JTAG/SWD调试器 | MCU内部Flash | 需要找到调试引脚,有时得刮漆 |
| NAND读取器 | NAND Flash(BGA封装) | 贵,但遇到大容量存储必须上 |
我个人习惯先用flashrom配合树莓派做SPI Flash提取。为什么?因为不用拆焊芯片,直接在电路板上夹住引脚就能读。但要注意——目标设备必须断电,否则轻则读错数据,重则烧掉IO口。
避坑指南:我曾经遇到过一块看似正常的SPI Flash,读出来的数据校验总是失败。折腾了半天才发现,是夹子接触不良导致某根数据线虚接。后来我养成了习惯:每次读取前先用万用表测一遍引脚通断。
2.1.2 软件提取:从系统内部下手
如果设备已经跑起来了,或者你能拿到固件更新包,那软件提取就省事多了。常见途径包括:
- 固件更新包抓取:用Wireshark抓OTA升级流量,或者从SD卡/USB升级文件里直接提取.bin文件
- /dev/mem或/dev/flash:在已root的设备上,直接dd整个Flash分区
- Bootloader交互:通过U-Boot的md命令或fastboot协议读取内存
- 调试串口:在系统启动时中断进入uboot shell,用sf read命令导出
举个例子,我在分析某款IP摄像头时,发现它的固件更新包是个加密的.bin文件。但通过抓取串口启动日志,我找到了uboot环境变量里存储的解密密钥——这就是典型的“硬件泄露软件秘密”案例。
2.2 固件解包与打包
拿到原始二进制文件后,下一步就是把它拆开。固件通常不是单一的文件,而是由Bootloader、内核、文件系统、配置分区等多个部分拼接而成。
2.2.1 识别固件结构
我一般先用binwalk做快速扫描。这个工具能自动识别固件里嵌入的文件系统和压缩数据。
# 基本扫描
binwalk firmware.bin
# 递归扫描并提取
binwalk -Me firmware.bin
# 查看熵值,判断是否加密
binwalk -E firmware.bin
输出结果会告诉你:偏移地址、文件类型、大小。比如看到“Squashfs filesystem, little endian”这样的标记,就说明文件系统找到了。
注意:binwalk不是万能的。如果固件头部被篡改或加密,它可能什么都识别不出来。这时候我会手动用hexdump或010 Editor查看文件头特征——比如U-Boot的“uImage”头以“27051956”开头,这是经验活。
2.2.2 常用解包工具
| 工具 | 用途 | 命令示例 |
|---|---|---|
| binwalk | 自动识别与提取 | binwalk -Me firmware.bin |
| unsquashfs | 解压SquashFS | unsquashfs rootfs.squashfs |
| jefferson | 解压JFFS2 | jefferson rootfs.jffs2 |
| ubireader | 解压UBIFS | ubireader_extract_images firmware.bin |
| firmware-mod-kit | 解包/打包一体化 | ./extract-firmware.sh firmware.bin |
说到打包,很多同学只解不解,改完东西装不回去。我建议你从一开始就养成习惯:解包前先备份原始固件,并记录每个分区的偏移和大小。打包时用mksquashfs或ubinize重新生成,再用dd拼回完整镜像。
# 重新打包SquashFS示例
mksquashfs extracted_rootfs/ new_rootfs.squashfs -comp xz -b 131072
# 拼回完整固件
dd if=bootloader.bin of=new_firmware.bin bs=1 conv=notrunc
dd if=kernel.bin of=new_firmware.bin bs=1 seek=0x80000 conv=notrunc
dd if=new_rootfs.squashfs of=new_firmware.bin bs=1 seek=0x300000 conv=notrunc
个人经验:我曾经在打包时忘了对齐分区边界,结果刷进去后内核能启动,但文件系统挂载失败。折腾了两天才发现是偏移地址没对齐到Flash的擦除块大小(通常是64KB或128KB)。记住:分区偏移必须是擦除块大小的整数倍。
2.3 固件文件系统分析
文件系统是固件里最“有料”的部分。Web服务、二进制程序、配置文件、脚本……漏洞往往就藏在这些文件里。
2.3.1 常见嵌入式文件系统
| 类型 | 特点 | 常见设备 |
|---|---|---|
| SquashFS | 只读、高压缩比 | 路由器、摄像头 |
| JFFS2 | 可写、支持NAND Flash | 老旧嵌入式设备 |
| UBIFS | 新一代、支持大容量NAND | 高端路由器、IoT网关 |
| CramFS | 古老、已淘汰 | 早期Linux设备 |
| initramfs | 内存文件系统 | 临时启动环境 |
解包完成后,我通常会按以下顺序做初步分析:
- 查看/etc/passwd和/etc/shadow——找硬编码密码或弱哈希
- 检查/etc/init.d/或/etc/rc.d/——看启动脚本里有没有可疑服务
- 搜索/bin和/usr/bin下的二进制文件——用
file命令确认架构,用strings找敏感字符串 - 查看Web目录(/www、/htdocs等)——找后门、硬编码Token、命令注入点
- 检查/lib和/usr/lib下的动态库——看有没有过时的OpenSSL或libcurl版本
一个实用技巧:用grep -r "password\|secret\|token\|admin" extracted_rootfs/快速扫描硬编码凭据。我在某款智能门锁的固件里,就靠这行命令找到了一个硬编码的API密钥——厂商居然把它写死在/lib/libauth.so里。
2.3.2 文件系统结构图
下面这张图展示了典型嵌入式Linux固件的文件系统层次,以及各目录在安全分析中的关注点。
2.4 本章小结
这一章我们走完了固件逆向的“三板斧”:提取、解包、分析。你想想看,从一块芯片上的二进制数据,到能逐行阅读的源代码和配置文件,这个过程本身就是一种“破解”的快感。
最后提醒一句:拿到固件只是开始,不是结束。下一阶段我们要在这些文件里“挖洞”——但那是后面章节的事了。今天的内容,足够你回去拿手边的路由器练练手了。
课后练习建议:找一个开源路由器的固件(比如OpenWrt或DD-WRT),用binwalk解包后,尝试找到Web管理界面的登录页面文件,看看有没有硬编码的测试账号。我当年第一次做这个练习时,就在一个厂商的固件里找到了“admin:admin”的备份账号——他们居然忘了删掉。