- 献词
- 致谢
- 前言
- 第一部分 IDA 简介
- 第 1 章 反汇编简介
- 第 2 章 逆向与反汇编工具
- 第 3 章 IDA Pro 背景知识
- 第二部分 IDA 基本用法
- 第 4 章 IDA 入门
- 第 5 章 IDA 数据显示窗口
- 第 6 章 反汇编导航
- 第 7 章 反汇编操作
- 第 8 章 数据类型与数据结构
- 第 9 章 交叉引用与绘图功能
- 第 10 章 IDA 的多种面孔
- 第三部分 IDA 高级应用
- 第 11 章 定制 IDA
- 第 12 章 使用 FLIRT 签名来识别库
- 第 13 章 扩展 IDA 的知识
- 第 14 章 修补二进制文件及其他 IDA 限制
- 第四部分 扩展 IDA 的功能
- 第 15 章 编写 IDA 脚本
- 第 16 章 IDA 软件开发工具包
- 第 17 章 IDA 插件体系结构
- 第 18 章 二进制文件与 IDA 加载器模块
- 第 19 章 IDA 处理器模块
- 第五部分 实际应用
- 第 20 章 编译器变体
- 第 21 章 模糊代码分析
- 第 22 章 漏洞分析
- 第 23 章 实用 IDA 插件
- 第六部分 IDA 调试器
- 第 24 章 IDA 调试器
- 第 25 章 反汇编器/ 调试器集成
- 第 26 章 其他调试功能
- 附录 A 使用 IDA 免费版本 5.0
- 附录 B IDC/SDK 交叉引用
12.2 应用 FLIRT 签名
初次打开一个二进制文件时,IDA 会尝试对这个文件的入口点应用特殊的签名文件,即启动签名。因为由各种编译器生成的入口点代码各不相同,因此,我们可以通过匹配入口点签名来识别用于生成某个特定二进制文件的编译器。
Main 与_start
如前所述,程序的入口点是即将执行的第一条指令的地址。因此,许多熟练的 C 程序员错误地认为这就是
main
函数的地址,但事实并非如此。程序的文件类型,而不是创建程序所使用的语言,决定了向这个程序提交命令行参数的方式。为了使加载器加载命令行参数的方式与程序预期接收参数的方式(例如,通过向main
提交参数)保持一致,程序必须在将控制权转交给main
之前执行一段初始化代码。IDA 将这段初始化代码作为程序的入口点,并将其标记为_start
。这段初始化代码还负责必须在
main
运行之前完成的初始化任务。在 C++ 程序中,这段代码负责确保在执行main
之前调用全局声明对象的构造函数。同样,为了在程序真正终止前调用所有全局对象的析构函数,必须插入在main
之后执行的清理代码(cleanup code)。
如果 IDA 能够识别用于创建某个二进制文件的编译器,那么,它会加载对应编译器库的签名文件,并将其应用于该文件的剩余部分。IDA 自带的签名大多与专用编译器有关,如 Microsoft Visual C++或 Borland Delphi。这是因为,这些编译器自带的二进制库的数量有限,而与开源编译器(如 GNU gcc )关联的库却拥有大量的二进制变体,与这类编译器自带的操作系统一样种类繁多。例如,每个版本的 FreeBSD 都自带有一个特殊的 C 标准库。为了获得最佳模式匹配效果,你需要为每一种不同版本的库生成签名文件。想象一下,收集每一个版本的 Linux 自带的 libc.a1 的每一个变体,其难度会有多大!这简直是一个无法完成的任务。从某种程度上说,这些差异归结为库源代码的变化(因而导致不同的编译代码)。但是,巨大的差异也是因为使用了不同的编译选项,如优化设置,以及在创建库时使用了不同的编译器版本。结果,IDA 自带的开源编译器库签名文件非常少。不过,如下所述,Hex-Rays 提供了各种工具,用于由静态库生成你自己的签名文件。
1. libc.a 是 Unix 系统上的静态链接二进制文件所使用的 C 标准库。
那么,在什么情况下,你需要手动对数据库应用签名呢?有时候,IDA 能够正确识别创建二进制文件所使用的编译器,但它并没有相关编译器库的签名。在这种情况下,你要么完全不用签名,要么需要获得二进制文件所使用的静态库的副本,并生成你自己的签名。其他情况下,IDA 可能根本无法识别一个编译器,因而也无法确定该对数据库应用哪些签名。在分析模糊代码时,你经常会遇到这种情况:为防止编译器识别,混淆代码中的启动例程(startup routine )经过了非常复杂的改编。这时,你必须首先对二进制文件进行大量的“去模糊”处理,然后才有可能匹配库签名。我们将在第 21 章中讨论处理模糊代码的技巧。
无论出于什么原因,如果希望手动对一个数据库应用签名,可以通过 File ▶Load File ▶FLIRT Signature File 打开如图 12-1 所示的选择签名对话框。
图 12-1 选择 FLIRT 签名
File 一栏显示 IDA 的<IDADIR>/sig 目录中每个.sig 文件的名称。需要注意的是,你不能为.sig 文件指定另外的存储位置。如果你生成了自己的签名,它们也需要和其他.sig 文件一起,存储在<IDADIR>/sig 目录中。Library name 一栏显示嵌入到每个文件中的库名称注释。记住,这些注释的描述性完全取决于签名创建者(可能就是你自己)。
选择一个库模块,IDA 将加载对应的.sig 文件中包含的签名,并将其与数据库中的每一个函数进行比较。你一次只能应用一组签名,因此,如果你希望对一个数据库应用几组不同的签名,需要重复上述过程。如果发现一个函数与签名相匹配,IDA 会将这个函数标记为库函数,并根据与其匹配的签名自动对它进行重命名。
警告 只有使用 IDA 哑名的函数才能被自动重命名。换言之,如果你已经对一个函数进行了重命名,而后这个函数与一个签名相匹配,那么,这时 IDA 不会再对这个函数进行重命名。因此,在分析过程中,你应该尽可能早地应用签名。
如前所述,静态链接二进制文件模糊了应用程序代码与库代码之间的区别。如果你足够幸运,拥有一个没有去除符号的静态链接二进制文件,那么,你至少拥有有用的函数名(和可信赖的程序员创建的函数名一样有用)来帮助你对代码进行分类。但是,如果二进制文件已经被去除符号,那么,你拥有的仅仅是数百个使用 IDA 生成的名称的函数,至于这些函数的用途,它们的名称无法提供任何信息。在这两种情况下,只要拥有相关签名,IDA 仍然能够识别出库函数(去除符号的二进制文件中的函数名称并不能为 IDA 提供足够的信息,使它准确判定一个函数是否为库函数)。图 12-2 是一个静态链接二进制文件的“概况导航栏”。
图 12-2 应用签名之前的静态链接二进制文件
在这个窗口中,没有函数被确定为库函数,因此,你需要深入分析代码。在应用一组适当的签名后,“概况导航栏”如图 12-3 所示。
图 12-3 应用签名之后的静态链接二进制文件
如你所见,“概况导航栏”证实了应用一组特殊签名所产生的效果。由于多数函数都找到与之匹配的签名,IDA 将大部分的代码标记为库代码,并对它们进行了相应的重命名。在图 12-3 所示的例子中,特定于应用程序的代码很可能集中在导航栏窗口的最左边。
在应用签名时,有两个值得注意的地方。第一,即使是对一个尚未去除符号的二进制文件,签名仍然有用。这时,你更多的是使用签名帮助 IDA 识别库函数,而不是对它们进行重命名。其次,静态链接二进制文件可能由几个单独的库组成,你需要对其应用几组签名才能完全识别所有的库函数。每应用一个签名,“概况导航栏”都会发生变化,以反映你发现的库代码。图 12-4 显示的就是这样一个例子。在这个图中,你看到的是一个使用 C 标准库和 OpenSSL2 加密库静态链接的二进制文件。
2. 参见 http://www.openssl.org/ 。
图 12-4 应用前几个签名后的静态二进制文件
具体来说,我们看到,在对二进制文件应用适当的 OpenSSL 签名后,IDA 将一个窄小的代码带(地址范围左侧的浅色代码带)标记为了库代码。通常,要创建静态链接二进制文件,首先应插入应用程序代码,其次附加所需的库,最后得到可执行文件。根据图 12-4 ,我们可以得出结论:OpenSSL 库的右侧很可能是其他的库代码,而应用程序代码则位于 OpenSSL 库左侧的一条非常窄小的代码带中。如果我们继续对图 12-4 中的二进制文件应用签名,最终的“概况导航栏”将如图 12-5 所示。
图 12-5 应用几组签名后的静态二进制文件
在这个例子中,我们应用了 libc 、libcrypto 、libkrb5 、libresolv 及其他库的签名。有时候,我们根据二进制文件中的字符串来选择签名。其他情况下,我们选择与二进制文件中已经确定的其他库关系密切的签名。最终,导航窗口会在导航带的中间显示一个深色的代码带,在导航带的最左边缘显示一个更小的深色代码带。要确定二进制文件中剩下的非库代码的性质,你需要进行更加深入的分析。在这个例子中,我们知道,中间的深色代码带是一种尚未识别的库,而左侧的深色代码带则为应用程序代码。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论