4、固件架构识别:CPU架构(ARM、MIPS、x86)

拿到一个陌生的固件,第一件事是什么?

别急着解包,也别急着跑模拟器。我个人的习惯是——先搞清楚它跑在什么CPU上。这就像你拿到一把钥匙,得先知道它开哪扇门。ARM、MIPS、x86,这三兄弟长得不一样,指令集不同,漏洞利用的方式也天差地别。

4.1 为什么架构识别这么重要?

说白了,你逆向分析时看到的汇编代码,完全取决于CPU架构。ARM用LDR/STR,MIPS用LW/SW,x86用MOV。如果你连目标架构都搞错了,那后面的分析就是缘木求鱼。

我在一次项目中就吃过这个亏。当时拿到一个路由器固件,想当然以为是ARM,结果用IDA反编译出来一堆看不懂的指令。折腾了半天才发现,这货居然是MIPS的。嗯,从那以后,我每次都会先确认架构,再动手。

核心要点:架构识别是固件分析的基石。选错工具链,后面的工作全是白费。

4.2 使用 file 命令快速识别

Linux下的file命令,是我最常用的第一道防线。它不看文件后缀,而是读文件头部的魔数(Magic Number),直接告诉你这是什么类型的文件。

用法很简单:

file firmware.bin

输出结果大概长这样:

firmware.bin: u-boot legacy uImage, Linux, ARM (ARM926EJ-S), OS Kernel Image (gzip)

看到没?ARM (ARM926EJ-S),架构信息直接写在脸上。如果是MIPS,它会显示MIPS (MIPS32)或者MIPS (MIPS64)。x86的话,通常是Intel 80386或者x86-64

但这里有个坑——file命令只识别文件头。如果固件是加密的或者被混淆了,它可能什么都认不出来。我曾经遇到一个固件,file命令只返回了data,后来才发现是厂商自定义的头部格式。

小技巧:如果file命令没识别出来,可以试试binwalk -M,它能扫描固件中的已知签名,有时候能发现隐藏的架构信息。

4.3 使用 binutils 工具链深入分析

file命令只能看个大概,真要确认架构,还得靠binutils工具链。这套工具是GNU项目的一部分,专门用来处理二进制文件。我常用的几个命令:

  • readelf:读取ELF文件头,信息最全
  • objdump:反汇编,看指令集
  • strings:提取可打印字符串,辅助判断

4.3.1 readelf:ELF文件的身份证

如果固件解包后得到的是ELF文件(比如Linux内核或应用程序),readelf就是最好的选择。

readelf -h vmlinux

输出示例:

ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF32
  Data:                              2's complement, little endian
  Machine:                           ARM

关键字段就是Machine。ARM、MIPS、x86都会直接显示。注意看Data字段,它告诉你字节序——ARM和x86通常是小端(little endian),MIPS则大小端都有。

注意:MIPS的字节序是个坑。我见过不少MIPS固件是大端的,如果你用小端的工具链去反汇编,出来的代码全是错的。一定要先确认字节序。

4.3.2 objdump:直接看指令

有时候文件头被破坏了,readelf读不出来。这时候我会用objdump直接反汇编一段代码,看指令特征。

objdump -d --start-address=0x1000 --stop-address=0x1100 firmware.bin

ARM的指令特征:

  • 指令长度固定4字节(ARM模式)或2字节(Thumb模式)
  • 常见指令:LDR、STR、MOV、ADD、SUB
  • 条件执行:EQ、NE、GT等后缀

MIPS的指令特征:

  • 指令长度固定4字节
  • 常见指令:LW、SW、ADDU、SUBU、BEQ、BNE
  • 延迟槽:分支指令后面紧跟的一条指令也会执行

x86的指令特征:

  • 指令长度可变(1-15字节)
  • 常见指令:MOV、PUSH、POP、CALL、RET
  • 寄存器命名:EAX、EBX、ECX、EDX

我个人习惯是,先看几条指令,心里就有数了。ARM的指令看起来规整,MIPS的延迟槽很特别,x86的变长指令一眼就能认出来。

4.3.3 strings:辅助判断

strings命令提取固件中的可打印字符串,有时候也能提供线索。

strings firmware.bin | grep -i "arm\|mips\|x86\|intel"

如果固件里有类似/lib/ld-linux.so.3这样的路径,那基本就是ARM了。MIPS的链接器路径通常是/lib/ld.so.1。x86的话,/lib/ld-linux.so.2是32位,/lib64/ld-linux-x86-64.so.2是64位。

经验之谈:我曾经用strings在一个固件里找到了mips-linux-gcc的编译路径,直接确认了架构。有时候厂商的编译环境会留下痕迹,多留个心眼。

4.4 架构识别流程图

下面这张图是我自己总结的架构识别流程,按这个顺序走,基本不会出错:

固件架构识别流程图 获取固件文件 步骤1:使用 file 命令快速识别 file firmware.bin 识别成功? 确认架构信息 步骤2:使用 readelf readelf -h 文件 识别成功? 确认架构信息 步骤3:使用 objdump objdump -d 文件 分析指令特征确认

4.5 实战案例:一个真实固件的架构识别

我记得有一次分析某品牌的路由器固件。拿到手是个.bin文件,大小16MB。先用file命令:

$ file firmware.bin
firmware.bin: data

嗯,没识别出来。厂商可能加了自定义头。接着用binwalk扫描:

$ binwalk -M firmware.bin

输出显示在偏移0x10000处有一个LZMA压缩的Linux内核。解包后得到vmlinux文件。再用readelf

$ readelf -h vmlinux
Machine: MIPS R3000

确认了,是MIPS架构。但字节序呢?看Data字段:

Data: 2's complement, big endian

大端MIPS。好,这下工具链选对了。用objdump反汇编验证一下:

$ mips-linux-gnu-objdump -d vmlinux | head -20

看到luiaddiusw这些指令,心里踏实了。就是MIPS大端,没跑。

避坑指南:我曾经遇到一个固件,readelf显示Machine是ARM,但反汇编出来的指令全是乱的。后来发现是Thumb模式,需要用-M force-thumb参数。ARM的Thumb和ARM模式指令长度不同,这个坑我踩过,你们别踩。

4.6 架构识别速查表

架构 指令长度 常见指令 字节序 file命令输出特征
ARM 4字节(ARM)/ 2字节(Thumb) LDR, STR, MOV, ADD 小端为主 ARM, ARM926EJ-S, Cortex-A
MIPS 4字节 LW, SW, ADDU, BEQ 大小端都有 MIPS, MIPS32, MIPS64
x86 1-15字节(可变) MOV, PUSH, POP, CALL 小端 Intel 80386, x86-64

这张表我建议你保存下来。刚开始做固件分析时,我经常对着它对照。用多了就熟了,现在看一眼指令就能猜个八九不离十。

最后提醒一句:架构识别不是一锤子买卖。有时候固件里混着多个架构的代码(比如bootloader是ARM,内核是MIPS),或者有Thumb/ARM模式切换。多验证几次,别偷懒。


公众号:蓝海资料掘金营,微信deep3321