第三章:固件获取与提取——从硬件到软件的完整链路
大家好,我是你们的固件安全讲师。今天我们来聊聊固件获取与提取这个环节。说实话,这是整个漏洞挖掘中最基础、也最容易被忽视的一步。我见过太多人拿着工具直接开搞,结果发现固件都没拿对——嗯,那后面的分析基本就是白费功夫。
固件获取,说白了就是解决一个问题:你要分析的二进制代码到底在哪? 它可能藏在芯片里、挂在官网上、或者躲在OTA升级包里。我们一条一条来看。
一、从硬件芯片读取固件
这是最硬核的方式。你手头有个设备,想直接扒它的固件。我个人习惯先看芯片型号,再决定用什么工具。
1.1 常见存储介质与接口
| 存储介质 | 常见接口 | 读取工具 | 注意事项 |
|---|---|---|---|
| SPI Flash | SPI (SOIC-8/16) | CH341A、树莓派+spi闪存编程器 | 电压匹配,3.3V为主 |
| NAND Flash | 并行接口 | 专用NAND编程器 | 坏块管理,ECC校验 |
| eMMC | MMC协议 | eMMC转接板+读卡器 | 需要焊接或飞线 |
| NOR Flash | 并行/SPI | 通用编程器 | 容量小,速度快 |
我在项目中遇到过最头疼的情况:一个IoT设备用了BGA封装的NAND Flash,引脚间距0.5mm。飞线?那简直是噩梦。后来我直接买了块同型号的开发板,从开发板上读——这招叫「曲线救国」,你想想看,有时候换个思路比硬刚更有效。
1.2 实战:用CH341A读取SPI Flash
CH341A是入门神器,便宜又好用。但要注意,它默认输出5V,很多SPI Flash是3.3V的。我曾经因为没注意电压,直接烧掉了一块Flash——嗯,从那以后我每次都会先量一下VCC引脚电压。
# 连接方式(以SOIC-8为例)
# CH341A引脚 -> SPI Flash引脚
# 1 (CS) -> 1 (CS#)
# 2 (DO) -> 2 (SO)
# 3 (WP) -> 3 (WP#)
# 4 (GND) -> 4 (GND)
# 5 (DI) -> 5 (SI)
# 6 (CLK) -> 6 (SCK)
# 7 (HOLD) -> 7 (HOLD#)
# 8 (VCC) -> 8 (VCC)
# 使用flashrom工具读取
sudo flashrom -p ch341a_spi -r firmware.bin
# 验证读取完整性
sha256sum firmware.bin
二、从官网/OTA获取固件
如果能从软件层面拿到固件,那当然比拆芯片省事多了。但这里有个坑:厂商往往不会把完整固件直接挂出来。
2.1 官网下载的常见套路
- 技术支持页面:很多厂商会把固件放在「下载中心」或「技术支持」里,但文件名可能很隐晦。比如「FW_2024_03_15.bin」这种,你根本不知道它是什么版本。
- FTP服务器:有些老牌厂商还在用FTP。我建议试试
ftp://ftp.厂商域名.com,匿名登录,说不定有惊喜。 - GitHub Releases:开源项目或部分厂商会把固件源码或二进制放在GitHub上。记得看Tags和Releases。
2.2 OTA升级包的捕获与分析
OTA升级包通常经过加密或签名。但别怕,我们一步步来。
- 抓包工具:用Burp Suite或mitmproxy设置中间人代理,捕获设备与服务器之间的HTTP/HTTPS请求。
- 识别升级包:通常是一个大文件下载请求,URL里可能包含
update、firmware、ota等关键词。 - 解密/解包:如果包体是加密的,需要逆向分析设备的解密逻辑。我遇到过一种情况:厂商用了简单的XOR加密,密钥就硬编码在二进制里——说白了,就是防君子不防小人。
# 用wget模拟OTA下载(假设已获取到URL)
wget https://ota.example.com/update/firmware_v2.1.0.bin
# 检查文件类型
file firmware_v2.1.0.bin
# 如果文件头是PK(ZIP格式),直接unzip
unzip firmware_v2.1.0.bin
# 如果是加密的,尝试常见解密方式
# 比如XOR解密(密钥0xAB)
python3 -c "
import sys
data = open('firmware_v2.1.0.bin', 'rb').read()
key = 0xAB
decrypted = bytes([b ^ key for b in data])
open('decrypted.bin', 'wb').write(decrypted)
"
三、固件解包与文件系统提取
拿到固件二进制文件后,下一步就是把它拆开。固件通常是一个大镜像,里面包含了Bootloader、内核、文件系统、配置数据等。我习惯先看文件头,判断它的格式。
3.1 常见固件格式与识别
| 格式 | 特征 | 常见于 | 解包工具 |
|---|---|---|---|
| TRX | 以"HDR0"开头 | Broadcom路由器 | trx_tool |
| U-Boot镜像 | 以"uImage"或"0x27051956"开头 | 嵌入式Linux设备 | dumpimage |
| SquashFS | 以"hsqs"或"sqsh"开头 | 只读文件系统 | unsquashfs |
| JFFS2 | 以"0x1985"开头 | 可读写文件系统 | jefferson |
| CPIO | 以"070701"或"070702"开头 | initramfs | cpio |
你想想看,如果连固件格式都认不出来,后面的分析根本无从下手。我建议先用 binwalk 扫描一遍,它能自动识别大部分常见格式。
3.2 使用binwalk进行固件分析
binwalk是我最常用的固件分析工具,没有之一。它就像固件的「CT扫描仪」,能把里面的结构一层层剥开。
# 扫描固件中的文件签名
binwalk firmware.bin
# 提取所有识别到的文件
binwalk -e firmware.bin
# 递归提取(处理嵌套的固件)
binwalk -Me firmware.bin
# 只提取特定偏移和大小的数据
binwalk -D 'squashfs:rootfs:squashfs' firmware.bin
我在项目中遇到过一个问题:binwalk扫描出来一堆东西,但文件系统提取失败。后来发现是固件头部有自定义校验,binwalk没识别出来。我手动跳过了前256字节,再跑一次——成功了。所以记住:工具是辅助,脑子才是核心。
3.3 手动解包:当工具失效时
有时候厂商会魔改文件系统格式,或者加了自定义压缩。这时候就得手动来了。
- 查找文件系统偏移:用
hexdump或xxd查看二进制,寻找文件系统魔数(Magic Number)。 - 计算大小:从魔数位置到文件末尾,或者根据固件头中的size字段。
- dd提取:用
dd if=firmware.bin of=rootfs.squashfs bs=1 skip=偏移量 count=大小提取。 - 尝试挂载:如果是SquashFS,可以用
mount -o loop,ro -t squashfs rootfs.squashfs /mnt直接挂载查看。
# 手动提取SquashFS文件系统
# 假设binwalk显示SquashFS在偏移0x100000,大小0x500000
dd if=firmware.bin of=rootfs.squashfs bs=1 skip=$((0x100000)) count=$((0x500000))
# 尝试挂载
sudo mount -o loop,ro -t squashfs rootfs.squashfs /mnt
# 如果挂载失败,可能是压缩方式不同
# 尝试unsquashfs
unsquashfs -d rootfs rootfs.squashfs
/bin、/sbin、/usr/bin 下的可执行文件,以及 /etc 下的配置文件。这些地方往往藏着漏洞。
知识体系总览
下面这张图是我自己画的,把本章的知识点串起来了。你可以把它当作一个检查清单:做固件获取时,按这个流程走,基本不会漏东西。
嗯,这张图基本把固件获取的完整链路讲清楚了。从硬件读取到软件下载,再到解包提取,每一步都有坑,但也都有对应的解法。我个人觉得,最关键的还是多动手、多踩坑——经验这东西,光看书是学不来的。
公众号:蓝海资料掘金营,微信deep3321