3、固件获取与预处理:从芯片、开发板、OTA包、路由器等设备提取固件的方法,固件格式识别、解包与文件系统提取
固件逆向工程的第一步,也是最关键的一步,在于“拿到”并“拆开”目标固件。无论是从Flash芯片上飞线读取、从开发板的调试接口转储、从OTA升级包中截获,还是从路由器Web管理后台下载,最终目的都是获得一个可供静态分析的二进制镜像。然而,原始镜像往往是经过Bootloader头部封装、压缩算法处理、专有文件系统打包甚至加密混淆的复合体。本章将系统讲解固件获取的四大物理/逻辑途径、固件格式的识别方法论、通用解包流程以及文件系统提取技术,帮助读者建立从“物理介质”到“可分析目录树”的完整工程链路。
3.1 固件获取的四大途径与操作要点
3.1.1 芯片直读:SPI Flash与并口NOR Flash
当设备没有提供任何软件层面的固件导出接口时,直接读取板载存储芯片是最可靠的物理获取方式。绝大多数嵌入式设备(路由器、IoT网关、工业控制器)使用SPI NOR Flash或SPI NAND Flash存储固件。对于SPI NOR Flash,常用的读取方案包括:使用编程器(如CH341A、RT809H)配合夹子或插座直接读取;或者使用树莓派GPIO模拟SPI主机,通过flashrom工具读取。操作时需注意芯片电压(3.3V或1.8V),避免带电插拔导致芯片损坏。对于BGA封装的NAND Flash,则需使用专用座子或热风枪拆焊后读取,风险较高。
3.1.2 开发板与调试接口转储
如果目标设备留有UART、JTAG或SWD调试接口,则可通过Bootloader或调试器转储固件。以U-Boot为例,进入命令行后可使用 md(内存显示)、mw(内存写)、nand read、sf read 等命令将Flash内容读取到内存,再通过 tftp 或 loady(YMODEM)传输到主机。对于JTAG/SWD接口,OpenOCD配合 flash read_bank 命令可以完整转储外部Flash。此方法的优势在于无需拆焊芯片,但要求设备能进入Bootloader或调试模式,且Bootloader未锁定。
3.1.3 OTA升级包截获与解析
智能设备通常通过OTA(Over-The-Air)方式升级固件。截获OTA包的方法包括:在路由器或PC上设置代理(如Burp Suite、Fiddler)捕获HTTPS流量并尝试SSL剥离;在局域网内使用ARP欺骗配合Wireshark抓取明文HTTP流量;或者从厂商云平台API直接下载升级包。OTA包往往采用差分升级(如bsdiff)或全量镜像+加密签名的方式。对于差分包,需先获取旧版本固件,再使用 bspatch 或 delta 工具还原完整镜像。部分厂商使用AES加密OTA包,密钥可能硬编码在Bootloader或应用层,需结合动态调试提取。
3.1.4 厂商发布包与固件聚合库
许多厂商在官网提供固件下载,或通过FTP/网盘分发。此外,一些固件聚合平台(如OpenWrt的固件选择器、DD-WRT的固件库)也提供了大量历史版本。从这些渠道获取固件最为便捷,但需注意固件版本与硬件版本(如RouterBOARD的硬件Rev)必须匹配,否则可能刷入变砖。同时,部分厂商会提供“开发版SDK”或“GPL源码包”,其中包含完整的文件系统构建脚本和内核配置,对逆向分析极具参考价值。
| 获取途径 | 工具/命令 | 适用场景 | 风险与限制 |
|---|---|---|---|
| SPI Flash编程器 | flashrom, CH341A | 无调试接口、Bootloader锁定 | 拆焊风险、NAND ECC处理复杂 |
| U-Boot命令行 | sf read, nand read, tftp | 设备可进入U-Boot | 需了解内存映射、网络环境 |
| JTAG/SWD | OpenOCD, J-Link | 开发板、工程样机 | 需目标芯片JTAG使能 |
| OTA抓包 | Burp Suite, mitmproxy | 已量产设备、远程升级 | HTTPS证书校验、差分包还原 |
| 厂商官网 | wget, curl | 公开固件、历史版本 | 版本匹配、可能加密 |
binwalk 的 -e 选项可以自动提取常见固件格式,但遇到加密或自定义格式时仍需手动分析。
3.2 固件格式识别:从魔数到熵分析
3.2.1 文件类型初步判断
拿到固件文件后,首先使用 file 命令查看文件类型。例如,file firmware.bin 可能输出 data(未知格式)或 u-boot legacy uImage。接着使用 binwalk firmware.bin 扫描文件中的签名。Binwalk通过匹配已知文件头(如 uImage 的 27 05 19 56,SquashFS的 hsqs,gzip的 1f 8b)来定位固件内部的各个组成部分。对于加密固件,binwalk可能无法识别有效签名,此时需计算文件的熵值。高熵值(接近8.0)通常表示加密或压缩数据,低熵值(低于6.5)则可能是明文代码或未压缩数据。
3.2.2 头部结构解析与自定义格式
许多厂商使用自定义头部封装固件,例如TP-Link的固件以 01 00 00 00 开头,包含固件大小、版本号、硬件ID等字段。VxWorks固件则使用 VxWorks 字符串作为标识。解析头部时,可使用 hexdump -C 查看前256字节,结合厂商文档或逆向Bootloader代码来理解字段含义。对于U-Boot的uImage格式,可使用 mkimage -l 查看头部信息,包括加载地址、入口点、操作系统类型和压缩算法。
3.2.3 压缩算法探测与解压
固件内部通常包含多个压缩段。Binwalk可以自动识别并提取gzip、lzma、xz、lzop等常见压缩流。对于无法自动识别的压缩数据,可尝试使用 binwalk -A 进行熵分析,或使用 dd 手动截取压缩流起始位置,再通过 dd if=firmware.bin of=part.bin bs=1 skip=偏移量 提取后使用 7z x 或 xz -d 解压。值得注意的是,部分固件使用LZMA的变种(如LZMA2),需使用 lzma 专用工具或Python的 lzma 模块处理。
# 扫描固件结构
binwalk firmware.bin
# 提取所有可识别文件
binwalk -e firmware.bin
# 查看熵分布(高熵区域可能为加密/压缩)
binwalk -E firmware.bin
# 手动提取uImage内核
dd if=firmware.bin of=kernel.img bs=1 skip=262144 count=1048576
mkimage -l kernel.img
3.3 文件系统提取:SquashFS、JFFS2与UBIFS
3.3.1 SquashFS只读文件系统
SquashFS是嵌入式Linux最常用的只读压缩文件系统,支持gzip、lzma、xz、zstd等压缩算法。提取SquashFS镜像可使用 unsquashfs 工具(来自squashfs-tools包)。Binwalk提取出的SquashFS段通常以 hsqs 魔数开头。如果遇到损坏或自定义的SquashFS,可使用 sasquatch(来自Binwalk的扩展工具)尝试修复和提取。SquashFS的超级块包含inode数量、压缩类型、块大小等关键信息,可通过 unsquashfs -s 查看。
3.3.2 JFFS2与UBIFS可写文件系统
JFFS2(Journalling Flash File System version 2)常用于NOR Flash,其镜像提取可使用 jefferson 工具(Python实现)。UBIFS用于NAND Flash,提取工具为 ubireader_extract_files(来自ubi_reader包)。这两种文件系统包含日志和磨损均衡元数据,提取时需注意处理擦除块头(EC header)和节点头。对于NAND Flash的OOB(Out-of-Band)数据,需在读取时一并保存,否则无法正确重组文件系统。
3.3.3 CramFS与RomFS等老旧格式
部分老旧设备(如早期IP Camera)使用CramFS或RomFS。CramFS镜像以 0x28cd3d45 魔数开头,可使用 cramfsck 工具解包。RomFS则无固定魔数,需通过文件系统结构特征识别。对于这些老旧格式,如果工具不支持,可尝试在Linux下通过 mount -o loop -t cramfs 直接挂载镜像文件(需root权限)。
| 文件系统 | 魔数/特征 | 提取工具 | 典型设备 |
|---|---|---|---|
| SquashFS | hsqs | unsquashfs, sasquatch | OpenWrt路由器、IP Camera |
| JFFS2 | 0x1985 (节点magic) | jefferson | 工业PLC、老旧路由器 |
| UBIFS | UBI# (卷标识) | ubireader_extract_files | NAND Flash设备、高端路由器 |
| CramFS | 0x28cd3d45 | cramfsck | 早期网络摄像头 |
| RomFS | 无固定魔数 | binwalk -A + 手动挂载 | 极简嵌入式系统 |
3.4 案例拆解:从TP-Link路由器提取并解包固件
背景与目标:某安全研究团队需要对TP-Link Archer C7 V5路由器进行漏洞挖掘,目标是提取其文件系统并定位Web管理接口的认证逻辑。该路由器使用Qualcomm Atheros QCA9558芯片组,Flash容量16MB,运行OpenWrt衍生固件。
初始现象与约束:设备已量产,无JTAG接口引出,U-Boot未锁定但未提供固件导出命令。团队决定采用“芯片直读+OTA抓包”双路径并行策略。OTA抓包路径因HTTPS证书固定而受阻,最终选择拆焊SPI Flash读取。
关键操作步骤:
- 硬件准备:使用热风枪(350°C,风速3级)拆下Winbond W25Q128FV(16MB SPI NOR Flash),清理焊盘后放入SOP-8测试座。
- 读取镜像:使用CH341A编程器配合
flashrom工具,命令为flashrom -p ch341a_spi -c W25Q128FV -r backup.bin,耗时约3分钟,读取两次并比对MD5确保一致性。 - 结构扫描:执行
binwalk backup.bin,输出显示在偏移0x200000处存在SquashFS文件系统,在0x1F0000处存在uImage内核。 - 提取文件系统:使用
dd if=backup.bin of=squashfs.bin bs=1 skip=2097152 count=8388608截取SquashFS段,然后执行unsquashfs squashfs.bin,成功解包出完整的/etc、/usr、/www目录。 - 分析关键文件:在
/www目录下发现cgi-bin脚本和luci配置,在/etc目录下找到shadow文件(包含root密码哈希)和config目录下的无线配置。
结果与复盘:成功提取固件后,团队通过分析 cgi-bin 中的 auth.lua 脚本,发现了一个命令注入漏洞(CVE-2023-12345)。该漏洞源于对 hostname 参数未做过滤直接拼接到 ping 命令中。复盘经验:①SPI Flash读取时务必确认芯片电压,CH341A默认3.3V,若芯片为1.8V需使用电平转换板;②binwalk的自动提取有时会遗漏尾部数据,建议手动检查 dd 截取;③OTA抓包失败后应尽早切换物理读取方案,避免浪费时间。
nanddump 配合 --oob 参数保留OOB区域,再通过 nandwrite 或 ubireader 处理。
3.5 常见误区与总结复盘
误区一:盲目依赖binwalk自动提取。Binwalk虽然强大,但面对加密固件、自定义头部或非标准压缩算法时可能失效。正确做法是先使用 binwalk -E 查看熵分布,结合 hexdump 手动分析头部结构,再决定提取策略。
误区二:忽略固件版本与硬件版本匹配。同一型号不同硬件版本(如V1、V2)的固件可能使用不同的Flash布局和文件系统格式。刷入错误版本可能导致设备变砖,分析时也会因结构不匹配而浪费大量时间。
误区三:混淆OTA差分包与全量包。差分升级包仅包含新旧版本差异,直接解包无法获得完整文件系统。需先获取旧版本固件,使用 bspatch 或 delta 工具还原。部分厂商使用 bsdiff 生成差分包,还原命令为 bspatch old.bin new.bin diff.patch。
误区四:忽视文件系统挂载时的权限问题。使用 mount -o loop 挂载镜像时,普通用户可能因权限不足而失败。建议使用 sudo 或改用 unsquashfs 等用户态工具提取,避免修改宿主机系统状态。
总结:固件获取与预处理是逆向工程的地基工程。物理读取(芯片直读、JTAG)是最可靠但成本最高的方式;逻辑获取(OTA、官网)效率高但受限于加密和版本匹配。格式识别需结合魔数扫描、熵分析和头部解析三种手段。文件系统提取则需根据Flash类型(NOR/NAND)和文件系统类型(SquashFS/JFFS2/UBIFS)选择对应工具。建议读者在实战中建立“先扫描、再分析、后提取”的标准化流程,并保存好每一步的中间产物(原始镜像、截取段、解压文件),以便回溯和验证。