- 献词
- 致谢
- 前言
- 第一部分 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 交叉引用
13.1 扩充函数信息
IDA 通过两种途径获得与函数有关的信息:类型库(.til)文件和 IDS 实用工具(.ids )文件。在初始分析阶段,IDA 使用存储在这些文件中的信息来提高反汇编过程的准确性及反汇编代码清单的可读性。它通过合并函数参数名称和类型,以及与各种库函数有关的注释来完成这个任务。
第 8 章曾提到过,类型库文件是 IDA 用于存储复杂数据结构布局的机制。同时,IDA 还使用类型库文件记录与函数的调用约定和参数序列有关的信息。IDA 以各种方式使用函数签名信息。首先,当一个二进制文件使用共享库时,IDA 无法知道这些库中的函数使用的是什么调用约定。这时,IDA 会尝试根据一个类型库文件中的相关签名来匹配库函数。如果它发现一个匹配的签名,IDA 就可以知道这个函数使用的调用约定,并对栈指针进行必要的调整(如前所述, stdcall
函数自己对栈进行清理)。使用函数签名的第二种方式是为传递给函数的参数提供注释。这些注释说明在调用函数之前,到底是哪一个参数被压入到栈上。注释提供的信息量取决于 IDA 能够解析的函数签名所包含的信息量。下面的两个签名都是有效的 C 声明,第二个签名提供了更多有关函数的信息,除数据类型以外,它还提供了形式参数名称。
LSTATUS _stdcall RegOpenKey(HKEY, LPCTSTR, PHKEY); LSTATUS _stdcall RegOpenKey(HKEY hKey, LPCTSTR lpSubKey, PHKEY phkResult);
IDA 的类型库中保存着大量常用 API 函数(包括许多 Windows API)的签名信息。调用 RegOpenKey
函数的默认反汇编代码清单如下所示:
.text:00401006 00C lea eax, [ebp+➋hKey] .text:00401009 00C push eax ➊; phkResult .text:0040100A 010 push offset ➋SubKey ; "Software\\Hex-Rays\\IDA" .text:0040100F 014 push 80000001h ➊; hKey .text:00401014 018 call ds:RegOpenKeyA .text:0040101A ➌00C mov [ebp+var_8], eax
值得注意的是,IDA 已经在右边缘(➊)添加了注释,指出在调用 RegOpenKey
之前,每个指令压入了什么参数。如果函数签名提供形式参数名称,那么 IDA 会更进一步,自动为与特定的参数对应的变量命名。在前面例子中的➋处,我们看到 IDA 已经根据 RegOpenKey
原型中对应的形式参数名称,对一个局部变量( hKey
)和一个全局变量( SubKey
)进行了命名。如果解析后的函数原型中仅包含类型信息,而没有形式参数名称,那么,前面例子中的注释将指出对应参数的数据类型,而非参数名称。至于 IpSubKey
参数,这个参数名称并不作为注释显示,因为这个参数碰巧指向一个全局字符串变量,而该字符串的内容则通过 IDA 的重复注释机制显示。最后,需要注意的是,IDA 已经将 RegOpenKey
识别为一个 stdcall
函数,并自动调整了栈指针(➌), RegOpenKey
在返回时也会这样做。所有这些信息均源自该函数的签名,同时,IDA 将在反汇编代码清单中适当的导入表位置以注释的形式显示这些信息,如下面的代码段所示:
.idata:0040A000 ; LSTATUS __stdcall RegOpenKeyA(HKEY hKey, LPCSTR lpSubKey, PHKEY phkResult) .idata:0040A000 extrn RegOpenKeyA:dword ; CODE XREF: _main+14p .idata:0040A000 ; DATA XREF: _main+14r
显示函数原型的注释来自 IDA 的一个.til 文件,该文件提供与 Windows API 函数有关的信息。
那么,在什么情况下,你希望生成自己的函数类型签名1 呢?如果你遇到一个链接到(无论是动态还是静态)IDA 并不包含其函数原型的库的二进制文件,你可能希望为这个库中的所有函数生成类型签名信息,以便 IDA 能够为自动为你的反汇编代码清单生成注释。这类库包括常用的图形或加密库,虽然它们不属于标准 Windows 库,但却使用广泛。OpenSSL 加密库就是这样一个库。
1. 这里,我们使用术语“签名”表示一个函数的参数类型、数量和顺序,而不是匹配已编译函数的代码模式。
第 8 章提到过,我们可以在一个数据库的本地.til 文件中添加复杂的数据类型信息。同样,我们可以通过 File▶Load File ▶Parse Header File 命令让 IDA 解析一个或几个函数原型,在同一个.til 文件中添加函数原型信息。类似地,你可以使用 tilib.exe(参见第 8 章)解析头文件和创建独立的.til 文件,通过将这些.til 文件复制到<IDADIR>/til 中,这些文件就可以全局使用。
如果你可以访问源代码,然后允许 IDA (或 tilibexe )为你解析源代码,那当然很好。但多数情况下,你都无法访问相关源代码,并且你仍然希望获得高质量的反汇编代码清单。那么,在没有源代码可供参考的情况下,你如何为 IDA 提供信息呢?这正是 IDA 实用工具或 idsutils
的作用所在。这些 IDA 实用工具包括 3 个用于创建.ids 文件的实用程序。我们首先介绍.ids 文件的定义,然后说明如何创建我们自己的.ids 文件。
手动重写被删除字节
利用
stdcall
调用约定的库函数可能会给 IDA 的栈指针分析造成不良影响。缺乏任何类型库或.ids 文件信息,IDA 都无法知道导入函数是否使用stdcall
约定。了解这一点非常重要,因为 IDA 可能无法在函数(IDA 并不了解它们的调用约定信息)调用中正确跟踪栈指针的行为。除了需要知道函数使用了stdcall
外,IDA 还必须了解在完成操作时,这个函数到底从栈上删除了多少个字节。缺乏调用约定信息,IDA 将尝试使用一种叫做“单纯方法”(simplex method)2 的算术分析技巧自动确定一个函数是否使用 stdcall。第二种技巧需要 IDA 用户手动干预。图 13-1 是一个专门用于编辑导入函数的函数编辑对话模式。2. Ilfak 在他的一篇博客文章中介绍了在 IDA 5.1 版中引入的“单纯方法”,地址为 http://hexblog.com/2006/06 。
图 13-1 编辑导入函数
导航到函数的导入表条目并选择编辑这个函数(Edit ▶Functions ▶Edit Function ,或 ALT+P ),即可打开该对话框。需要注意的是,这个特殊的对话框提供的功能有限(相对于图 7-7 所示的“编辑函数”对话框)。因为这是一个导入函数条目,IDA 无法访问该函数的已编译主体,因而也无法获得与该函数的栈帧结构有关的信息,以及它是否使用
stdcall
约定的直接证据。由于缺乏这些信息,IDA 只得将 Purged bytes 输入框设置为1,表示它不知道该函数在返回时是否从栈中清除了字节。在这种情况下,要重写 IDA ,需要输入已删除字节的正确数量,这样,在相关函数被调用时,IDA 会将获得的信息合并到它的栈指针分析中。如果 IDA 了解该函数的行为,Purged bytes 输入框可能已经填有数据(如图 13-1 所示)。注意,使用“单纯方法”分析时,这个输入框绝不可能会填入数据。
13.1.1 IDS 文件
IDA 使用.ids 文件扩展它在库函数方面的知识。.ids 文件通过列举共享库中包含的每一个导出函数,来描述这个库的内容。与函数有关的详细信息包括函数名称、它的相关序号3 ,还包括该函数是否使用 stdcall
(如果使用 stdcall
,包括返回时该函数从栈上删除了多少字节的代码),另外也包括在反汇编代码清单中引用该函数时显示的可选注释。.ids 文件实际上是压缩后的.idt 文件,后者包含每个库函数的文本说明。
3. 序号是与每个导出函数有关的整数索引。使用序号可通过整数查询表迅速定位一个函数。若通过将函数名称与字符串进行比较来定位函数,则很缓慢。
初次在数据库中加载一个可执行文件时,IDA 将确定该文件所依赖的共享库。IDA 会在<IDADIR>/ids 目录中搜索与每一个共享库对应的.ids 文件,以获得有关该可执行文件可能引用的任何库函数的说明。需要记住的是,.ids 文件中不一定包含函数签名信息。因此,IDA 可能无法仅仅根据.ids 文件中的信息提供函数参数分析。但是,如果.ids 文件能够正确指出函数所使用的调用约定,以及函数从栈中清除的字节数量,IDA 就能够进行准确的栈指针调整。如果一个 DLL 导出改编名称,IDA 就能够根据这个改编名称推断出一个函数的参数签名,在加载.ids 文件后,我们就可以利用这些信息。我们将在 13.1.2 节介绍.idt 文件的语法。在这方面,.til 文件包含与反汇编函数调用有关的更多有用信息,不过,要想生成.til 文件,你需要使用源代码。
13.1.2 创建 IDS 文件
IDA 的 idsutils
实用工具用于创建.ids 文件。这些实用工具包括两个库解析器:从 Windows DLL 中提取信息的 dll2idt 和从 ar 库中提取信息的 ar2idt.exe 。无论使用哪一个解析器,其输出都是一个.idt 文本文件,它每行显示一个导出函数,并将导出函数的序号与函数名称对应起来。.idt 文件的语法非常简单,idsutils 自带的 readme.txt 文件介绍了这种语法。.idt 文件中的绝大多数行用于根据以下方案描述导出函数。
导出项以正数开头,这个数是导出函数的序号。
序号后是一个空格,后面接
Name=
函数形式的Name
指令,例如,Name=RegOpenKeyA
。如果使用零这个特殊的序号,则Name
指令用于指定当前的.idt 文件所描述的库名称,如下所示:
0 Name=advapi32.dll
- 一个可选的
Pascal
指令可用于说明一个函数是否使用了stdcall
调用约定,并指出该函数在返回时从栈中删除了多少个字节的数据。例如:
483 Name=RegOpenKeyA Pascal=12
- 可以在导出项后附加一个可选的
Comment
指令,指定一条注释,并在反汇编代码清单中每个引用该函数的位置与函数一起显示这条注释。一个完整的导出项如下所示:
483 Name=RegOpenKeyA Pascal=12 Comment=Open a registry key
此外,读者可以参阅 idsutils
的 readme.txt 文件了解其他可选指令。 idsutils
解析实用工具的目的,是尽可能自动化地创建.idt 文件。创建.idt 文件的第一步是获得你希望解析的库的副本。然后,使用合适的解析实用工具解析这个副本。如果希望为与 OpenSLL 有关的 ssleay32.dll 库创建一个.idt 文件,可以使用以下命令:
$ ./dll2idt.exe ssleay32.dll Convert DLL to IDT file. Copyright 1997 by Yury Haron. Version 1.5 File: ssleay32.dll ... ok
这时,如果解析成功,我们将得到一个名为 SSLEAY32.idt 的文件。由于 dll2idt.exe 基于从 DLL 库本身获得的信息生成输出文件名,因此,输入文件名与输出文件名之间存在大小写差异。生成的.idt 文件的前几行如下所示:
ALIGNMENT 4 ;DECLARATION ; 0 Name=SSLEAY32.dll ; 121 Name=BIO_f_ssl 173 Name=BIO_new_buffer_ssl_connec 122 Name=BIO_new_ssl 174 Name=BIO_new_ssl_connect 124 Name=BIO_ssl_copy_session_id
需要注意的是,解析器无法确定一个函数是否使用 stdcall
,以及如果使用了,它从栈上删除了多少字节的数据。要想增加任何 Pascal
或 Comment
指令,你必须在创建最终的.ids 文件之前使用文本编辑器手动添加。创建.ids 文件的最后一个步骤是使用 zipids.exe 实用工具压缩.idt 文件,并将得到的.ids 文件复制到<IDADIR>/ids 目录中。
$ ./zipids.exe SSLEAY32.idt File: SSLEAY32.idt ... {219 entries [0/0/0]} packed $ cp SSLEAY32.ids ../Ida/ids
这样,只要加载了一个链接到 ssleay32.dll 的二进制文件,IDA 就会加载 SSLEAY32.ids。如果你选择不将新建的.ids 文件复制到<IDADIR>/ids 目录中,你随时可以通过 File▶Load File ▶IDS File 加载它们。
在使用.ids 文件时,可采用另一个步骤将.ids 文件链接到特定的.sig 或.til 文件。在选择.ids 文件时,IDA 会使用一个名为<IDADIR>/ida/idsnames 的 IDS 配置文件。这个文本文件可执行以下操作。
- 将共享库的名称与它对应的.ids 文件名映射起来。如果共享库的名称不能完全转换成一个 MS-DOS 8.3 形式的文件名,这样做可帮助 IDA 定位正确的.ids 文件,如下所示:
libc.so.6 libc.ids +
- 将.ids 与.til 文件映射起来。这样,只要 IDA 加载指定的.ids 文件,它会自动加载指定的.til 文件。使用下面的命令,一旦 IDA 加载 SSLEAY32.ids,openssl.til 文件将会自动加载(请参阅 idsnames 文件了解相关语法信息):
SSLEAY32.ids SSLEAY32.ids + openssl.til
- 将.sig 文件与对应的.ids 文件映射起来。这样,只要反汇编代码清单应用指定的.sig 文件,IDA 将加载指定的.ids 文件。下面的命令行指出:一旦用户应用 libssl.sig FLIRT 签名,IDA 应加载 SSLEAY32.ids 文件:
libssl.sig SSLEAY32.ids +
第 15 章将介绍如何使用脚本编写 idsutils
提供的库解析器。同时,我们将利用 IDA 的函数分析功能生成更加详细的.idt 文件。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论