固件提取技术:硬件提取、软件提取与文件系统挂载
固件分析的第一步,也是最关键的一步——把固件拿到手。你想想看,没有固件,后面所有的分析都是空谈。这一章我重点讲三种主流提取方式:硬件提取、软件提取,以及拿到固件后怎么把它“拆开”。
我个人习惯把提取技术分成两类:一类是物理接触,一类是逻辑漏洞。前者需要你手里有烙铁和编程器,后者需要你脑子里有漏洞思路。两条路都能走通,但适用场景完全不同。
核心原则:能用软件提取就别动硬件,动硬件之前先做足功课。我曾经因为焊盘脱落报废过一块开发板,教训深刻。
2.1 硬件提取:SPI Flash 与 JTAG
硬件提取说白了就是“物理读芯片”。设备断电,把存储芯片拆下来或用夹子夹住,用编程器把二进制数据读出来。这招对加密不强、没有防读保护的设备特别有效。
2.1.1 SPI Flash 提取
SPI Flash 是 IoT 设备里最常见的固件载体。我见过 90% 的路由器、摄像头、智能家居设备都在用 SPI 接口的 NOR Flash 或 NAND Flash。提取流程其实不复杂:
- 识别芯片型号——看丝印,比如 Winbond 25Q128、MXIC 25L256。记下型号后去查 datasheet。
- 连接编程器——用 SOP8 夹子夹住芯片,或者直接焊下来放烧录座上。我建议新手用夹子,免拆焊风险低。
- 读取数据——用 FlashROM、CH341A 或树莓派 GPIO 模拟 SPI 协议,把全片数据 dump 出来。
- 校验完整性——读两遍,比对 MD5。不一致说明接触不良,重新夹。
我的经验:SPI Flash 的 WP#(写保护)引脚和 HOLD# 引脚如果悬空,可能导致读取失败。我习惯把这俩引脚直接拉高到 3.3V,省去很多麻烦。
这里有个坑——有些芯片开启了 状态寄存器保护(SRP)。你读出来的数据全是 0xFF 或 0x00,看起来像空片。其实不是没数据,是保护位锁住了。解决办法是用编程器先清掉 SRP 位,再读。
2.1.2 JTAG 提取
JTAG 是调试接口,但也能用来提取固件。前提是目标芯片的 JTAG 没有被禁用,而且你能找到正确的引脚。
JTAG 至少需要 4 根线:TMS、TCK、TDI、TDO。再加上 VCC 和 GND,一共 6 根。我常用的工具是 OpenOCD + FT2232H 调试器。连接成功后,可以直接 dump 内部 Flash 或 SRAM。
警告:JTAG 提取速度比 SPI 慢很多,而且容易触发硬件看门狗。我曾经在 dump 到一半时设备重启,前功尽弃。建议先禁用看门狗,或者用短脉冲方式分段读取。
怎么找 JTAG 引脚?我一般用 JTAGulator 或 JLink 的扫描功能。它会自动遍历引脚组合,直到找到正确的 JTAG 信号。嗯,这里要注意——有些设备会故意把 JTAG 引脚藏在 BGA 封装下面,或者用电阻拉低/拉高来混淆,这时候就得靠原理图反推了。
2.2 软件提取:Bootloader 漏洞
软件提取比硬件提取优雅得多。不需要拆机,不需要焊接,只要一个串口或网络连接就能把固件拉出来。最常见的入口就是 Bootloader。
Bootloader 是设备上电后第一个运行的程序。它负责初始化硬件、加载操作系统。如果 Bootloader 存在漏洞,攻击者就能在系统启动前拿到控制权。
2.2.1 U-Boot 漏洞利用
U-Boot 是嵌入式 Linux 设备最常用的 Bootloader。它有一个命令行界面,通过串口交互。我见过很多设备出厂时没有禁用 U-Boot 的 autoboot 功能,而且没有设置密码。你只要在启动时按住某个键(比如空格、回车、ESC),就能进入 U-Boot 控制台。
进入控制台后,你可以执行以下命令提取固件:
# 查看 Flash 分区信息
U-Boot> mtdparts
# 读取整个 Flash 到内存
U-Boot> sf probe 0
U-Boot> sf read 0x82000000 0x0 0x1000000
# 通过 tftp 上传到 PC
U-Boot> setenv serverip 192.168.1.100
U-Boot> setenv ipaddr 192.168.1.10
U-Boot> tftp 0x82000000 firmware.bin 0x1000000
你看,只要三步,固件就到手了。我曾在一次项目中用这个办法从一台工业路由器里提取出完整的文件系统,整个过程不到 5 分钟。
关键点:U-Boot 的 sf 命令(SPI Flash 操作)和 tftp 命令(网络传输)是提取固件的核心。如果设备没有网络接口,也可以用 loadb 命令通过串口传输,但速度很慢。
2.2.2 其他 Bootloader 漏洞
除了 U-Boot,还有 RedBoot、Barebox、Lilboot 等。它们的漏洞模式类似:
- 未授权访问——没有密码保护,直接进入命令行。
- 缓冲区溢出——在输入 bootargs 或 kernel 地址时触发,获得 shell。
- 环境变量注入——通过修改 NVRAM 或 EEPROM 中的环境变量,劫持启动流程。
我遇到过一台设备,它的 Bootloader 会从 TFTP 服务器加载内核。如果 TFTP 服务器不存在,它就无限重试。我利用这个行为,在本地搭建了一个 TFTP 服务器,返回一个伪造的内核镜像,成功拿到了设备的 root shell。
避坑指南:我曾经在提取固件时忽略了 Bootloader 的校验机制。设备启动时会校验内核签名,我 dump 出来的固件虽然完整,但修改后无法启动。后来我学会了先 dump 完整的 Flash 镜像,包括 Bootloader 本身,再在本地分析分区结构。
2.3 固件解包与文件系统挂载
拿到固件二进制文件后,下一步就是把它“拆开”。固件通常是一个完整的镜像,里面包含了 Bootloader、内核、文件系统、配置数据等。我们需要把它还原成可读的文件结构。
2.3.1 识别固件格式
第一步是确定固件的封装格式。常见的有:
| 格式 | 特征 | 工具 |
|---|---|---|
| Binwalk 可识别 | 包含文件系统签名(如 SquashFS、JFFS2) | binwalk、dd |
| U-Boot 镜像 | 以 uImage 头开始(0x27051956) | dumpimage、mkimage |
| TRX 格式 | Broadcom 路由器常用,头部有魔数 | trx_tool、binwalk |
| Pkg 格式 | 某些厂商自定义打包 | 手动分析 + 脚本 |
我个人习惯先用 binwalk 扫描一遍:
$ binwalk firmware.bin
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 U-Boot version string, "U-Boot 1.1.4"
131072 0x20000 uImage header, header size: 64 bytes
131136 0x20040 Linux kernel ARM boot executable
1048576 0x100000 SquashFS filesystem, little endian, version 4.0
看到 SquashFS 签名了吗?这就是文件系统的起始位置。用 dd 命令把它切出来:
$ dd if=firmware.bin of=rootfs.squashfs bs=1 skip=1048576
2.3.2 挂载文件系统
切出文件系统后,需要挂载才能查看内容。SquashFS 是只读压缩文件系统,挂载命令如下:
$ sudo mount -t squashfs -o loop rootfs.squashfs /mnt/firmware
$ ls /mnt/firmware
bin dev etc home lib proc sbin tmp usr var
看到熟悉的 Linux 目录结构了吧?这就是设备的完整文件系统。你可以查看配置文件、检查二进制程序、寻找硬编码密码等。
如果是 JFFS2 或 YAFFS2 格式,需要用对应的工具:
# JFFS2 挂载
$ sudo modprobe mtdblock
$ sudo modprobe jffs2
$ sudo dd if=rootfs.jffs2 of=/dev/mtdblock0
$ sudo mount -t jffs2 /dev/mtdblock0 /mnt/firmware
# YAFFS2 挂载(需要 unyaffs2 工具)
$ unyaffs2 rootfs.yaffs2 /mnt/firmware
注意:有些固件使用了 LZMA、LZO、gzip 等压缩算法。Binwalk 会自动解压,但有时需要手动指定。我遇到过一种情况——固件头部被篡改,binwalk 识别不出签名。这时候我会用 hexdump 手动查看二进制数据,寻找文件系统的魔数。
2.4 本章知识体系
下面这张图总结了固件提取的完整流程,从硬件到软件,再到解包挂载:
这张图把整个流程串起来了。从顶层到底层,每一步都有对应的技术和工具。我个人建议你按照这个流程走一遍,从最简单的 SPI Flash 提取开始,逐步过渡到 Bootloader 漏洞利用,最后练习解包挂载。每一步都有坑,但踩过之后你就真正掌握了。
实战建议:找一台旧路由器练手。先拆机看 Flash 型号,用编程器读出来;再用 binwalk 解包;最后挂载文件系统,看看里面有没有硬编码密码或后门账号。我当年就是这么入门的。