第1章:固件逆向工程基础——二进制文件格式解析与工具链搭建
各位同学,欢迎来到《思科固件后门查找与隐蔽功能分析实战课程》。我是你们的老朋友,一个在固件逆向坑里摸爬滚打多年的工程师。今天咱们聊点实在的——二进制文件格式、解包打包工具链,还有CPU架构识别与交叉编译环境搭建。
说实话,我刚开始接触固件逆向时,也踩过不少坑。有一次拿到一个思科路由器的固件,直接拿binwalk去解,结果解出来一堆乱码。后来才发现,我连文件格式都没搞清楚。嗯,这节课就是帮你避免这种尴尬。
1.1 二进制文件格式解析:ELF、TRX、UBI
固件逆向的第一步,就是搞清楚你手里拿的是什么格式的文件。不同的格式,解析方式完全不同。我习惯先拿file命令看一眼,再决定下一步怎么走。
1.1.1 ELF格式
ELF(Executable and Linkable Format)是Linux系统下最常见的可执行文件格式。思科很多基于Linux的固件,核心组件都是ELF格式。说白了,它就是操作系统和应用程序之间的桥梁。
ELF文件的结构其实不复杂,我画个图帮你理解:
你想想看,ELF文件头里记录了文件类型、目标架构、入口点地址这些关键信息。我常用readelf -h来查看文件头,用objdump -d来反汇编代码段。有一次我在分析一个思科交换机固件时,发现它的入口点地址指向了一个奇怪的位置,顺着查下去,果然发现了一个隐藏的后门函数。
关键点:ELF文件中的.text段是代码段,.data段是已初始化数据段,.bss段是未初始化数据段。后门代码通常藏在.text段里,但有些狡猾的开发者会把它塞到.init或.fini段里。
1.1.2 TRX格式
TRX是Broadcom(博通)芯片组路由器固件的常见格式。思科很多早期家用路由器都用这个格式。说白了,TRX就是一个简单的固件容器,里面塞了内核、文件系统、还有一些元数据。
TRX的结构很简单:
- 头部(28字节):包含魔数(HDR0)、长度、CRC32校验、标志位、版本号
- 分区1:通常是Linux内核(zImage)
- 分区2:通常是根文件系统(SquashFS或JFFS2)
- 分区3:可选,可能是配置文件或附加数据
我记得有一次,一个朋友拿了个思科WRT54G的固件给我,说解不开。我一看,TRX格式,头部CRC校验不对。后来发现是固件被修改过,有人把后门代码塞到了分区3里,然后重新计算了CRC。嗯,这种手法在固件后门里很常见。
小技巧:用hexdump -C查看TRX文件的前28字节,如果看到HDR0字样,基本可以确定是TRX格式。然后用dd命令按偏移量提取各个分区。
1.1.3 UBI格式
UBI(Unsorted Block Images)是NAND Flash上常用的文件系统格式。思科中高端设备,比如Catalyst系列交换机,很多都用UBI。它比TRX复杂得多,支持坏块管理、磨损均衡这些高级功能。
UBI的结构包含:
- UBI头部:包含卷ID、逻辑块号、数据长度、CRC校验
- UBI卷:每个卷可以是一个文件系统(UBIFS)或一个原始分区
- 擦除块:NAND Flash的最小擦除单位,通常128KB或256KB
我曾经在分析一个思科ASA防火墙固件时,遇到了UBI格式。当时用binwalk解出来一堆碎片,后来才发现需要先提取UBI卷,再用ubireader工具解析。你想想看,如果一开始就搞错了格式,后面全是白费功夫。
注意:UBI格式的固件,解包时一定要先确认NAND Flash的页大小和擦除块大小。搞错了这两个参数,解出来的数据全是乱的。我曾经因为这个浪费了整整两天时间。
1.2 固件解包与打包工具链
工具链是固件逆向的武器。我习惯用一套组合拳:binwalk做初步扫描,firmware-mod-kit做深度解包,必要时自己写Python脚本补刀。
1.2.1 binwalk
binwalk是固件逆向的瑞士军刀。它能自动识别固件里的文件系统、内核、引导加载程序等。我常用的命令:
# 扫描固件,识别文件类型和偏移量
binwalk firmware.bin
# 提取所有识别的文件
binwalk -e firmware.bin
# 只提取特定类型的文件(比如SquashFS)
binwalk -D 'squashfs:rootfs:squashfs' firmware.bin
# 查看固件的熵值,判断是否加密
binwalk -E firmware.bin
说实话,binwalk也不是万能的。有一次我遇到一个思科固件,binwalk死活识别不出文件系统。后来我手动看了下熵值图,发现固件被加密了。嗯,这种情况就需要先解密,再解包。
1.2.2 firmware-mod-kit
firmware-mod-kit(FMK)是binwalk的升级版,专门用于解包和重新打包固件。它支持多种文件系统格式,包括SquashFS、CramFS、JFFS2等。
FMK的使用流程:
- 解包:
./extract-firmware.sh firmware.bin - 修改:在解包后的目录里修改文件
- 打包:
./build-firmware.sh firmware/ new_firmware.bin
我个人习惯在解包后,先用tree命令看看目录结构,再决定从哪里入手。FMK的extract-firmware.sh脚本会自动识别固件格式,调用相应的解包工具。但要注意,有些固件用了自定义的文件系统,FMK可能搞不定。
避坑指南:我曾经用FMK重新打包一个思科固件后,刷到设备上直接变砖。后来发现是打包时没有正确设置文件系统的块大小。建议在打包前,先查看原始固件的文件系统参数,保持一致。
1.3 CPU架构识别与交叉编译环境搭建
固件逆向的核心是分析二进制代码。但不同CPU架构的指令集完全不同。你想想看,用x86的反汇编器去分析MIPS的代码,那不是鸡同鸭讲吗?
1.3.1 CPU架构识别
识别CPU架构的方法有很多,我常用的:
- 看文件头:ELF文件的
e_machine字段直接告诉你架构(如0x08表示MIPS,0x28表示ARM) - 看字符串:固件里通常有交叉编译工具链的路径信息,比如
/opt/toolchains/mips-linux-gnu/ - 看指令特征:MIPS的延迟槽、ARM的Thumb指令,都有明显的特征
我记得有一次,一个思科固件里的二进制文件,用file命令显示是"data",但用readelf -h一看,发现是MIPS架构。原来文件头被故意破坏了,但ELF头部信息还在。嗯,这种小把戏骗不了老手。
1.3.2 交叉编译环境搭建
交叉编译环境是固件逆向的必备工具。你需要用目标架构的编译器来编译测试代码,或者编译一些辅助工具(比如gdbserver)。
我常用的交叉编译工具链:
| 架构 | 工具链名称 | 下载地址 |
|---|---|---|
| MIPS (大端) | mips-linux-gnu | https://buildroot.org/ |
| MIPS (小端) | mipsel-linux-gnu | https://buildroot.org/ |
| ARM (32位) | arm-linux-gnueabihf | https://developer.arm.com/tools-and-software |
| ARM (64位) | aarch64-linux-gnu | https://developer.arm.com/tools-and-software |
搭建交叉编译环境的步骤:
- 下载工具链:从官方源或第三方(如Linaro、Buildroot)下载
- 设置环境变量:
export PATH=$PATH:/path/to/toolchain/bin - 测试编译:写一个简单的Hello World程序,用交叉编译器编译
- 验证:用
file命令检查生成的可执行文件,确认架构正确
# 示例:用MIPS交叉编译器编译Hello World
mips-linux-gnu-gcc -o hello hello.c
file hello
# 输出:hello: ELF 32-bit MSB executable, MIPS, MIPS32 rel2 version 1
注意:交叉编译环境搭建时,一定要确认工具链的版本与固件中使用的版本一致。版本不匹配可能导致编译出来的程序在目标设备上运行异常。我曾经因为用了新版本的glibc,导致编译出来的gdbserver在旧版固件上段错误。
好了,这一章的内容就到这里。二进制文件格式、解包工具链、CPU架构识别与交叉编译环境搭建,这三块是固件逆向的基石。你想想看,如果连文件格式都搞不清楚,后面的分析根本无从谈起。下一章我们会深入探讨固件中常见的后门模式,以及如何用静态分析技术快速定位可疑代码。
公众号:蓝海资料掘金营,微信deep3321