3、固件文件系统分析:常见文件系统类型与关键文件识别
做固件逆向分析,拿到一个固件包之后,第一件事是什么?
我个人习惯,不是急着去反汇编,也不是去搜字符串。而是先看看它的文件系统。说白了,文件系统就是固件的骨架。你搞清楚了骨架,里面的血肉(配置、脚本、二进制)自然就清晰了。
今天咱们就聊聊固件里最常见的几种文件系统,以及怎么把它们挂载起来、怎么找到那些关键文件。
3.1 常见文件系统类型
嵌入式设备嘛,资源有限,所以文件系统也得省着点用。我这些年遇到的,主要是下面这四种:
| 文件系统 | 特点 | 常见场景 | 压缩方式 |
|---|---|---|---|
| Squashfs | 只读、高压缩比、支持大文件 | OpenWrt、LEDE、多数Linux路由器 | gzip / lzma / lzo / xz |
| Cramfs | 只读、压缩、单文件最大16MB | 老旧嵌入式设备、部分IP Camera | zlib |
| JFFS2 | 可读写、支持NAND Flash、磨损均衡 | 早期Linux嵌入式设备、部分工业设备 | zlib / lzma |
| YAFFS2 | 专为NAND Flash设计、可读写、速度快 | Android旧版本、部分IoT设备 | 无压缩(按页存储) |
嗯,这里要注意一点。很多人以为固件里只有一个文件系统,其实不是。我拆过不少设备,经常是多个分区、多种文件系统混着用。比如一个分区是Squashfs放系统文件,另一个分区是JFFS2放用户配置。
核心判断方法:用 binwalk 扫描固件,看输出里的文件系统签名。Squashfs 的签名是 hsqs,Cramfs 是 0x28cd3d45,JFFS2 有 0x1985 标记。这个我后面会细讲。
3.2 文件系统挂载与浏览
找到文件系统之后,怎么把它打开?
我刚开始做这行的时候,傻乎乎地直接 mount,结果报错一堆。后来才明白,嵌入式文件系统大多需要先解压,或者指定偏移量。
3.2.1 提取与挂载 Squashfs
Squashfs 是最常见的,也是最好处理的。我一般用两种方式:
方式一:直接用 unsquashfs 解压
# 从固件中提取 Squashfs 分区(假设偏移量是 0x100000)
dd if=firmware.bin of=rootfs.squashfs bs=1 skip=$((0x100000))
# 解压
unsquashfs rootfs.squashfs
# 解压后的文件在 squashfs-root/ 目录下
方式二:挂载到本地(适合只读浏览)
sudo mount -t squashfs rootfs.squashfs /mnt/firmware -o loop,ro
ls /mnt/firmware
小技巧:如果你不确定偏移量,用 binwalk -e firmware.bin 自动提取。它会帮你把每个文件系统都拆出来。我曾经遇到一个固件,里面嵌了三个 Squashfs 分区,binwalk 全给我扒出来了。
3.2.2 处理 Cramfs
Cramfs 比较老,但有些工业设备还在用。挂载方式跟 Squashfs 类似:
# 挂载 Cramfs
sudo mount -t cramfs rootfs.cramfs /mnt/cramfs -o loop,ro
# 或者用 cramfsck 检查和解压
cramfsck -x ./extracted rootfs.cramfs
这里有个坑。Cramfs 单个文件不能超过 16MB,所以如果你在固件里看到一个很大的文件,那肯定不是 Cramfs。我遇到过有人把大文件拆成多个小文件,然后硬塞进 Cramfs 里,结果挂载后文件内容全是乱的。
3.2.3 JFFS2 和 YAFFS2 的挂载
这两种文件系统跟 Flash 芯片绑定得很紧。直接挂载镜像文件,有时候会失败。我的经验是:
# JFFS2 挂载(需要 mtd 内核支持)
sudo modprobe mtdblock
sudo modprobe jffs2
sudo dd if=rootfs.jffs2 of=/tmp/mtdblock.img bs=1
sudo losetup /dev/loop0 /tmp/mtdblock.img
# 这里需要模拟 MTD 设备,比较复杂,我一般用 jefferson 工具
jefferson rootfs.jffs2
# YAFFS2 挂载(更麻烦,推荐用 unyaffs2)
unyaffs2 rootfs.yaffs2 ./yaffs_root
注意:JFFS2 和 YAFFS2 的镜像文件,经常带有 OOB(Out-of-Band)数据。如果你直接跳过 OOB 去挂载,文件系统会报错。我曾经花了一整天,才发现是 OOB 没处理好。后来我写了个脚本,先剥离 OOB 再挂载,就稳了。
3.3 关键文件识别
文件系统挂载好了,接下来就是找东西。我一般按这个优先级来翻:
3.3.1 配置文件(优先级最高)
配置文件中藏着设备的所有秘密。密码、密钥、API 地址、调试开关……全在里面。
- /etc/config/ —— OpenWrt 的 UCI 配置文件,比如
network、wireless、firewall - /etc/passwd, /etc/shadow —— 用户密码哈希,有时候是明文
- /etc/ssl/ —— 证书和私钥,我找到过硬编码的 HTTPS 证书
- *.conf, *.cfg, *.ini —— 各种自定义配置文件
我个人习惯,先 grep 一下 password、secret、key 这些关键词。十次里有八次能直接找到硬编码的凭据。
3.3.2 脚本文件(第二优先级)
脚本文件里经常有启动逻辑、调试接口、后门。重点关注:
- /etc/init.d/ —— 启动脚本,看哪些服务开机自启
- /bin/, /sbin/, /usr/bin/ —— 很多设备用 shell 脚本实现功能
- *.sh, *.bash —— 自定义脚本,有时候会调用 curl/wget 下载东西
嗯,这里有个经验。很多厂商会在脚本里写死调试 IP 或者 telnet 端口。我曾在 /etc/init.d/rcS 里看到一行 telnetd -l /bin/sh,这就是个典型的调试后门。
3.3.3 二进制文件(第三优先级)
二进制文件是逆向分析的主战场。但一开始,我们只需要知道哪些是关键的:
- /usr/sbin/httpd, /usr/sbin/lighttpd —— Web 服务器,常有漏洞
- /usr/bin/upnpd —— UPnP 服务,历史上漏洞很多
- 自定义命名的二进制 —— 比如
dvrHelper、ipcamApp,这些往往是厂商自己写的,质量堪忧
怎么快速识别?用 file 命令看类型,用 strings 搜敏感字符串。我一般先跑一遍 strings,看看有没有 debug、backdoor、test 这些词。
3.4 知识体系总览
为了让你更直观地理解整个分析流程,我画了一张图:
这张图把整个流程串起来了。从拿到固件,到识别文件系统类型,再到挂载解压,最后找到关键文件。你跟着这个流程走,基本不会漏东西。
3.5 实战避坑指南
最后,分享几个我踩过的坑:
- 偏移量不对: 固件里经常有头部信息,文件系统不是从 0 开始的。用
binwalk先扫一遍,别自己瞎猜。 - 文件系统损坏: 有些厂商会魔改文件系统,标准工具挂不上。这时候可以试试
binwalk -Me递归提取。 - 符号链接失效: 解压后的文件系统里,符号链接可能指向绝对路径。你最好在 chroot 环境里浏览,或者用
readlink手动检查。
我曾经遇到一个设备,它的 Squashfs 里所有文件都是 0 字节。后来才发现,厂商把文件内容放在了另一个分区,Squashfs 里只是占位符。这种骚操作,你不仔细看根本发现不了。
好了,文件系统这块就聊到这儿。你先把这些基础打牢,后面分析固件漏洞的时候,才能游刃有余。