第二章:固件提取基础
固件分析的第一步是什么?说白了,就是拿到固件本身。你可能会想,这有什么难的?把芯片拆下来,读出来不就行了?嗯,事情远没那么简单。我在早期做固件安全评估时,就吃过不少亏——有的设备加密了,有的接口被禁用了,有的甚至焊盘都给你抹掉了。所以这一章,咱们就聊聊固件提取的那些事儿。
硬件接口提取:三种最常见的“后门”
硬件提取,说白了就是直接通过物理接口把固件“读”出来。我个人习惯先看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引脚?我常用的方法是:
- 用万用表测电阻,找到VCC和GND。
- 用逻辑分析仪抓信号,看哪个引脚有规律的脉冲。
- 用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先分析一下原固件的压缩参数。
知识体系总览
下面这张图展示了本章的核心逻辑:
这张图把固件提取分成了三条路:硬件接口、软件工具、解包打包。你从哪条路走,取决于你手里有什么资源。有硬件?走左边。只有固件文件?走中间。已经拿到固件了?走右边。三条路最终都通向同一个目标——拿到可分析的固件。
公众号:蓝海资料掘金营,微信deep3321