4. 固件解包与文件系统分析:binwalk工具使用、常见文件系统解析、手动提取
好,咱们进入正题。拿到一个固件,第一件事是什么?不是反汇编,不是找漏洞。是把它拆开,看看里面到底装了啥。说白了,固件解包就是逆向工程的第一步,也是最基础的一步。
我刚开始搞固件安全那会儿,拿到一个路由器固件,直接扔进IDA里想找漏洞。结果折腾半天,发现那是个加密的固件,连文件系统都没看到。嗯,从那以后,我养成了一个习惯:先解包,再分析。
4.1 binwalk:固件分析的瑞士军刀
binwalk这个工具,我估计搞固件的人没有不知道的。它就像一个扫描仪,能把固件里藏着的各种东西都翻出来。
安装很简单,我建议直接用pip:
pip install binwalk
当然,如果你要深度分析,最好装一下它的依赖,比如sasquatch(专门解Squashfs的)。
基本用法就一行命令:
binwalk firmware.bin
它会扫描固件,告诉你哪里有什么文件系统、哪里有什么内核、哪里有什么压缩数据。输出大概长这样:
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 TRX firmware header, little endian, image size: 8388608 bytes, ...
131072 0x20000 LZMA compressed data, properties: 0x5D, dictionary size: 65536 bytes, ...
262144 0x40000 Squashfs filesystem, little endian, version 4.0, compression:lzma, size: 8126464 bytes, ...
看到没?它直接告诉你:偏移0x40000处有一个Squashfs文件系统。这就是我们要找的东西。
binwalk -Me firmware.bin 可以自动解包所有识别到的内容。但我个人习惯先用 -e 参数手动指定,因为自动解包有时候会解出一些没用的东西。
4.2 常见文件系统:Squashfs、JFFS2、UBIFS
嵌入式设备里,文件系统就那么几种。我遇到最多的是Squashfs,其次是JFFS2,UBIFS相对少一些。咱们一个一个说。
Squashfs
Squashfs是只读压缩文件系统,路由器、IoT设备里最常见。它把整个文件系统压缩成一个块,启动时解压到内存里用。
解包Squashfs,我推荐用unsquashfs:
unsquashfs -d output_dir squashfs_image.bin
但有时候binwalk识别出来的Squashfs,直接用unsquashfs解不了。为什么?因为厂商可能用了非标准的压缩方式。我曾经遇到一个华为的路由器固件,它的Squashfs用了LZMA压缩,但标准unsquashfs只支持gzip。后来我找到了sasquatch这个工具,它支持更多压缩方式。
# 安装sasquatch
git clone https://github.com/devttys0/sasquatch
cd sasquatch
make && sudo make install
# 然后就可以解了
unsquashfs -d output_dir squashfs_image.bin
JFFS2
JFFS2是日志结构的文件系统,常用于NAND Flash。它比Squashfs复杂一些,因为它是可写的。
解包JFFS2,我一般用jefferson:
pip install jefferson
jefferson jffs2_image.bin -d output_dir
或者用更底层的工具:
# 先挂载
modprobe mtdblock
modprobe jffs2
dd if=jffs2_image.bin of=/tmp/jffs2.img
mount -t jffs2 /tmp/jffs2.img /mnt/jffs2
嗯,这里要注意,JFFS2的镜像有时候会有擦除块头(erase block header),需要先去掉才能挂载。我遇到过好几次这种情况,折腾了半天才发现是多了个头部。
UBIFS
UBIFS是JFFS2的升级版,专门为MLC NAND Flash设计。它比JFFS2更快,空间利用率更高。
解包UBIFS稍微麻烦一点,因为它需要UBI(Unsorted Block Images)层。我一般用ubireader:
pip install ubi_reader
ubireader_extract_files ubi_image.bin -o output_dir
或者手动挂载:
modprobe nandsim
modprobe ubi
modprobe ubifs
# 创建模拟NAND设备
modprobe nandsim first_id_byte=0x2c second_id_byte=0xda third_id_byte=0x90 fourth_id_byte=0x95
# 挂载UBI
ubiattach -m 0 -d 0
ubimkvol /dev/ubi0 -N rootfs -m
mount -t ubifs ubi0:rootfs /mnt/ubifs
hexdump 看看开头有没有"UBI#",如果有,那就是UBI镜像。
4.3 手动提取:当工具失效时
工具不是万能的。我遇到过很多次,binwalk识别不出来,或者解包失败。这时候就得手动来了。
手动提取的核心思路:找到文件系统的起始偏移和大小,然后用dd命令切出来。
举个例子,假设你通过分析固件结构,知道Squashfs从0x40000开始,大小是0x7D0000字节:
dd if=firmware.bin of=squashfs.bin bs=1 skip=$((0x40000)) count=$((0x7D0000))
然后就可以用unsquashfs解了。
但问题来了:你怎么知道偏移和大小?
我常用的方法:
- 看头部:很多固件有头部结构,比如TRX、BIN、CHK等。分析头部就能知道文件系统的位置。
- 找魔数:Squashfs的魔数是"hsqs"(0x68737173),JFFS2的魔数是"1985"(0x19851985),UBIFS的魔数是"UBI#"。用hexdump或者binwalk的-D参数可以搜。
- 看大小:文件系统后面通常跟着填充数据或者下一个分区。通过计算差值可以估算大小。
我曾经遇到一个固件,binwalk完全识别不出来。我手动用hexdump看了半天,发现文件系统被加密了。后来我找到了解密密钥,才解出来。嗯,那是另一个故事了。
4.4 知识体系图
下面这张图,是我自己总结的固件解包流程。你跟着走,基本不会出错。
4.5 实战:一个完整的解包案例
咱们来个完整的例子。假设你有一个路由器固件 router_fw.bin:
# 第一步:扫描
binwalk router_fw.bin
# 输出:
# 0 0x0 TRX firmware header
# 131072 0x20000 LZMA compressed data
# 262144 0x40000 Squashfs filesystem, version 4.0
# 第二步:解包
binwalk -e router_fw.bin
# 或者手动解
dd if=router_fw.bin of=squashfs.bin bs=1 skip=$((0x40000))
unsquashfs -d fw_root squashfs.bin
# 第三步:查看文件系统
ls -la fw_root/
# 你会看到 /bin, /usr, /etc, /lib 等目录
# 第四步:找关键文件
find fw_root/ -name "*.conf" -o -name "*.cfg" -o -name "*.key"
嗯,到这里,固件就成功解包了。你可以开始分析里面的配置文件、二进制程序、甚至找找有没有硬编码的密码。
- binwalk是首选工具,但不要完全依赖它
- Squashfs最常见,JFFS2和UBIFS次之
- 手动提取是必备技能,用dd和hexdump
- 遇到加密固件,先找解密方法
我个人习惯,每次拿到新固件,先跑一遍binwalk,然后手动验证一下。如果binwalk识别出来的偏移和大小跟实际不符,那就得手动分析了。你想想看,如果连文件系统都解不出来,后面的漏洞挖掘根本无从谈起。
好了,这一章就到这里。记住,解包是基础,基础不牢,地动山摇。
公众号:蓝海资料掘金营,微信deep3321