固件基础与文件系统:固件结构分析、常见文件系统、固件解包与打包工具
做固件逆向,第一步就是面对那个二进制大块头。说实话,很多人一上来就想着怎么反汇编、怎么找漏洞,结果连固件都解不开,卡在第一步。我见过不少新手,拿着binwalk跑一遍,看到一堆东西就以为完事了——其实远远不够。
今天我们就聊聊固件的基础结构,以及那些你一定会打交道的文件系统。嗯,这部分内容很基础,但基础往往最容易被忽视。
固件结构:它到底长什么样?
一个典型的嵌入式固件,说白了就是一堆东西拼在一起。我拆过的固件少说也有上百个了,结构上大同小异。通常包含这么几块:
- 引导加载程序(Bootloader):比如U-Boot,负责初始化硬件、加载内核。这部分通常是固定的,很少改动。
- 内核(Kernel):Linux内核或者RTOS内核,压缩过的居多。
- 文件系统(Rootfs):存放应用程序、配置、库文件的地方。这是逆向分析的重点区域。
- 配置分区(Config/Data):保存设备设置、日志等。有时候加密,有时候明文。
- 头部信息(Header):包含魔数、版本、校验和、大小等。不同厂商的头部格式千奇百怪。
你想想看,固件逆向的第一步,其实就是把这些部分拆开。拆不开,后面什么都做不了。
核心要点:固件逆向的起点是识别结构,而不是直接反汇编。先搞清楚固件怎么组织的,再谈后续分析。
我习惯用十六进制编辑器先看一眼固件头部。比如看到 HDR0、U-Boot、7375 之类的魔数,基本就能判断是哪家的东西。有一次我遇到一个路由器固件,头部全是乱码,折腾了半天才发现厂商把头部做了简单的XOR加密——嗯,这种坑踩过一次就记住了。
常见文件系统:SquashFS、JFFS2、YAFFS2
嵌入式设备里,文件系统就那么几种。我列个表,你一看就明白:
| 文件系统 | 特点 | 常见场景 | 压缩方式 |
|---|---|---|---|
| SquashFS | 只读、高压缩比、支持多种压缩算法 | 固件根文件系统(最常见) | gzip / lzma / lzo / xz |
| JFFS2 | 可读写、支持磨损均衡、掉电安全 | 配置分区、日志分区 | zlib / 无压缩 |
| YAFFS2 | 专为NAND Flash设计、速度快 | 老旧设备、部分工业设备 | 无压缩(按页存储) |
SquashFS:你遇到最多的那个
说实话,现在90%以上的路由器、摄像头、IoT设备,根文件系统都是SquashFS。为什么?因为它只读,压缩率高,而且挂载速度快。你想想看,设备启动时不需要写文件系统,直接解压到内存就能跑,多省事。
我在项目中遇到过一台智能音箱,固件解包后发现SquashFS用了LZMA压缩。当时binwalk默认的gzip解压没跑出来,我还以为固件加密了。后来手动指定了 -e -M lzma 参数才搞定。所以啊,别太依赖工具的自动识别。
小技巧:用 binwalk -Me 固件.bin 可以递归解压,但有时候需要手动指定压缩算法。试试 binwalk -e -M lzma 固件.bin。
JFFS2:可读写,但有点慢
JFFS2是日志结构的文件系统,支持掉电安全。说白了就是不怕突然断电。但它的缺点也很明显——挂载慢,尤其是分区大的时候。我记得有一次分析一个工业网关,JFFS2分区有64MB,挂载花了将近两分钟。嗯,这种场景下,现在更多人用UBIFS了。
解包JFFS2其实不难。用 jefferson 这个工具就行:
# 安装 jefferson
pip install jefferson
# 解包 JFFS2 镜像
jefferson rootfs.jffs2 -d output_dir
打包回去的话,稍微麻烦一点。需要用 mkfs.jffs2 工具,注意指定页大小和擦除块大小。我曾经因为页大小没对齐,导致固件刷进去设备变砖——还好有备份。
警告:JFFS2的页大小和擦除块大小必须与目标设备匹配。不匹配的话,设备可能无法启动。建议从原固件中提取这些参数。
YAFFS2:老古董,但还在用
YAFFS2是专门为NAND Flash设计的。它没有压缩,直接按页存储数据。所以解包起来反而最简单——基本上就是直接提取文件。但它的缺点也很明显:空间利用率低。
我最近一次遇到YAFFS2,是在一个2015年的工业相机上。说实话,现在新设备很少用YAFFS2了,UBIFS基本取代了它。但如果你做逆向,难免会遇到老设备,所以还是得会处理。
解包YAFFS2可以用 unyaffs2:
# 解包 YAFFS2 镜像
unyaffs2 rootfs.yaffs2 output_dir
固件解包与打包工具:binwalk 和 firmware-mod-kit
工具这块,我重点说两个:binwalk 和 firmware-mod-kit。这两个基本覆盖了90%的场景。
binwalk:你的瑞士军刀
binwalk 是固件逆向的标配工具。它能做的事情很多:
- 扫描固件中的文件签名(魔数)
- 自动提取已知格式的文件
- 支持递归解包
- 自定义签名规则
我常用的命令就这几个:
# 扫描固件,看看里面有什么
binwalk 固件.bin
# 自动提取所有已知文件
binwalk -e 固件.bin
# 递归解包(遇到压缩文件继续解)
binwalk -Me 固件.bin
# 只提取特定偏移的文件
binwalk -D 'squashfs:rootfs.squashfs' 固件.bin
binwalk 的原理其实很简单——它维护了一个签名数据库,扫描二进制文件时匹配这些签名。比如看到 hsqs 就知道是SquashFS,看到 U-Boot 就知道是引导程序。
个人经验:binwalk 的签名数据库可以自己扩展。我在 ~/.config/binwalk/magic/ 下加过几个厂商自定义的魔数,效果不错。
firmware-mod-kit:打包利器
binwalk 擅长解包,但打包回去就有点力不从心了。这时候 firmware-mod-kit 就派上用场了。它本质上是一堆脚本的集合,封装了各种文件系统的打包工具。
基本用法:
# 解包固件
./extract-firmware.sh 固件.bin
# 修改文件系统后,重新打包
./build-firmware.sh 固件.bin/ 新固件.bin
我曾经用 firmware-mod-kit 修改过一个路由器的固件,把里面的telnetd换成了dropbear,然后重新打包刷回去。整个过程很顺利,但要注意一点——打包时一定要保持原固件的头部信息不变,否则设备不认。
避坑指南:我曾经因为修改了固件头部的大小字段,导致设备刷入后无法启动。后来发现厂商在头部做了CRC校验。所以修改固件后,记得重新计算校验和。用 firmware-mod-kit 的 --fix-checksum 参数可以自动修复。
知识体系:一张图看懂
下面这张图,是我自己总结的固件逆向入门知识体系。你看一眼,心里就有数了:
这张图把固件逆向的入门路径画得很清楚了。从左到右,先分析结构,再识别文件系统,最后用工具解包打包。每一步都有对应的知识点和工具。
好了,固件基础这部分就聊到这儿。记住一句话:解包是逆向的第一步,也是最重要的一步。解不开,后面都是白搭。下次遇到固件,先别急着反汇编,花点时间把结构搞清楚——你会发现后面的事情顺利很多。
公众号:蓝海资料掘金营,微信deep3321