3、固件获取与预处理:从芯片、开发板、OTA包、官网等途径获取固件,固件格式识别与解包
固件获取是嵌入式逆向工程的起点,其质量与完整性直接决定后续分析的深度与效率。在实际项目中,固件可能以裸二进制(Raw Binary)、Intel HEX、Motorola S-Record、U-Boot镜像、文件系统镜像(如SquashFS、JFFS2、CramFS)、OTA差分包、加密签名包等多种形态存在。本章将系统讲解从芯片引脚、开发板调试接口、OTA升级链路、厂商官网等渠道获取固件的方法,并深入探讨固件格式的识别技巧与解包工具链的使用,帮助读者建立一套可复用的固件预处理工作流。
上图展示了固件获取与预处理的完整工程链路。左侧为六种常见获取途径,中间为预处理核心步骤,右侧为最终输出产物,底部则对应各阶段常用的工具链。整个流程强调从物理介质到逻辑文件的逐层抽象,每一步都需要结合硬件知识、文件系统原理与二进制分析技巧。
3.1 固件获取途径与硬件操作要点
3.1.1 核心原理:固件的物理存在形态
固件本质上存储在非易失性存储器中,常见介质包括 SPI NOR Flash、NAND Flash、eMMC、SD卡以及芯片内部OTP/Flash。不同介质对应不同的读取方式:SPI NOR Flash 可通过编程器夹子或插座直接读取;NAND Flash 由于存在坏块管理和页/块结构,读取后需要额外处理;eMMC 则需通过 SD 卡座或 eMMC 转接板读取。对于 MCU 内部 Flash,通常依赖 JTAG/SWD 调试接口或芯片厂商提供的 Bootloader 模式导出。
理解固件的物理布局至关重要。例如,典型的路由器固件在 SPI Flash 中的布局为:Bootloader(U-Boot)→ 内核(Kernel)→ 根文件系统(RootFS)→ 配置分区(NVRAM/Config)。若直接读取整个 Flash,得到的镜像将包含多个逻辑分区,需要后续识别与切割。
3.1.2 操作流程:从硬件到二进制文件
- SPI Flash 读取:使用 CH341A、RT809H 等编程器,配合 SOP8/SOP16 测试夹或拆焊后读取。读取前需确认 Flash 型号、电压(3.3V/1.8V)与引脚顺序。读取完成后保存为 .bin 文件,并记录 Flash 容量(如 16MB)。
- JTAG/SWD 导出:对于 ARM Cortex-M/A 系列芯片,使用 OpenOCD + FT2232H 调试器,通过
flash read_bank 0 firmware.bin命令导出内部 Flash 内容。需注意芯片是否锁定调试端口(读保护)。 - OTA 包捕获:在设备升级时,通过代理工具(如 Burp Suite、Fiddler)拦截 HTTPS 请求,或搭建本地升级服务器,获取固件下载链接。OTA 包常为 .tar.gz、.zip、.bin 或自定义格式,需进一步解包。
- 官网/论坛收集:厂商官网的“支持/下载”页面、GitHub Releases、第三方固件社区(如 OpenWrt、DD-WRT)是重要来源。注意核对固件版本、硬件版本(HW Ver)与地区码。
- 串口导出:部分设备 U-Boot 提供
loadb、tftp命令,可通过串口或网口将内存中的固件导出。例如在 U-Boot 提示符下执行md 0x80000000 0x10000查看内存,再用mw配合tftp上传。
3.1.3 案例拆解:从智能插座提取固件
某智能插座采用 ESP8266 模组,固件存储在 2MB SPI Flash 中。初始现象:设备无法通过官方 App 升级,需要提取固件进行漏洞分析。操作过程如下:
- 拆开外壳,找到 PCB 上的 25Q16 芯片(2MB SPI NOR Flash)。
- 使用 CH341A 编程器配合 SOP8 测试夹,连接 MOSI、MISO、CLK、CS、VCC、GND 引脚。
- 读取 Flash 内容保存为
smartplug_full.bin,大小 2,097,152 字节。 - 使用
binwalk smartplug_full.bin扫描,发现偏移 0x0 处为 Bootloader,0x1000 处为固件头部,0x100000 处为文件系统。 - 提取后得到三个独立文件,进一步分析发现固件未加密,但包含硬编码的 Wi-Fi 密码与云平台 。
复盘:该案例中,由于 Flash 容量小且未启用加密,直接读取即可获得完整固件。若遇到加密固件,则需结合芯片数据手册查找 AES 密钥存储位置或利用 Bootloader 漏洞绕过校验。
3.1.4 常见误区与总结
- 误区一:认为所有固件都是单个 .bin 文件。实际上,许多设备使用 U-Boot + FIT 镜像或自定义容器格式,需先解析头部。
- 误区二:忽略 Flash 的字节序与位序。SPI Flash 通常为 MSB 优先,但某些 MCU 内部 Flash 可能采用 LSB 优先,读取后需字节交换。
- 误区三:OTA 包直接解压后即为完整文件系统。OTA 包可能包含元数据、签名、差分包,需先验证签名并还原完整镜像。
3.2 固件格式识别与解包技术
3.2.1 核心原理:魔数、熵与文件系统特征
固件格式识别的核心在于寻找“魔数”(Magic Number)——文件头部的特定字节序列。例如,U-Boot 镜像以 27 05 19 56 开头;SquashFS 文件系统以 hsqs(0x68737173)开头;JFFS2 以 85 19 开头;gzip 压缩数据以 1F 8B 开头。此外,熵值(Entropy)分析可判断数据是否加密或压缩:高熵值(接近 8.0)通常表示加密或压缩数据,低熵值(低于 6.0)则可能是明文代码或未压缩数据。
文件系统识别还需关注其超级块(Superblock)中的魔数与版本信息。例如,CramFS 的魔数为 0x28CD3D45,SquashFS 的魔数为 0x73717368(小端序)。通过 file 命令可快速识别已知格式,但自定义格式需手动分析。
3.2.2 操作流程:使用 binwalk 与手动分析
# 1. 基础扫描
binwalk firmware.bin
# 2. 详细扫描(包含熵分析)
binwalk -E firmware.bin
# 3. 自动提取已知文件
binwalk -e firmware.bin
# 4. 手动查看头部
hexdump -C firmware.bin | head -20
# 5. 搜索字符串
strings -n 8 firmware.bin | grep -i "squashfs\|uboot\|kernel"
当 binwalk 无法识别时,需手动分析:
- 使用
hexdump查看文件头部 256 字节,寻找可读字符串或规律性结构。 - 计算分段熵值,定位高熵区域(加密/压缩)与低熵区域(代码/数据)。
- 尝试常见文件系统偏移:SquashFS 通常从 0x100000 或 0x200000 开始,JFFS2 从 0x1F0000 开始。
- 使用
dd命令按偏移切割镜像,再对切割后的文件进行识别。
3.2.3 案例拆解:识别并解包某 IP 摄像头的未知固件
某 IP 摄像头固件 ipcam_fw.bin 大小为 8MB,binwalk 扫描无结果。初始现象:文件头部为 00 00 00 00,无任何魔数。分析过程:
- 使用
binwalk -E发现文件前 1MB 熵值约为 5.5,后 7MB 熵值约为 7.9,推测前段为 Bootloader 或未压缩代码,后段为压缩/加密数据。 - 在偏移 0x100000 处发现字符串
hsqs,确认存在 SquashFS 文件系统。 - 使用
dd if=ipcam_fw.bin of=rootfs.squashfs bs=1 skip=1048576 count=7340032提取文件系统。 - 使用
unsquashfs rootfs.squashfs成功解包,得到完整的 Linux 根文件系统,包含 /bin、/etc、/usr 等目录。
复盘:该案例中,固件头部被厂商自定义的 1MB 引导代码填充,导致 binwalk 无法自动识别。通过熵值分析定位高熵区域,再结合字符串搜索,成功找到文件系统偏移。此方法适用于大多数“伪装”固件。
3.2.4 常见误区与总结
- 误区一:完全依赖 binwalk 自动提取。binwalk 对未知格式或加密固件无效,需结合手动分析。
- 误区二:忽略文件系统对齐。SquashFS 通常按 4KB 对齐,偏移可能为 0x1000 的倍数,切割时需注意。
- 误区三:混淆大小端。ARM 小端设备与 MIPS 大端设备的文件系统魔数可能不同,需根据架构调整。
| 工具 | 用途 | 典型命令 |
|---|---|---|
| binwalk | 固件扫描与提取 | binwalk -e firmware.bin |
| file | 识别已知文件类型 | file rootfs.squashfs |
| hexdump | 查看二进制头部 | hexdump -C -n 256 firmware.bin |
| unsquashfs | 解包 SquashFS | unsquashfs -d ./rootfs rootfs.squashfs |
| jefferson | 解包 JFFS2 | jefferson rootfs.jffs2 |
| firmware-mod-kit | 自动化固件解包/重打包 | ./extract-firmware.sh firmware.bin |
| 7z | 解压常见压缩格式 | 7z x firmware.tar.gz |
3.3 固件预处理:从原始镜像到可分析目标
3.3.1 核心原理:分区切割与文件系统挂载
获取的原始固件通常包含多个逻辑分区,预处理的目标是将这些分区分离,并转换为可挂载、可浏览的文件系统。对于 Linux 设备,常见分区包括:Bootloader、Kernel(zImage/uImage)、RootFS(SquashFS/EXT4/JFFS2)、Config(NVRAM/UBI)。切割后,RootFS 可通过 mount -o loop 挂载(需内核支持),或使用 unsquashfs 等工具直接解包。
对于 NAND Flash 设备,还需处理 OOB(Out-of-Band)数据与坏块标记。通常使用 nanddump 工具读取时自动跳过坏块,但若手动读取,需在预处理阶段过滤 OOB 区域。
3.3.2 操作流程:标准化预处理步骤
- 镜像完整性校验:计算 SHA-256 哈希,与官方发布值比对(如有)。
- 分区扫描与切割:使用 binwalk 或手动分析确定各分区偏移与大小,用
dd切割。 - 内核解压:若内核为 uImage,使用
mkimage -l查看信息,再用dd跳过 64 字节头部提取 vmlinuz,最后用zcat或lzma -d解压。 - 文件系统解包:根据类型选择工具,如 SquashFS 用
unsquashfs,JFFS2 用jefferson,EXT4 用mount -o loop。 - 符号与配置提取:从解包后的文件系统中提取 /etc/passwd、/etc/shadow、Web 配置文件、SSL 证书、私钥等敏感信息。
3.3.3 案例拆解:某企业级 AP 固件的完整预处理
某企业级无线 AP 固件 ap_fw.bin 大小 32MB,目标为提取 Web 管理后台的源码与默认凭据。操作过程:
- binwalk 扫描发现:0x0 为 U-Boot,0x80000 为 LZMA 压缩内核,0x200000 为 SquashFS,0x1A00000 为 JFFS2 配置分区。
- 使用
dd切割出内核与两个文件系统。 - 解压内核后,使用
vmlinux-to-elf工具将 vmlinuz 转换为 ELF 文件,便于 Ghidra 分析。 - 解包 SquashFS 后,在 /www 目录找到 CGI 脚本与 JavaScript 文件,发现存在命令注入漏洞。
- 在 JFFS2 配置分区中找到默认管理员密码的 MD5 哈希,通过彩虹表破解。
复盘:该案例体现了预处理对后续漏洞挖掘的关键作用。通过分区切割与文件系统解包,不仅获得了可分析的源码,还提取了敏感配置。若跳过预处理直接分析整个 .bin 文件,将难以定位具体代码模块。
3.3.4 常见误区与总结
- 误区一:忽略 U-Boot 环境变量。U-Boot 的 env 分区可能包含启动参数、内存布局等关键信息,需单独提取。
- 误区二:未处理文件系统权限。解包后的文件可能保留原始 UID/GID,在宿主机上浏览时需使用 root 权限或映射用户。
- 误区三:忘记检查固件更新包中的版本信息。同一设备不同版本的固件,其分区布局可能不同,需以目标版本为准。
tree 命令生成解包后的目录树,并保存 binwalk 扫描日志,便于回溯。