3. 固件获取与提取:从Flash芯片读取到BGA拆焊

固件逆向的第一步,也是最关键的一步——把固件拿到手。没有二进制,后面全是空谈。

我见过太多人,花了几周时间分析一个根本不完整的固件,最后发现是提取环节出了问题。所以这一章,咱们把固件获取的几种主流手段掰开揉碎了讲清楚。

3.1 从Flash芯片读取:SPI与I2C接口

嵌入式设备里最常见的固件载体就是Flash芯片。SPI Flash占绝大多数,I2C EEPROM在一些低端设备里也能见到。

3.1.1 SPI Flash读取

SPI Flash引脚少,速度快,容量大。标准25系列(如W25Q64、MX25L128)几乎统治了IoT设备市场。

硬件连接要点:

  • VCC/GND:供电必须稳定,我习惯用3.3V,别搞错电压
  • CS#:片选,拉低才能通信
  • SCK:时钟,一般10MHz以内都稳
  • MOSI/MISO:数据线,注意交叉连接
注意:很多设备会在运行时给Flash供电,在线读取时务必确认目标板电源状态。我曾经遇到过在线读取时芯片被主板拉死,数据全是0xFF的情况。

常用工具与命令:

我个人最常用的是CH341A编程器配合flashrom开源工具。便宜、稳定、社区支持好。

# 识别芯片
flashrom -p ch341a_spi -V

# 读取整个固件
flashrom -p ch341a_spi -r firmware.bin

# 校验读取结果
flashrom -p ch341a_spi -v firmware.bin

读取完成后,用hexdump或binwalk快速看一眼,确认不是全0或全1。

3.1.2 I2C EEPROM读取

I2C接口的EEPROM容量小(通常2KB~256KB),多用于存储配置参数或Bootloader。读取时注意设备地址,7位地址常见于0x50~0x57。

# 使用i2c-tools扫描总线
i2cdetect -y 1

# 读取256字节
i2cget -y 1 0x50 0x00 i 256 > eeprom.bin
小技巧:I2C总线上的上拉电阻经常被忽略。如果读取不稳定,先检查上拉电阻是否正常,4.7kΩ是常见值。

3.2 从JTAG/SWD接口转储

如果Flash芯片被封装在BGA下面,或者板子有读保护,JTAG/SWD就成了突破口。

3.2.1 JTAG接口

JTAG有4个标准信号:TMS、TCK、TDI、TDO。加上TRST和SRST可选。连接时注意目标板电压,1.8V和3.3V很常见。

常用工具:

  • OpenOCD:开源,支持芯片多
  • J-Link:商业工具,速度快
  • Bus Blaster:便宜好用
# OpenOCD连接STM32示例
openocd -f interface/jlink.cfg -f target/stm32f4x.cfg

# 读取Flash内容
flash read_bank 0 firmware.bin 0x08000000 0x100000

3.2.2 SWD接口

SWD只有两根线:SWDIO和SWCLK。ARM Cortex-M系列几乎都支持。我个人觉得SWD比JTAG更稳定,尤其在高速模式下。

关键点:SWD不需要TDO,半双工通信。调试器必须支持SWD协议,J-Link和ST-Link都行。

3.2.3 绕过读保护

很多芯片有读保护(RDP)。Level 1可以用全擦除解除,Level 2基本无解。我遇到过一台设备,RDP Level 1,但Bootloader里有个漏洞,可以通过串口命令解锁——这就是另一个故事了。

保护等级 描述 绕过难度
Level 0 无保护 直接读取
Level 1 禁止调试接口读取Flash 中等(全擦除或漏洞利用)
Level 2 永久锁定,不可回退 极高(物理攻击)

3.3 从OTA更新包提取

OTA更新包是固件逆向的“金矿”。厂商通常会把固件打包成加密或压缩的格式,但总有办法拆开。

3.3.1 抓包获取

用Wireshark抓取设备升级时的网络流量。HTTP/HTTPS、MQTT、CoAP都有可能。我习惯在路由器上做端口镜像,或者用中间人代理。

# 使用mitmproxy抓取HTTPS流量
mitmproxy --mode transparent --listen-port 8080

3.3.2 解包分析

拿到更新包后,先看文件头。常见格式:

  • 原始二进制:直接就是固件
  • 差分补丁:bsdiff/xdelta,需要原固件
  • 加密包:AES/RSA,需要找密钥
  • 容器格式:U-Boot的uImage、Intel HEX、SREC
# 查看文件头
hexdump -C update.bin | head -20

# 使用binwalk自动提取
binwalk -Me update.bin
经验之谈:很多厂商会把密钥硬编码在Bootloader里。先提取Bootloader,再解密OTA包,成功率很高。

3.4 从BGA芯片拆焊读取

这是最后的手段。当Flash芯片是BGA封装,且没有其他接口可用时,只能拆下来读。

3.4.1 拆焊工具

  • 热风枪:温度控制在280~350°C,风速适中
  • 预热台:防止PCB变形
  • 助焊剂:必须用,不然锡球不熔化
  • 镊子:陶瓷或钛合金,不导热

3.4.2 操作步骤

  1. PCB固定在预热台上,预热到100°C左右
  2. 热风枪对准芯片,均匀加热
  3. 用镊子轻轻推动芯片,感觉锡球熔化后取下
  4. 清理焊盘,植球或直接飞线到编程器
警告:BGA拆焊风险极高。我曾经拆坏过一块价值2000美元的工业控制板,就因为预热不够。新手建议先在废板上练习。

3.4.3 读取方式

拆下来的芯片可以用专用BGA编程器读取,或者飞线到SPI编程器。BGA封装的Flash内部还是SPI接口,只是引脚间距小。

# 飞线后读取
flashrom -p ch341a_spi -r bga_firmware.bin

3.5 知识体系总览

下面这张图总结了固件获取的四种路径和适用场景:

固件获取路径总览 固件获取目标 Flash芯片读取 SPI / I2C 接口 调试接口转储 JTAG / SWD OTA更新包 抓包 / 解包 BGA拆焊读取 热风枪 / 飞线 选择路径的决策因素 芯片封装 → 接口可用性 → 保护等级 → 成本与风险 优先顺序:OTA包 > SPI/I2C > JTAG/SWD > BGA拆焊

3.6 避坑指南

做了这么多年固件逆向,我踩过的坑不少。挑几个典型的说说:

  • 电压不匹配:1.8V的Flash接到3.3V编程器,芯片直接冒烟。先查datasheet
  • 在线干扰:芯片在主板上时,其他器件可能干扰SPI总线。我习惯断开CS#引脚再读
  • 读保护误判:有时候不是读保护,而是芯片进入了低功耗模式。复位一下再试
  • OTA包不完整:抓包时漏了分片,解出来固件校验失败。用Wireshark的Follow TCP Stream功能
核心原则:先软后硬,先易后难。能用OTA包解决的,别拆芯片。能用JTAG读的,别动热风枪。