我已经从 PPC 中提取了固件二进制文件并将其加载到 IDA 中。现在我试图确定它包含什么操作系统,但不知道从哪里开始。
确定固件二进制文件中包含的操作系统的最简单方法是什么?
注意:问题在于,这样的努力非常特定于人们正在查看的文件。因此,在必要时偏离此处概述的路径需要很多常识和可能的经验。
好吧,你有很多选择,我们所有人可能都有这样的秘诀。
- 我首先会从
file
(此处为Windows 版本)开始。 - 假设失败,我会在网上搜索前 8 到 16 个字节的十六进制值,看看会出现什么。此外,我会输入适合我的场景的搜索词。
- 这没有产生任何结果,我将继续看看我是否可以说服该工具
strings
向我展示任何有用的东西(Sysinternals 上有一个Windows 版本的字符串)。这可能已经足够了。 - 但是,如果文件包含压缩或加密的 blob,我现在会引入firmware-mod-kit。它将直接从 SVN 构建(仅在几天前测试过),随附
binwalk
并允许解压一些initrd
无法被 vanilla 解压的 sbinwalk
。或者,您可以尝试find-firmware.pl
(或一般来说,它所属的脚本集) - 如果这一切都失败了,我会在支持某种二进制模板(例如010 Editor或免费软件 Neo)的十六进制编辑器中打开该文件。
...从那里我会寻找模式。这就是很难描述我如何打勾的地方,这是一种视觉上的东西。基本上我会寻找偏移量,但模式00
和非00
在某些情况下也会给我提示。此外,如果十六进制编辑器具有直方图功能,我可能会使用直方图功能来查看特定的数据 blob 是否可能被加密或压缩(这两者都会使字节值的分布相对均匀)。知道平台 (PPC) 是 Big Endian,我会假设偏移量也将是 Big Endian。所以这个设置会被打开,然后我会看看我是否可以解码文件头之类的东西的含义。如果可以,那么我将检查我找到的所有 blob,看看我是否可以从它们中辨别出某种信息,例如压缩算法。例如gzip
具有特定的前导字节,而原始deflate
除非程序员选择使用它们。如果所有这些都失败了,我将不得不暂时假设这是其他一些压缩格式,并且会使用LZMA SDK作为参考来提出一个工作理论(通常是binwalk
之前的步骤负责做到这一点) )。寻找这些指纹的过程中,我希望能够提取属于自己的文件并且可以由外部工具处理的 blob。
如果之前的所有方法都失败了,我会着手寻找一个应该在某种程度上知道文件格式的软件,例如将固件文件作为输入的固件闪存程序。
注意:如果您的固件 blob 来自驱动程序,例如在 Linux 中,您当然会首先查看该驱动程序以寻找线索。
原始答案(在很明显这是固件二进制文件之前)
我将从file
命令开始(此处为Windows 版本,感谢 amccormack - 请参阅他的评论)。它通常会告诉你它是为哪个操作系统(和内核等)构建的众所周知的可执行格式。
例子:
# On an AIX 5.3 machine
$ file python
python: executable (RISC System/6000) or object module not stripped
# Debian 6 on PPC64
$ file python
python: ELF 32-bit MSB executable, PowerPC or cisco 4500, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, with unknown capability 0x41000000 = 0x13676e75, with unknown capability 0x10000 = 0xb0401, stripped
# Ubuntu 10.04, x64
$ file python
python: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.15, not stripped
# Windows
>file C:\Python27\python.exe
C:\Python27\python.exe; PE32 executable for MS Windows (console) Intel 80386 32-bit
正如 0xC0000022L 已经说过的那样,字符串确实是第一步。由于这是您直接从闪存芯片获得的转储,因此除非已加密,否则应该有一些可读字符串(例如来自引导加载程序的字符串)。
你提到它是一个基于 PPC 的设备,所以你也可以搜索常见的 PPC 操作码(binwalk 可以做到这一点,指定 -A 选项);你应该至少看到一些操作码弹出。
要检查的另一件事是二进制数据的熵(binwalk 的 -E 选项可以执行此操作,许多其他工具也可以执行此操作)。固件通常会压缩某些部分(例如内核和文件系统),这将在固件映像中显示为单独的、熵非常高的部分。字符串和代码具有中等熵。加密数据具有非常高的熵。
设备的硬件也可以为您提供一些线索。闪存容量很小(比如 2MB 或更少)的设备不太可能运行大型操作系统,例如 Linux;他们可能正在运行某种较小的 RTOS。
另一件要考虑的事情是您如何转储闪存、它是什么类型的闪存芯片以及闪存芯片的数据总线。例如,在小端系统上以 16 位模式运行的并行闪存芯片每两个字节翻转一次。因此,如果您在 16 位模式下对芯片进行原始读取,则字符串“ABCD”将被读出为“BADC”。这显然会搞乱任何基于签名的分析,并导致字符串看起来异常熟悉但同时不可读。
这不是火箭科学。IDA 不是用来做出这个简单决定的工具。检查固件映像中找到的字符串。如果您找不到任何内容,则它可能已全部加密和/或压缩。在任一情况下,请尝试Binwalk以获得额外帮助。固件中使用的文件系统类型也可以作为线索。
此外,只有这么多可能的选择。一些是(最常见的粗体):
- Linux(各种分支,uLinux,OpenWrt,...)
- VxWorks
- BSD(各种分支,iOS 是其中之一)
- 山猫操作系统
- 昆士兰
- QNX
- 视窗CE
- 嵌入式 Windows NT
发布者,既然你说它可能是 VxWorks,这里有一种可能的头数据结构,如 WR54G(S)v5 中所使用,如此处所述。
////////////////////////////////////////////////////////////////
// Linksys VxWorks based firmware image format
#pragma pack(1)
typedef struct _VxFileDescriptor
{
DWORD nFileId_BigEnd; // file type (see below)
DWORD nFileSize_BigEnd;
} VxFileDescriptor, *pVxFileDescriptor;
typedef struct _VxLinksysHeader
{
DWORD nCodePattern;
BYTE cUnknown_4[4];
BYTE cYear;
BYTE cMonth;
BYTE cDay;
BYTE nProductVersion_0;
BYTE nMinorVersion_0;
BYTE cZUnknown_0D;
BYTE cImageFormatVersion[4];
BYTE cZUnknown_12[238];
//
// offset 0x100 -- begining of an secondary header?
//
// After this point, all integers are stored big endian
// and should be read by endian neutral code
// (that is, read as big endian).
//
BYTE nProductVersion_1;
BYTE nMinorVersion_1;
WORD nMajorVersion_1;
BYTE cZUnknown_104[2];
WORD nHeaderSizeBigEnd;
DWORD nChecksumBigEnd;
BYTE cZUnknown_10B[2];
WORD nUnknown_10D;
BYTE cZUnknown_110[0x30];
BYTE cModelName[0x20];
BYTE cVendorName[0x20];
VxFileDescriptor TrailingFiles[8];
// parts of file that follow primary file descriptors
VxFileDescriptor FileDescriptors[8];
// primary file descriptors, immediately follow header
} VxLinksysHeader, *pVxLinksysHeader;
大多数操作系统都有某种字符串来标识操作系统和版本。因此,通过使用十六进制编辑器查看文件,您通常可以识别 os. 否则,您将需要搜索已知的签名/足迹。