3、固件提取实战:三种主流方法

固件提取,说白了就是怎么把芯片里的程序“读”出来。这步要是卡住了,后面逆向、调试都白搭。我这些年经手的项目,提取方式基本就三类:用编程器硬读、利用Bootloader漏洞软取、通过调试接口转储。今天咱们一个一个过。

核心观点:没有万能的方法。每种芯片、每种场景,适合的手段都不一样。你手里得有“三板斧”,才能见招拆招。

3.1 方法一:使用编程器读取SPI Flash

这是最直接、最“物理”的方法。适合那些固件存在外部SPI Flash里的情况,比如很多路由器、物联网设备。

典型场景:你拿到一块板子,主芯片是MT7620,旁边有个8脚芯片,型号是W25Q128。嗯,这就是SPI Flash,固件就躺在里面。

3.1.1 硬件连接

你需要一个编程器,我常用的是CH341A,便宜又好用。连接方式很简单:

  • VCC → 3.3V(注意,别接5V,会烧芯片)
  • GND → 地
  • CS → 片选
  • CLK → 时钟
  • MOSI → 主出从入
  • MISO → 主入从出
警告:我曾经有一次没注意,把VCC接到了5V上,结果“噗”的一声,芯片冒烟了。所以,连接前一定用万用表确认电压。

3.1.2 读取操作

连接好之后,打开编程器软件(比如AsProgrammer)。选择芯片型号,点击“读取”。等个几十秒,固件就出来了。

# 以W25Q128为例,16MB的Flash
# 读取成功后,保存为.bin文件
# 文件大小应该是16,777,216字节(16MB)
$ ls -la firmware.bin
-rw-r--r-- 1 user user 16777216 Mar 15 10:30 firmware.bin
小技巧:读取完成后,最好读两次,然后用diff对比一下。如果两次结果完全一致,说明读取是可靠的。如果不一致,检查一下连接线,或者降低SPI时钟频率。

3.2 方法二:利用Bootloader漏洞提取固件

这个方法更“优雅”。不需要拆芯片,只需要通过串口或网络,利用Bootloader的漏洞把固件“骗”出来。

典型场景:设备上电时,串口输出“Press any key to stop autoboot”。你按了回车,进入了U-Boot命令行。这就是机会。

3.2.1 常见漏洞类型

漏洞类型 描述 利用方式
未授权Shell Bootloader直接暴露了命令行 直接执行dump命令
缓冲区溢出 输入超长字符串导致崩溃 控制PC指针,跳转到dump代码
环境变量注入 通过修改环境变量执行恶意命令 设置bootargs为非法值
固件签名绕过 Bootloader不验证固件签名 上传自定义固件,包含dump功能

3.2.2 实战:U-Boot下dump固件

假设你进入了U-Boot命令行。可以用以下命令把Flash内容读出来:

# 1. 查看Flash信息
U-Boot> sf probe
U-Boot> sf info

# 2. 读取整个Flash到内存
U-Boot> sf read 0x82000000 0x0 0x1000000

# 3. 通过tftp发送到电脑
U-Boot> tftp 0x82000000 firmware.bin 0x1000000

电脑上启动tftp服务器,等着收文件就行。

关键点:不是所有Bootloader都这么配合。有些会限制命令,有些会要求密码。这时候就需要找漏洞了。我记得有个项目,Bootloader的“md”命令(内存显示)没有做边界检查,我硬是一点一点把固件“看”出来的。

3.3 方法三:通过JTAG/SWD接口转储Flash内容

这是最“硬核”的方法。适合那些芯片有调试接口,但Bootloader被锁、Flash被焊死的情况。

典型场景:你拿到一个工业控制器,主芯片是STM32F4,Flash是内部集成的。没有外部SPI Flash,Bootloader也进不去。这时候,JTAG/SWD就是你的救命稻草。

3.3.1 接口定义

接口 引脚 说明
JTAG TMS, TCK, TDI, TDO, nTRST 5线,功能全,但占用引脚多
SWD SWDIO, SWCLK 2线,够用,现在主流

3.3.2 连接与读取

你需要一个调试器,比如J-Link或ST-Link。连接SWDIO和SWCLK,再加上GND。然后:

# 使用J-Link Commander
$ JLinkExe -device STM32F407VG -if SWD -speed 4000

# 连接成功后,读取Flash
J-Link> connect
J-Link> savebin firmware.bin 0x08000000 0x100000

这里0x08000000是STM32的Flash起始地址,0x100000是大小(1MB)。

提示:如果芯片有读保护(RDP),直接读会失败。这时候需要先解除保护。但解除保护会擦除整个Flash,所以……嗯,你懂的。我建议你先试试能不能用“攻击”方式绕过,比如电压故障注入。

3.4 三种方法对比

我把这三种方法的特点整理了一下,方便你选型:

方法 难度 风险 适用场景 成功率
编程器读取 低 低(可能焊坏焊盘) 外部SPI Flash 高
Bootloader漏洞 中 中(可能变砖) 有串口/网络的设备 中
JTAG/SWD转储 高 高(可能触发保护) 有调试接口的芯片 中高

3.5 本章知识体系

下面这张图,帮你理清三种方法的逻辑关系:

固件提取三种方法知识体系 固件提取三种方法知识体系 固件提取 编程器读取SPI Flash Bootloader漏洞提取 JTAG/SWD转储 CH341A编程器 AsProgrammer U-Boot命令行 tftp传输 J-Link调试器 SWD接口 核心原则:没有万能方法,根据场景灵活选择 编程器→Bootloader→JTAG,从易到难,逐步尝试

3.6 避坑指南

最后,分享几个我踩过的坑:

  • 编程器夹子不可靠:我曾经用夹子夹SPI Flash,结果接触不良,读出来的数据全是0xFF。后来我都是直接焊线,或者用烧录座。
  • Bootloader漏洞利用要小心:有一次我试图利用缓冲区溢出,结果把Bootloader搞崩了,设备直接变砖。后来花了半天时间用编程器重刷才救回来。
  • JTAG/SWD速度别太高:我习惯用4000kHz,但有些板子布线不好,高速下会出错。遇到奇怪的问题,先降速试试。
  • 读保护不是闹着玩的:STM32的RDP Level 2是永久性的,一旦设置就无法解除。所以,拿到新芯片,第一件事就是检查读保护级别。
总结:固件提取是逆向的第一步,也是最关键的一步。三种方法各有优劣,你需要根据实际情况灵活选择。记住,没有最好的方法,只有最合适的方法。