第二讲:固件基础与逆向工程入门
大家好,欢迎来到第二讲。今天咱们聊聊固件结构,这是后续所有分析工作的地基。我刚开始接触固件安全时,也踩过不少坑——比如解包到一半发现文件系统不对,或者提取出来的内核根本跑不起来。嗯,今天就把这些经验一并分享给你。
固件到底长什么样?
说白了,固件就是嵌入式设备的操作系统镜像。它通常包含三个核心部分:Bootloader、Kernel 和 Rootfs。我习惯把它们比作「启动三部曲」——先引导、再加载内核、最后挂载文件系统。
核心三要素:
- Bootloader:负责硬件初始化,加载内核到内存。常见的有 U-Boot、RedBoot、Barebox。
- Kernel:操作系统的核心,管理硬件资源。通常是 Linux 内核,也可能是 VxWorks、RTOS。
- Rootfs:根文件系统,包含用户程序、库文件、配置文件。常见格式有 SquashFS、JFFS2、YAFFS2、CramFS。
你想想看,如果连固件结构都搞不清楚,后面做漏洞挖掘就像蒙着眼睛找针。我在一次路由器固件分析中,就遇到过 Bootloader 被加密的情况——解包出来全是乱码,后来才发现是厂商加了自定义头部。
固件结构示意图
下面这张图是我自己画的,帮你直观理解固件在 Flash 中的布局:
这张图展示了一个典型的 16MB SPI Flash 布局。Bootloader 在最前面,接着是内核,最后是文件系统。实际项目中,厂商可能会在头部加自定义签名或加密,这时候就需要手动分析偏移量了。
常用固件提取工具
拿到固件文件后,第一步就是提取里面的内容。我常用的工具主要有两个:binwalk 和 dd。它们各有各的脾气,配合使用效果最好。
binwalk —— 固件分析的瑞士军刀
binwalk 是我最常用的工具。它能自动识别固件中的文件系统、压缩包、内核镜像等。说白了,它就是靠「魔数签名」来猜文件类型的。
我的习惯:拿到固件后,先跑一遍 binwalk -Me firmware.bin,让它自动解包。如果解不出来,再手动分析。
常用命令一览:
| 命令 | 作用 | 个人备注 |
|---|---|---|
binwalk firmware.bin |
扫描固件中的文件签名 | 先看输出,确认有哪些组件 |
binwalk -e firmware.bin |
自动提取所有可识别文件 | 最常用,但有时会漏 |
binwalk -Me firmware.bin |
递归提取 + 数学模式扫描 | 对付加密或混淆固件有效 |
binwalk -D 'squashfs:rootfs' firmware.bin |
指定提取特定类型文件 | 当自动提取失败时手动指定 |
注意:binwalk 不是万能的。我曾经遇到一个固件,厂商把 SquashFS 的魔数改成了 0xDEADBEEF,binwalk 直接认不出来。这时候就得靠 dd 手动切了。
dd —— 最原始的切分工具
dd 是 Linux 下的数据复制工具,但用来切固件特别顺手。当你知道了某个组件的偏移量和大小,用 dd 就能精确提取。
基本用法:
# 从固件中提取 Bootloader(偏移 0x0,大小 0x30000)
dd if=firmware.bin of=bootloader.bin bs=1 skip=0 count=196608
# 提取内核(偏移 0x30000,大小 0x70000)
dd if=firmware.bin of=kernel.bin bs=1 skip=196608 count=458752
# 提取文件系统(偏移 0xA0000)
dd if=firmware.bin of=rootfs.squashfs bs=1 skip=655360
你可能会问:「偏移量怎么知道?」嗯,这就是 binwalk 的用武之地了。先用 binwalk 扫描,它会告诉你每个组件的起始地址和大小,然后你用 dd 精确切分。
固件解包与打包实战
理论说完了,咱们来点实际的。我拿一个常见的路由器固件做演示,手把手带你走一遍流程。
第一步:扫描固件结构
$ binwalk DIR-859_A1_FW122b02.bin
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 TRX firmware header, little endian, image size: 8388608
196 0xC4 LZMA compressed data, properties: 0x5D
1310720 0x140000 SquashFS filesystem, little endian, version 4.0
...
看到没?这个固件包含 TRX 头部、LZMA 压缩数据,以及 SquashFS 文件系统。偏移量 0x140000 就是文件系统的起始位置。
第二步:提取文件系统
# 用 binwalk 自动提取
binwalk -e DIR-859_A1_FW122b02.bin
# 或者用 dd 手动提取
dd if=DIR-859_A1_FW122b02.bin of=rootfs.squashfs bs=1 skip=1310720
我个人更推荐先用 binwalk -e,如果它搞不定,再手动来。有一次 binwalk 提取出来的 SquashFS 是坏的,我手动用 dd 切出来反而能用——原因可能是 binwalk 的自动提取逻辑在某些边界情况下会出错。
第三步:解包文件系统
SquashFS 需要用 unsquashfs 工具解包:
# 解包 SquashFS
unsquashfs rootfs.squashfs
# 解包后会在当前目录生成 squashfs-root 文件夹
ls squashfs-root/
bin/ dev/ etc/ lib/ mnt/ proc/ sbin/ tmp/ usr/ var/
看到熟悉的 Linux 目录结构了吗?到这里,你就可以开始分析里面的二进制文件、配置文件、Web 界面代码了。
第四步:修改后重新打包
分析完了,如果想修改固件再刷回去,就需要重新打包:
# 重新打包为 SquashFS
mksquashfs squashfs-root/ new_rootfs.squashfs -comp xz -b 131072
# 用 binwalk 重新组装固件(需要保留原头部和内核)
cat original_header.bin kernel.bin new_rootfs.squashfs > new_firmware.bin
避坑指南:我曾经在重新打包时忘了保持原来的压缩算法和块大小,结果刷进去设备直接变砖。所以,打包前一定要用 unsquashfs -s rootfs.squashfs 查看原文件系统的参数。
常见文件系统格式对比
不同厂商喜欢用不同的文件系统,我整理了一个表格,方便你快速对照:
| 文件系统 | 特点 | 常见设备 | 解包工具 |
|---|---|---|---|
| SquashFS | 只读、高压缩比 | 路由器、摄像头 | unsquashfs |
| JFFS2 | 可读写、磨损均衡 | 工业设备、旧款路由器 | jefferson / jffs2dump |
| YAFFS2 | 专为 NAND Flash 设计 | 手机、嵌入式设备 | unyaffs |
| CramFS | 古老、简单 | 老旧设备 | cramfsck |
| UBIFS | 新一代、支持大容量 | 新式路由器、IoT 设备 | ubireader / ubidump |
嗯,看到这里,你应该对固件结构有了整体认识。记住:拿到固件先扫结构,再提取组件,最后解包分析。这个流程我用了好几年,基本没出过大问题。
下一讲我们会深入 Bootloader 的逆向分析,到时候再聊 U-Boot 的那些坑。今天就到这里,动手试试吧。