3、固件文件系统与格式:常见固件格式(BIN、HEX、ELF)、文件系统(Cramfs、Squashfs、JFFS2)、固件打包与解包工具

物联网设备的固件本质上是一个“可执行的二进制集合体”,它既包含CPU执行的机器码,也包含操作系统内核、设备驱动、根文件系统以及应用程序。在安全分析、OTA升级和漏洞挖掘场景中,我们首先面对的问题不是“如何逆向”,而是“如何正确拆开这个固件包”。不同格式(BIN、HEX、ELF)决定了固件的加载方式与地址映射;不同文件系统(Cramfs、Squashfs、JFFS2)决定了内部数据的组织与压缩策略;而打包/解包工具则是连接“物理固件”与“逻辑文件”的桥梁。本章将系统梳理这三层知识,并给出实战中的判断路径与操作流程。

固件格式与文件系统知识框架 固件格式 • BIN:裸二进制,无地址信息 • HEX:Intel HEX,含地址记录 • ELF:含段表、符号、重定位 • 判断:file + readelf + hexdump • 关键:加载地址与入口点 文件系统 • Cramfs:只读、高压缩、适合小分区 • Squashfs:只读、块压缩、支持大文件 • JFFS2:可写、磨损均衡、NAND Flash • 判断:binwalk + strings + 魔数 • 关键:压缩算法与页大小 打包/解包工具 • binwalk:固件扫描与提取 • firmware-mod-kit:自动解包 • unsquashfs / mkcramfs • jefferson / jffs2dump • 关键:偏移量 + 大小 + 校验 固件分析标准流程 ① 识别格式 file / hexdump / readelf ② 扫描结构 binwalk -Me 固件.bin ③ 提取文件系统 unsquashfs / jefferson ④ 分析内容 strings / 反汇编 / 配置 ⑤ 重打包 mkcramfs等 关键点:固件头偏移、文件系统魔数、压缩算法、大小端、分区表 常见魔数:Squashfs(0x68737173), Cramfs(0x28cd3d45), JFFS2(0x1985) 工具:binwalk, dd, firmware-mod-kit, unsquashfs, jefferson

上图展示了固件分析的核心知识框架。从格式识别到文件系统提取,再到内容分析,每一步都依赖对底层结构的准确理解。下面我们将逐一深入。

3.1 常见固件格式:BIN、HEX、ELF

核心原理

固件格式决定了二进制数据如何被解释和加载。三种格式各有其适用场景:BIN(Binary)是最原始的格式,它只是内存映像的连续字节流,没有任何地址信息,通常直接烧录到Flash的固定偏移处;HEX(Intel HEX)是一种文本格式,每行记录包含地址、数据长度和校验和,主要用于早期EPROM编程器或MCU烧录,它允许非连续地址的数据块;ELF(Executable and Linkable Format)是Linux/Unix系统下的标准可执行文件格式,包含ELF头、程序头表(段)和节头表(节),支持重定位、动态链接和调试信息,是分析路由器、智能家居网关等Linux设备固件的主要对象。

判断方法

在实际操作中,我们通常使用以下命令快速判断固件格式:

# 查看文件类型
file firmware.bin

# 查看二进制头部(前64字节)
hexdump -C firmware.bin | head -n 4

# 如果是ELF文件,查看段信息
readelf -h firmware.elf
readelf -l firmware.elf

判断要点:BIN文件通常以随机数据或特定芯片厂商的头部开始(如Broadcom的“BCM”头);HEX文件以“:”开头,每行格式为“:LLAAAARR...”;ELF文件以“\x7fELF”魔数开头。对于BIN文件,我们还需要进一步识别其内部是否包含文件系统或内核镜像,这通常需要借助binwalk。

案例拆解:一个典型路由器固件的格式识别

假设我们拿到一个名为“router_fw.bin”的文件,大小16MB。初始现象是:直接使用file命令显示“data”,无法识别。我们执行hexdump -C router_fw.bin | head -n 8,看到前4字节为“\x27\x05\x19\x56”,这并非标准魔数。接着使用binwalk router_fw.bin,输出显示在偏移0x100处存在“TRX”头部(一种常见的路由器固件封装格式),在0x1F0000处发现“Squashfs文件系统”的魔数。由此判断:该固件是TRX封装,内部包含一个Linux内核和一个Squashfs根文件系统。我们使用binwalk -Me router_fw.bin自动提取,得到内核镜像和文件系统目录树。复盘经验:对于未知BIN文件,不要盲目尝试反汇编,先做结构扫描是最高效的路径。

常见误区与总结

风险提示:不要将HEX文件直接当作BIN文件烧录。HEX是文本格式,烧录时需要先转换为BIN(可使用objcopy -I ihex -O binary)。另外,ELF文件通常包含调试符号和未初始化的数据段(.bss),在提取固件时,应使用objcopy -O binary仅提取可加载段,否则生成的BIN文件可能包含大量无用的填充数据。

总结:格式识别是固件分析的第一步,也是最容易出错的一步。务必使用file、hexdump、readelf和binwalk进行交叉验证,确认固件的封装类型和内部结构。

3.2 文件系统:Cramfs、Squashfs、JFFS2

核心原理

文件系统是固件中存放用户数据、配置、Web页面和应用程序的部分。在物联网设备中,由于Flash存储空间有限,通常使用压缩式只读文件系统。三种主流文件系统各有特点:

特性CramfsSquashfsJFFS2
读写属性只读只读可读写
压缩算法zlibgzip/lzma/lzo等zlib/rtime
最大文件大小16MB16EB(理论)取决于Flash
适用场景小容量NOR Flash大容量NAND Flash需要保存运行数据的设备
磨损均衡不支持不支持支持
典型魔数0x28cd3d450x687371730x1985

判断与提取方法

识别文件系统类型后,使用对应工具提取:

# 使用binwalk自动识别并提取
binwalk -Me firmware.bin

# 手动提取Squashfs(假设偏移为0x1F0000)
dd if=firmware.bin of=squashfs.img bs=1 skip=$((0x1F0000))
unsquashfs squashfs.img

# 手动提取Cramfs
dd if=firmware.bin of=cramfs.img bs=1 skip=$((0x10000))
# 使用cramfsck或firmware-mod-kit中的工具

# 手动提取JFFS2(需要指定页大小)
dd if=firmware.bin of=jffs2.img bs=1 skip=$((0x20000))
jefferson jffs2.img

关键参数:对于JFFS2,必须知道NAND Flash的页大小(通常为2KB或4KB)和擦除块大小(通常为128KB),否则无法正确解析节点。对于Squashfs,需要关注压缩算法和块大小,不同版本(v4.0 vs v3.0)的布局略有差异。

实战场景:从Squashfs中提取Web配置文件

在一次智能摄像头固件分析中,我们通过binwalk发现固件包含Squashfs文件系统。提取后,在/etc/目录下发现shadow文件,其中包含root用户的哈希密码。进一步分析/www/目录,发现存在一个config.php文件,其中硬编码了云服务器的。这个密钥被用于设备与云端的通信认证。通过提取该密钥,我们成功模拟了设备与云端的通信协议。复盘:Squashfs文件系统虽然只读,但其中包含的敏感信息(密码、密钥、内部IP)是安全分析的重要突破口。

常见误区与总结

经验提醒:不要只依赖binwalk的自动提取。有时binwalk会误报偏移,或者文件系统有自定义头部。建议使用binwalk -A查看ASCII码,结合strings搜索关键路径(如“/etc/passwd”、“/bin/sh”)来辅助定位文件系统的真实偏移。

总结:文件系统是固件内容的“容器”。理解Cramfs、Squashfs、JFFS2的差异,能帮助我们选择合适的提取工具和参数。对于只读文件系统,提取后应保持其原始属性,避免在重打包时破坏权限和所有权信息。

3.3 固件打包与解包工具链

核心原理

固件打包(重打包)是漏洞利用和固件修改的关键步骤。解包工具将固件拆解为可编辑的文件;打包工具则将修改后的文件重新封装为可烧录的固件。工具链的核心是处理三个问题:偏移对齐(文件系统起始地址必须与Flash分区对齐)、校验和修复(很多固件头包含CRC32或MD5校验)、大小调整(修改文件系统后,其大小变化可能影响后续分区的偏移)。

常用工具清单

工具功能典型命令
binwalk固件扫描、提取、签名识别binwalk -Me fw.bin
firmware-mod-kit自动化解包/重打包脚本集./extract-firmware.sh fw.bin
unsquashfs / mksquashfsSquashfs解包/打包unsquashfs -d rootfs rootfs.img
mkcramfs / cramfsckCramfs打包/检查mkcramfs rootfs cramfs.img
jefferson / jffs2dumpJFFS2解包/打包jefferson -d rootfs jffs2.img
dd按偏移截取/拼接数据dd if=fw.bin of=part.img bs=1 skip=1024
objcopyELF与BIN格式互转objcopy -O binary kernel.elf kernel.bin

操作流程:手动重打包一个Squashfs固件

假设原始固件结构为:头部(512字节)+ 内核(2MB)+ Squashfs(剩余空间)。我们修改了Squashfs中的某个脚本,需要重新打包。操作步骤如下:

  1. 提取原始分区:使用dd分别提取头部、内核和Squashfs镜像。
  2. 解包Squashfs:使用unsquashfs解包到rootfs/目录。
  3. 修改文件:编辑rootfs/etc/init.d/rcS,添加我们的启动脚本。
  4. 重新打包:使用mksquashfs rootfs new_squashfs.img -comp xz -b 128K。注意:压缩算法和块大小必须与原始固件一致,否则设备可能无法挂载。
  5. 拼接固件:使用cat head.bin kernel.bin new_squashfs.img > new_fw.bin。
  6. 修复校验和:如果固件头有CRC32校验,需要计算新固件的CRC并回填。可使用crc32命令或Python脚本。

案例拆解:因块大小不匹配导致的启动失败

在一次对某品牌路由器的重打包过程中,我们修改了Squashfs中的Web管理页面,使用mksquashfs默认参数(块大小128KB,gzip压缩)重新打包。烧录后设备无法启动,串口日志显示“VFS: Cannot open root device”。排查发现,原始固件的Squashfs块大小为256KB,且使用了LZMA压缩。我们重新使用-b 256K -comp lzma打包后,设备正常启动。复盘:Squashfs的块大小和压缩算法必须与内核配置的CONFIG_SQUASHFS_*选项匹配,否则内核无法识别。在打包前,应使用unsquashfs -s查看原始镜像的超级块信息。

常见误区与总结

风险提示:重打包固件可能导致设备变砖。务必在操作前备份原始固件,并确保有串口或JTAG调试接口可用于恢复。另外,修改文件系统后,文件的所有者和权限可能发生变化,建议使用sudo操作并保持root:root所有权。

总结:固件打包与解包是一项精细的工程。工具链的选择取决于文件系统类型和固件封装格式。核心原则是:保持原始参数不变(压缩算法、块大小、偏移对齐),修复所有校验,验证打包结果(使用binwalk重新扫描)。

本章要点回顾:固件格式(BIN/HEX/ELF)决定了数据的解释方式;文件系统(Cramfs/Squashfs/JFFS2)决定了内容的组织方式;工具链(binwalk/unsquashfs/mksquashfs)是操作固件的手段。在实际安全分析中,建议遵循“先识别、再扫描、后提取、慎修改”的流程,每一步都要有明确的依据和备份。