3. 文件系统提取与探索:手动提取SquashFS文件系统
好,咱们继续。上一节我们把固件解包了,拿到了一个 bin 文件。但说实话,那只是第一步。真正的宝藏,都藏在文件系统里。对于思科这类设备,SquashFS 几乎是标配。为什么?因为它压缩率高,适合嵌入式设备的有限存储空间。
我个人习惯,拿到固件后第一件事就是确认文件系统的类型。别急着解压,先看看它到底是什么。
3.1 识别 SquashFS 魔数
每个文件系统都有自己独特的“身份证”,也就是魔数。SquashFS 的魔数通常是 hsqs 或者 sqsh。我们可以用 hexdump 或者 binwalk 来确认。
# 用 hexdump 查看文件头部
hexdump -C firmware.bin | head -20
# 或者直接用 binwalk 扫描
binwalk firmware.bin
如果看到类似这样的输出:
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 uImage header, ...
...
1048576 0x100000 SquashFS filesystem, little endian, version 4.0, ...
嗯,那就对了。注意看偏移量,比如这里是 0x100000。这个地址就是 SquashFS 的起始位置。
3.2 手动提取 SquashFS
知道了偏移量,提取就简单了。我们可以用 dd 命令来切分。
# 从偏移量 0x100000 开始,提取 SquashFS 部分
dd if=firmware.bin of=squashfs.img bs=1 skip=$((0x100000))
但这里有个坑:dd 默认会一直读到文件末尾。如果固件后面还有别的数据(比如 JFFS2 分区),你会把垃圾也一起读进来。更好的做法是先确定 SquashFS 的大小。
怎么确定?用 binwalk -e 可以自动提取,但咱们是手动派,得自己来。你可以用 unsquashfs -s 来查看镜像信息:
# 先试一下,看能不能识别
unsquashfs -s squashfs.img
如果报错说“文件太小”,说明你多切了或者少切了。调整一下偏移量再试。我一般会先用 binwalk 的 -Me 选项自动提取一次,然后对照它的输出,手动验证一遍。这样既能保证效率,又能加深理解。
3.3 解压与探索目录结构
拿到干净的 squashfs.img 后,解压就简单了:
# 解压到当前目录下的 squashfs-root 文件夹
unsquashfs squashfs.img
如果一切顺利,你会看到一个完整的 Linux 文件系统。来看看典型的思科设备目录结构:
| 目录 | 说明 |
|---|---|
/bin |
核心二进制文件,比如 busybox |
/sbin |
系统管理工具,httpd、ntpd 通常在这里 |
/usr/sbin |
另一个常见位置,dhcpd 可能在这里 |
/etc |
配置文件,启动脚本 |
/lib |
共享库,依赖分析的关键 |
/www |
Web 管理界面文件 |
我个人习惯先看 /etc/init.d 或者 /etc/rc.d,那里有启动脚本。从启动脚本里,你能看到哪些服务是默认开启的,它们的启动参数是什么。这比直接翻二进制文件要快得多。
3.4 定位关键二进制文件
好,现在我们来定位那三个关键目标:httpd、ntpd、dhcpd。
最直接的方法是用 find 命令:
# 在解压后的目录里搜索
find squashfs-root -type f -name "httpd"
find squashfs-root -type f -name "ntpd"
find squashfs-root -type f -name "dhcpd"
但有时候,厂商会给二进制文件改名。比如 httpd 可能叫 mini_httpd 或者 thttpd。这时候,你需要用 file 命令来识别:
# 查看文件类型
file squashfs-root/sbin/* | grep ELF
把所有 ELF 文件列出来,然后根据大小和名字猜测。我遇到过最坑的一次,httpd 被编译进了 busybox 里,根本找不到单独的二进制文件。那怎么办?只能从启动脚本里找线索,看它调用了哪个符号链接。
实战技巧: 找到二进制文件后,别急着分析。先看看它的依赖库是否完整:
# 查看动态链接库依赖
readelf -d squashfs-root/sbin/httpd | grep NEEDED
如果缺少某个 .so 文件,你的动态分析可能会失败。提前准备好对应的库文件,或者考虑静态编译的版本。
3.5 我的避坑指南
做文件系统提取这么多年,我踩过不少坑。分享几个典型的:
- 偏移量算错: 有时候固件头部有多个 uImage 嵌套,偏移量要一层层剥。我曾经因为少算了一个头部偏移,浪费了半天时间。
- 压缩算法不匹配: 老版本 SquashFS 用 gzip,新版本用 lz4 或 zstd。如果
unsquashfs报错,试试指定算法:unsquashfs -comp xz squashfs.img。 - 文件权限丢失: 解压后有些文件可能是只读的,记得用
chmod -R +w改一下权限,不然后面修改配置时会报错。 - 符号链接断裂: 嵌入式系统里大量使用相对路径的符号链接。如果你把文件系统移到别的目录下,链接可能失效。用
find -type l -xtype l可以找出所有断链。
嗯,说到这,我想起一个项目。当时分析一台思科交换机,固件里明明有 dhcpd,但启动脚本里却找不到对应的服务。后来发现,它被一个叫 dnsmasq 的程序替代了,而 dnsmasq 同时提供了 DHCP 和 DNS 功能。所以,别死盯着名字,要看功能。
3.6 本章知识体系
为了让你更直观地理解整个流程,我画了一张图:
这张图把整个流程串起来了。从固件分析开始,到识别魔数、手动提取、解压、探索目录,最后定位到关键二进制文件并做依赖分析。每一步都有对应的工具和方法。
好了,这一章的内容就到这里。记住,文件系统提取是固件逆向的基础,基础打牢了,后面的分析才能事半功倍。下一章我们会深入分析 httpd 的二进制代码,看看它的 Web 服务是怎么处理请求的。