逆向工程/文件格式
本节将讨论逆向工程专有文件格式。许多软件开发人员需要逆向工程专有文件格式,特别是为了实现互操作性。例如,Open Office 项目每年都需要逆向工程 Microsoft Office 文件格式。此外,逆向工程也用于取证目的。本节中的章节将讨论如何理解专有文件格式。
大多数文件格式以“头”开头,一些字节描述了文件类型和版本。由于有几种具有相同扩展名的不兼容文件格式(例如“.doc”和“.cod”),头文件提供了足够的信息供程序查看该文件是否为该程序可以处理的格式之一。
许多程序员在将数据写入磁盘之前将数据打包在某种“容器格式”中。如果他们使用标准 zlib 将数据压缩存储,则文件将以 2 个字节 0x1f 0x8b(以十进制表示,31 139)开头。
有些文件主要由空白空间组成,例如,由 OS X 生成的 .ds_store 文件。在十六进制编辑器中,空白空间将显示为一系列 0。文件格式的创建者可能出于多种原因添加空白空间,例如,此研究的作者关于 .ds_store 文件推测它们的存在是为了加快写入数据速度,因为其他数据不需要被移动来腾出空间。它们还可以防止碎片化。
对于大多数目的,空白空间可以忽略。
文件格式逆向工程是十六进制编辑器的领域。通常它们更多地用于显示文件内容而不是编辑它们。十六进制编辑器允许您将数据结构叠加在数据之上(有时称为自定义视图或类似的),这非常有用。一旦在一个文件中发现了特定的结构,就可以使用这些机制来记录结构,以及提供比十六进制代码更具意义的信息显示。
Unix/Linux 工具也很有用,例如 strings(1) 和 file(1)。
- strings
- 查找并在文件中打印可打印字符序列。这可以提示文件中嵌入的数据。
- file
- 尝试确定文件类型。有时文件格式设计人员会重复使用已经知名的文件格式或文件压缩算法。 file(1) 可能会揭示这一点,虽然可能性不大但并非没有。
这些工具的 Windows 移植版本也可用。例如,作为Cygwin环境的一部分(strings是binutils的一部分,不要问...)。
与十六进制编辑器同样重要的是大脑。文件格式逆向工程意味着要对十六进制编辑器和其他工具显示的内容进行推理。猜测结构、关系和数据的含义,发展理论,然后验证它们。很少有工具可以帮助你做到这一点。
在少数情况下,额外的工具很有帮助。例如,用于暴力检查文件特定部分是否包含一些嵌入的压缩数据。通常这些工具是作为自定义工具即时编写或脚本化的。另一组典型的自定义工具是用来将文件拆分成单独的组件的工具 - 一旦发现特定文件实际上包含单独的部件,以及它们如何在文件中分隔。C/C++、Java,以及Perl 等脚本语言通常在这里使用(Perl 因为它可以处理二进制数据,而传统的 Unix 脚本工具通常只限于文本数据)。
在某些情况下,专有文件格式可能包含可执行代码。例如,某些嵌入式设备的固件更新文件很可能包含可执行代码。通常,这些代码被包装在一个结构中,例如一个文件系统,被压缩,并带有引导/闪存代码等。在这种情况下,针对特定可执行格式的反汇编器/反编译器可能会有所帮助。
此外,对校验和算法、压缩算法、编码技术以及编程语言的文档和熟悉程度非常有帮助。
同样非常有帮助的是能够使用生成和读取专有文件格式的应用程序。该应用程序可以用来创建测试文件,还可以验证自己生成的测试文件是否正确。
首先寻找明显的东西。例如,魔数、块结构、文件中的 ASCII 文本。任何可以或多或少清楚地识别的东西都可以是通往更多内容的入场券。一旦识别出一个特定的结构,就寻找指向该数据的文件内指针。例如,如果数据由文件中的其他部分通过绝对地址或相对地址引用。确定字节序(小端序还是大端序)也非常重要。
如果您能够访问创建文件的软件,则始终可以创建包含您选择的內容的文件。这使得逆向工程变得容易得多。在密码学术语中,您正在进行已知明文攻击。
一旦你形成了关于文件中的某些数据可能代表什么的理论,你就可以通过创建一个修改后的文件来验证该理论。使用十六进制编辑器或自定义工具将它替换为其他数据。然后将修改后的文件加载到原始应用程序中。如果应用程序加载文件并显示预期的更改,那么该理论可能就是正确的。有时由于可能存在的防御机制,更改应用程序并重新加载它并不容易。有些应用程序在运行之前会检查代码的哈希值和签名。
部分或完全压缩、加密或混淆的文件格式是最难破解的。当然,压缩不同于加密,通常出于不同的目的进行。然而,产生的文件格式通常看起来很相似:一堆乱码。当文件格式设计人员进行加密时,这是预期的结果,但当应用压缩时,这也是一个经常想要的副作用。
请注意:维基教科书不提供法律意见 |
如果使用十六进制编辑器或类似工具检查文件显示它只是包含乱码,例如没有易于识别的文本字符串、模式或类似内容,这可能表明特定文件被压缩、加密或混淆。逆向工程这些文件的方法是相似的。然而,从法律角度来看,可能会有很大差异。许多国家有关于绕过复制保护的法律,而加密可以被视为一种复制保护。有关更多提示,请参阅逆向工程/法律方面,并在尝试逆向工程加密或其他受保护的文件格式之前咨询合格的法律顾问。类似的问题也可能出现在文件格式仅使用混淆的情况下。格式“所有者”可能会争辩说混淆被用作某种复制保护、加密或其他东西,绕过它可能会违反某些法律。再次,请咨询合格的法律顾问。
本节的剩余部分只讨论文件压缩的反向工程。这通常只是完整反向工程过程的初始步骤。一旦成功解压缩,就需要应用其他反向工程方法来识别文件内容和结构。
文件格式设计者通常会使用众所周知的压缩算法。无论是使用特定算法的特定且众所周知的实现(众所周知的工具),还是重新实现一个众所周知的算法而无需修改。在最简单的情况下,这已经被记录在案。例如,众所周知 OpenOffice 文件格式使用 ZIP 存档,因此没有必要对该格式进行反向工程。
不幸的是,对于许多格式,我们没有这样的文档。如果使用了特定算法的众所周知的实现,反向工程通常相对容易。这种压缩文件格式往往以格式标识符(*魔数*)开头,清楚地识别特定的压缩格式。压缩工具已将其“指纹”保留在原位。
例子:
以下是特定 SOHO 路由器的虚拟固件更新文件的头几个字节的十六进制转储
00000000 60 ea 27 00 1e 06 01 00 10 00 02 84 84 86 dc 34 |`.'............4| 00000010 84 86 dc 34 00 00 00 00 00 00 00 00 00 00 00 00 |...4............| 00000020 00 00 44 54 41 2e 41 52 4a 00 00 b1 18 78 a6 00 |..DTA.ARJ....x..| 00000030 00 60 ea 27 00 1e 06 01 00 10 01 00 84 4c 86 dc |.`.'.........L..| 00000040 34 9b 17 0c 00 e8 a4 25 00 25 10 0d 10 00 00 20 |4......%.%..... | 00000050 00 00 00 44 54 41 2e 4d 45 4d 00 00 50 98 0b 8f |...DTA.MEM..P...| 00000060 00 00 1f 30 84 dd 7b db 48 da 6f fd ee fd bb da |...0..{.H.o.....|
该文件使用以下方式压缩ARJ. 不仅字符串 *DTA.ARJ* 对人眼来说很明显,而且前两个字节60 ea, 已知用于识别 ARJ 压缩文件。
Unix/linux 工具 **file**(1) 对许多标准压缩文件格式了如指掌。
例子:
**file** 为上述固件文件返回以下内容
firmware.bin: ARJ archive data, v6, slash-switched, original name: DTA.ARJ, os: MS-DOS
在发现压缩格式之后,下一步很明显:获取使用压缩工具的版本并使用它来解压缩数据。然而,结果通常需要更多的反向工程。例如,上面提到的路由器固件可能包含针对路由器闪存的不同区域的单独部分,每个部分都由一个自己的校验和保护。
使用众所周知的压缩算法和工具的变体有时也可以找到,这更难进行反向工程。在这种情况下,文件以一些附加数据为前缀,并且无法仅通过检查文件格式来识别实际压缩格式。例如,假设另一个虚拟 SOHO 路由器的固件更新文件,其构建方式如下
例子:
另一个 SOHO 路由器固件更新文件的虚拟结构
+--------------------------+ | Boot loader | +--------------------------+ | Decompression algorithm | +--------------------------+ | Compressed data | +--------------------------+
当然,只有在反向工程文件格式后才能知道格式。那么该怎么做呢?好吧,在虚拟情况下,我们假设使用 Unix/Linux 工具 **strings**(1) 检查文件会显示文件中以下有趣的字符串
例子:
**strings** 的简化输出
: : unknown compression method invalid window size incorrect header check need dictionary incorrect data check invalid block type invalid stored block lengths too many length or distance symbols invalid bit length repeat inflate 1.1.3 Copyright 1995-1998 Mark Adler oversubscribed dynamic bit lengths tree incomplete dynamic bit lengths tree oversubscribed literal/length tree incomplete literal/length tree oversubscribed distance tree incomplete distance tree empty distance tree with lengths invalid literal/length code invalid distance code invalid distance code invalid literal/length code incompatible version buffer error insufficient memory data error stream error file error stream end need dictionary 1.1.3 application.bin : :
这些字符串非常有启发性,那些有知识的人会认出 *Mark Adler* 的名字是以下作者之一zlib zlib, 这是以下的基础info-zip以及 GNU 的 gzip. 那些不太了解的人至少可能想到搜索名称和关键字压缩。
可以肯定地假设至少部分文件是 ZIP 压缩的。进一步探查可能会发现该文件不包含完整的 ZIP 存档,而只是一个使用 ZIPdeflate算法压缩的节,并应该使用 ZIPinflate算法解压缩(可能是 1.1.3 版,因为 **strings** 的输出显示)。因此,可以利用自定义工具进一步将虚拟文件分离为其组件,该工具迭代地将inflate算法应用于文件,直到生成的結果有意义(例如,直到结果包含一些可识别的纯文本字符串)。
如果创建或读取文件的软件可用,则很有可能反向工程文件格式。您可以在读取/写入文件时对正在运行的应用程序进行实时分析。这样做可能是确定文件数据结构的最简单方法。
如果软件不可用,如果存在未知或自制/临时压缩算法,或者已知算法的非标准实现,那么一切皆有可能。要弄清楚应用算法的细节,以便可以构建相应的解压缩算法,就必须极其幸运,尽管密码学家强烈反对使用临时加密方案,因为这些方案通常无法经受住严格的密码分析。
有时可以找到额外的信息。例如,如果供应商已为特定算法提交了专利申请,或者已知在其他产品(例如通信协议)中爱上了特定压缩技术。有时可能会发现文件格式实际上属于某个 OEM 或第三方产品,并且有关该产品的的信息是可用的。
否则,试错方法可能会揭示有关文件的一些信息。例如,运行长度编码是一种流行的、简单的且易于实现的压缩算法,因此它们有时可以在自制实现中找到。值得尝试调查一下文件是否可能以这种方式压缩。调查其他一些众所周知的压缩技术也值得一试。
最后但并非最不重要的一点是,密码分析技术可能会揭示有关压缩的一些有趣信息。例如,重复出现的块信息可能指向特定的压缩算法。但是,这需要付出很多努力、时间和技能。