固件提取技术:从物理芯片到文件系统
固件分析的第一步,也是最关键的一步——把固件从设备里「请」出来。我见过不少新手,拿着一个 IoT 设备,翻来覆去不知道从哪下手。其实说白了,提取固件就两条路:硬件方法和软件方法。
这一章,我会把这两条路都给你讲透。包括我踩过的坑,和一些实战技巧。
硬件提取方法
硬件提取,就是直接跟芯片打交道。设备不开机、系统崩溃、或者压根没有软件接口的时候,这是唯一的路。
SPI Flash 提取
大部分嵌入式设备,固件都存在 SPI Flash 里。这种芯片通常 8 个脚,长得像个小蜘蛛。我刚开始做的时候,总觉得这玩意儿很神秘,后来发现,说白了就是把它当 U 盘读。
常用的工具有:
- 编程器:CH341A、FT2232H、树莓派的 GPIO 也能干这活
- 夹子:SOP8 夹子,免拆焊,但容易接触不良
- 热风枪:实在不行就吹下来,焊到转接板上读
我的习惯:先用夹子试,不行再拆焊。夹子读数据时,手要稳,最好用胶带固定一下。我曾经因为手抖,读出来的固件全是 0xFF,白忙活半天。
读取命令很简单,以 CH341A 为例:
# 安装工具
sudo apt-get install flashrom
# 读取整个 Flash,保存为 bin 文件
sudo flashrom -p ch341a_spi -r firmware.bin
# 验证读取是否成功
sudo flashrom -p ch341a_spi -v firmware.bin
注意:读取前一定要确认 Flash 型号和电压。1.8V 的芯片用 3.3V 去读,直接烧掉。我有个同事就干过这事,芯片冒烟了。
JTAG 提取
JTAG 是调试接口,但也能用来 dump 固件。它比 SPI 更「暴力」——直接通过 CPU 把整个内存空间读出来。
JTAG 接口通常有 4 个信号线:
| 信号 | 功能 | 常见颜色 |
|---|---|---|
| TMS | 模式选择 | 橙色 |
| TCK | 时钟 | 黄色 |
| TDI | 数据输入 | 绿色 |
| TDO | 数据输出 | 蓝色 |
找 JTAG 接口是个技术活。我常用的方法:
- 看 PCB 上有没有 4 个或 5 个一排的过孔
- 用万用表量,JTAG 的 TMS 和 TCK 通常有上拉电阻
- 用 JTAGulator 或 J-Link 的扫描功能自动识别
实战经验:有一次我拿到一个路由器,SPI Flash 被涂了黑胶,没法直接读。最后通过 JTAG 把整个固件 dump 出来了。JTAG 的速度比 SPI 慢,但胜在不需要拆芯片。
软件提取方法
软件方法的前提是——设备能开机,而且你能拿到 shell 权限。不管是 telnet、SSH 还是串口,只要能执行命令就行。
dd 命令
dd 是 Linux 下的「磁盘克隆」工具。在嵌入式设备上,它能把 Flash 分区整个读出来。
先看看分区表:
cat /proc/mtd
# 输出示例:
# dev: size erasesize name
# mtd0: 00040000 00010000 "bootloader"
# mtd1: 00020000 00010000 "config"
# mtd2: 007a0000 00010000 "firmware"
然后 dump 每个分区:
# 读取 bootloader 分区
dd if=/dev/mtd0 of=/tmp/bootloader.bin
# 读取整个固件分区
dd if=/dev/mtd2 of=/tmp/firmware.bin
# 如果设备有网络,直接传到本地
nc -l -p 1234 > firmware.bin # 本地执行
dd if=/dev/mtd2 | nc 192.168.1.100 1234 # 设备上执行
避坑指南:我曾经用 dd 读一个 16MB 的 Flash,结果设备内存只有 8MB,直接 OOM 了。后来改用 dd bs=1024 count=16384 分段读取,才搞定。
binwalk 提取
binwalk 是固件分析的瑞士军刀。它不仅能提取,还能识别固件里嵌了什么东西。
基本用法:
# 扫描固件,看看里面有什么
binwalk firmware.bin
# 自动提取所有文件
binwalk -e firmware.bin
# 只提取特定类型的文件(比如 squashfs)
binwalk -D 'squashfs:squashfs' firmware.bin
binwalk 的识别能力很强。它通过文件头签名来识别:
| 文件类型 | 文件头签名 | 说明 |
|---|---|---|
| U-Boot | 0x27051956 | 常见的 bootloader |
| SquashFS | hsqs / sqsh | 只读文件系统 |
| JFFS2 | 0x1985 | 可写文件系统 |
| LZMA | 0x5D0000 | 压缩数据 |
| gzip | 0x1F8B | 压缩数据 |
为什么 binwalk 这么重要? 因为很多固件是「套娃」结构——外面一层 U-Boot,中间是压缩的内核,最里面才是文件系统。binwalk 能一层层帮你剥开。
固件解包与文件系统挂载
拿到固件文件后,下一步就是把它解包,挂载文件系统,这样才能看到里面的配置、脚本、二进制程序。
解包工具
除了 binwalk,还有一些专用工具:
- unsquashfs:解压 SquashFS 文件系统
- jefferson:解压 JFFS2 文件系统
- firmware-mod-kit:一个集成的固件解包/打包工具集
举个例子,解包一个 SquashFS 文件系统:
# 先用 binwalk 找到文件系统的偏移
binwalk firmware.bin
# 假设 SquashFS 在偏移 0x100000 处
# 用 dd 切出来
dd if=firmware.bin of=rootfs.squashfs bs=1 skip=$((0x100000))
# 解包
unsquashfs rootfs.squashfs
# 解包后的文件在 squashfs-root 目录下
ls squashfs-root/
挂载文件系统
有时候你不想解包,只想看看里面的内容。那就直接挂载:
# 挂载 SquashFS(只读)
sudo mount -t squashfs rootfs.squashfs /mnt/firmware
# 挂载 JFFS2(需要模拟 MTD 设备)
modprobe mtdblock
modprobe jffs2
dd if=rootfs.jffs2 of=/tmp/jffs2.img
sudo mount -t jffs2 /tmp/jffs2.img /mnt/firmware
注意:挂载 JFFS2 比较麻烦,因为它需要 MTD 设备模拟。我建议直接用 jefferson 解包,省事。
知识体系总览
下面这张图,把固件提取的整个流程串起来了。你可以把它当作一个「决策树」——根据你的设备状态,选择对应的提取方法。
嗯,这张图基本把本章的内容都串起来了。你从最顶上的「固件提取」开始,根据设备能不能开机,选择左边硬件或者右边软件。最后汇合到「固件解包与挂载」——这是分析前的最后一步。
我个人建议,刚开始做固件分析时,先从软件方法入手。找个能开机的路由器,用 dd 和 binwalk 练手。等熟悉了流程,再挑战硬件提取。毕竟,拆芯片这种事,手一抖就是几百块的成本。