第二章:固件提取基础

固件分析的第一步是什么?说白了,就是拿到固件本身。你可能会想,这有什么难的?把芯片拆下来,读出来不就行了?嗯,事情远没那么简单。我在早期做固件安全评估时,就吃过不少亏——有的设备加密了,有的接口被禁用了,有的甚至焊盘都给你抹掉了。所以这一章,咱们就聊聊固件提取的那些事儿。

硬件接口提取:三种最常见的“后门”

硬件提取,说白了就是直接通过物理接口把固件“读”出来。我个人习惯先看PCB板上的测试点,很多厂商为了方便调试,会留下一些“后门”。

1. SPI Flash 提取

大多数嵌入式设备的固件都存储在SPI Flash里。为什么?便宜、成熟、够用。提取SPI Flash有两种方式:

  • 在线读取:用夹子夹住Flash芯片的引脚,通过编程器直接读取。注意,有些设备会检测SPI总线上的活动,一旦发现异常就复位或擦除固件。
  • 离线读取:把Flash芯片焊下来,放到编程器上读。这招最稳,但需要热风枪和焊接技术。

关键点:SPI Flash的引脚定义通常是固定的——CS(片选)、MISO(主入从出)、MOSI(主出从入)、CLK(时钟)、VCC(电源)、GND(地)。用万用表先确认VCC和GND,别接反了,否则芯片会冒烟。

我的经验:我曾经遇到一个设备,SPI Flash的CS引脚被拉到了高电平,导致编程器无法选中芯片。后来发现是厂商在Bootloader里做了引脚复用,把CS当成了GPIO。解决办法?在芯片上电瞬间用示波器抓一下CS波形,找到那个“窗口期”再读取。

2. JTAG 调试接口

JTAG是调试和编程的标准接口。如果你能找到JTAG的引脚,恭喜你,你几乎可以控制整个芯片。JTAG通常有4个信号:TMS(模式选择)、TCK(时钟)、TDI(数据输入)、TDO(数据输出)。

怎么找JTAG引脚?我常用的方法是:

  1. 用万用表测电阻,找到VCC和GND。
  2. 用逻辑分析仪抓信号,看哪个引脚有规律的脉冲。
  3. 用JTAGulator或类似工具暴力枚举。

注意:很多现代芯片都有JTAG熔丝保护。一旦熔丝烧断,JTAG接口就被永久禁用了。这时候你只能通过其他方式提取固件。

3. UART 串口

UART是嵌入式设备最常用的调试接口。你想想看,工程师总得打印点日志吧?UART通常有3个信号:TX(发送)、RX(接收)、GND(地)。

提取UART数据的方法:

  • 用USB转串口模块连接TX和RX。
  • 设置波特率(常见的有115200、57600、38400、9600)。
  • 用PuTTY或minicom查看输出。

避坑指南:我曾经遇到一个设备,UART的TX引脚被串联了一个100欧姆的电阻,导致信号衰减严重。后来我用示波器一看,波形都快成一条直线了。解决办法?换一个高阻抗的接收器,或者直接焊掉那个电阻。

软件工具提取:没有硬件也能干活

有时候你拿不到硬件,或者硬件已经被加密了。这时候,软件工具就派上用场了。

1. Binwalk:固件分析的瑞士军刀

Binwalk是我最常用的固件分析工具。它能自动识别固件中的文件系统、压缩包、Bootloader等。

# 扫描固件中的文件系统
binwalk firmware.bin

# 提取所有识别的文件
binwalk -e firmware.bin

# 只提取特定类型的文件
binwalk -D 'png:image:png' firmware.bin

Binwalk的工作原理是什么?说白了,就是通过文件签名(Magic Number)来识别。比如,U-Boot的签名是“27051956”,Squashfs文件系统的签名是“hsqs”。

关键点:Binwalk不是万能的。如果固件被加密或混淆了,Binwalk就识别不出来。这时候你需要先解密或解混淆。

2. dd:最原始但最可靠

dd命令是Linux下的数据复制工具。虽然简单,但在固件提取中非常有用。

# 从NAND Flash中读取固件
dd if=/dev/mtd0 of=firmware.bin bs=1M

# 跳过前512字节的Bootloader
dd if=firmware.bin of=kernel.bin bs=512 skip=1

为什么用dd?因为很多嵌入式设备的Flash是直接映射到内存地址空间的。你只要知道起始地址和大小,就能用dd把固件“抠”出来。

我的经验:有一次我分析一个路由器固件,发现厂商把固件分成了4个分区。我用dd分别读取每个分区,然后拼接起来,才得到了完整的固件。嗯,这一步花了我整整一个下午。

3. Strings:快速定位敏感信息

Strings命令能从二进制文件中提取可打印的字符串。虽然简单,但非常实用。

# 提取所有字符串
strings firmware.bin

# 提取长度大于10的字符串
strings -n 10 firmware.bin

# 结合grep查找敏感信息
strings firmware.bin | grep -i "password\|admin\|root"

你想想看,很多厂商会把默认密码、硬编码密钥、调试信息留在固件里。用Strings扫一遍,往往能发现不少“惊喜”。

注意:Strings只能提取ASCII和Unicode字符串。如果固件用了压缩或加密,Strings就无能为力了。

固件解包与打包:拆开看看里面有什么

拿到固件后,下一步就是解包。常见的固件格式有:

格式 特点 解包工具
Squashfs 只读压缩文件系统,常用于路由器 unsquashfs
Cramfs 老式只读文件系统 cramfsck
JFFS2 日志型文件系统,用于NAND Flash jefferson
UBIFS 新一代NAND Flash文件系统 ubi_reader

解包命令示例:

# 解包Squashfs文件系统
unsquashfs rootfs.squashfs

# 解包Cramfs文件系统
cramfsck -x output_dir rootfs.cramfs

# 解包JFFS2文件系统
jefferson rootfs.jffs2

打包呢?有时候你需要修改固件,然后重新打包。比如,添加一个后门或者修复一个漏洞。

# 打包Squashfs文件系统
mksquashfs rootfs_dir rootfs.squashfs -comp xz

# 打包Cramfs文件系统
mkcramfs rootfs_dir rootfs.cramfs

关键点:重新打包时,一定要保持文件系统的原始结构。比如,权限、所有者、时间戳等。否则,设备可能无法启动。

避坑指南:我曾经修改了一个路由器的固件,重新打包后刷进去,结果设备变砖了。后来发现是文件系统的压缩方式不对——原厂用的是LZMA,我用了Gzip。从那以后,我每次打包前都会用binwalk先分析一下原固件的压缩参数。

知识体系总览

下面这张图展示了本章的核心逻辑:

固件提取知识体系 硬件接口提取 软件工具提取 固件解包与打包 SPI Flash(在线/离线) JTAG(调试接口) UART(串口日志) Binwalk(自动识别) dd(原始数据复制) Strings(敏感信息) Squashfs/Cramfs JFFS2/UBIFS 重新打包与验证 核心目标:获取完整、可分析的固件镜像

这张图把固件提取分成了三条路:硬件接口、软件工具、解包打包。你从哪条路走,取决于你手里有什么资源。有硬件?走左边。只有固件文件?走中间。已经拿到固件了?走右边。三条路最终都通向同一个目标——拿到可分析的固件。


公众号:蓝海资料掘金营,微信deep3321