- 献词
- 致谢
- 前言
- 第一部分 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 交叉引用
15.4 有用的 IDC 函数
现在,你已经拥有了编写格式完整的 IDC 脚本所需的全部信息。但是,你仍然无法与 IDA 进行任何有益的交互。IDC 提供了大量内置函数用于以各种方式访问数据库。IDA 帮助系统的 Index of IDC functions (IDC 函数目录)主题对所有这些函数进行了一定程度的说明。多数情况下,这些说明不过是从 IDC 的主要包含文件 idc.idc 中复制的几行相关内容。在学习 IDC 的过程中,弄懂这些简短的说明是一个令人沮丧的经历。“在 IDC 中,我该如何完成 x?”这个问题通常没有明确的答案。要想完成某个任务,最常用的方法是浏览 IDC 的函数列表,根据其名称寻找一个似乎能够满足需求的函数。函数是根据用途来命名的,这一推断并非总是成立。例如,许多时候,从数据库中提取信息的函数叫做 GetXXX
;但其他情况下,函数名并不使用 Get
前缀。更改数据库的函数可能直接叫做 SetXXX
、 MakeXXX
或其他别的名称。总体来说,如果你想要使用 IDC 函数,请经常浏览函数列表并仔细阅读函数说明。如果你完全不知所措,请访问 Hex-Rays 的支持论坛1 。
1. 该支持论坛当前的地址为 http://www.hex-rays.com/forum 。
在本节的剩余部分,我们将介绍一些有用(根据我们的经验)的 IDC 函数,并根据功能对它们分类。即使你只计划使用 Python 编写脚本,了解下面这些函数仍会对你有所帮助,因为 IDAPython 为这里的每一个函数提供了对应的 Python 函数。由于 IDA 帮助系统已经讨论了这些函数,所以我们并不打算介绍每一个 IDC 函数。
15.4.1 读取和修改数据的函数
下面的函数可用于访问数据库中的各个字节、字和双字。
long Byte(long addr)
,从虚拟地址addr
处读取一个字节值。long Word(long addr)
,从虚拟地址addr
处读取一个字(2 字节)值。long Dword(long addr)
,从虚拟地址addr
处读取一个双字(4 字节)值。void PatchByte(long addr, long val)
,设置虚拟地址addr
处的一个字节值。void PatchWord(long addr, long val)
,设置虚拟地址addr
处的一个字值。void PatchDword(long addr, long val)
,设置虚拟地址addr
处的一个双字值。bool isLoaded(long addr)
,如果addr
包含有效数据,则返回 1,否则返回 0。
在读取和写入数据库时,这里的每一个函数都考虑到了当前处理器模块的字节顺序(小端或大端)。 PatchXXX
函数还根据被调用的函数,通过仅使用适当数量的低位字节,将所提供的值调整到适当大小。例如,调用 PatchByte(0x401010, 0x1234)
将使用字节值 0x34(0x1234
的低位字节)修改 0x401010
位置。如果在用 Byte
、 Word
和 Dword
读取数据库时提供了一个无效的地址,它们将分别返回值 0xFF
、 0xFFFF
和 0xFFFFFFFF
。因为你没有办法将这些错误值与存储在数据库中的合法值区分开来,因此,在尝试从数据库中的某个地址读取数据之前,你可能希望调用 isLoaded
函数,以确定这个地址是否包含任何数据。
由于 IDA 在刷新反汇编窗口时“行为古怪”,你可能会发现,修补操作的结果并不会立即在窗口中显示出来。这时,你可以拖动滚动带离开被修补的位置,然后返回这个位置,即可迫使窗口正确进行更新。
15.4.2 用户交互函数
为了进行用户交互,需要熟悉 IDC 的输入/输出函数。下面详细介绍 IDC 的一些重要的接口函数。
void Message(string format, ...)
,在输出窗口打印一条格式化消息。这个函数类似于 C 语言的printf
函数,并接受printf
风格的格式化字符串。void print(...)
,在输出窗口中打印每个参数的字符串表示形式。void Warning(string format, ...)
,在对话框中显示一条格式化消息。string AskStr(string default, string prompt)
,显示一个输入框,要求用户输入一个字符串值。如果操作成功,则返回用户的字符串;如果对话框被取消,则返回 0。string AskFile(long doSave, string mask, string prompt)
,显示一个文件选择对话框,以简化选择文件的任务。你可以创建新文件保存数据(doSave=1
),或选择现有的文件读取数据(doSave=0
)。你可以根据mask
(如*.*或*.idc
)过滤显示的文件列表。如果操作成功,则返回选定文件的名称;如果对话框被取消,则返回 0。long AskYN(long default, string prompt)
,用一个答案为“是”或“否”的问题提示用户,突出一个默认的答案(1 为是,0 为否,-1 为取消)。返回值是一个表示选定答案的整数。long ScreenEA()
,返回当前光标所在位置的虚拟地址。bool Jump(long addr)
,跳转到反汇编窗口的指定地址。
因为 IDC 没有任何调试工具,你可能需要将 Message
函数作为你的主要调试工具。其他几个 AskXXX
函数用于处理更加专用的输入,如整数输入。请参考帮助系统文档了解可用的 AskXXX
函数的完整列表。如果希望创建一个根据光标位置调整其行为的脚本,这时, ScreenEA
函数就非常有用,因为你可以通过它确定光标的当前位置。同样,如果你的脚本需要将用户的注意力转移到反汇编代码清单中的某个位置,也需要用到 Jump
函数。
15.4.3 字符串操纵函数
虽然简单的字符串赋值和拼接操作可以通过 IDC 中的基本运算符实现,但是,更加复杂的操作必须使用字符串操纵函数实现,这些函数如下所示。
string form(string format, ...)//preIDA5.6
,返回一个新字符串,该字符串根据所提供的格式化字符串和值进行格式化。这个函数基本上等同于 C 语言的sprint
函数。string sprintf(string format,...)//IDA5.6+
,在 IDA5.6 中,sprint
用于替代form
(参见上面)。long atol(string val)
,将十进制值val
转换成对应的整数值。long xtol(string val)
,将十六进制值val
(可选择以0x
开头)转换成对应的整数值string ltoa(long val, long radix)
,以指定的radix
(2 、8 、10 或 16)返回val
的字符串值。long ord(string ch)
,返回单字符字符串ch
的 ASCII 值。long strlen(string str)
,返回所提供字符串的长度。long strstr(string str, string substr)
,返回str
中substr
的索引。如果没有发现子字符串,则返回-1。string substr(string str, long start, long end)
,返回包含str
中由start
到end-1
位置的字符的子字符串。如果使用分片(IDA5.6 及更高版本),此函数等同于str[start:end]
。
如前所述,IDC 中没有任何字符数据类型,它也不支持任何数组语法。如果你想要遍历字符串的每个字符,必须把字符串中的每个字符当成连续的单字符子字符串处理。
15.4.4 文件输入/ 输出函数
输出窗口并不总是显示脚本输出的理想位置。对于生成大量文本或二进制数据的脚本,你可能希望将其结果输出到磁盘文件上。我们已经讨论了如何使用 AskFile
函数要求用户输入文件名。但是, AskFile
仅返回一个包含文件名的字符串值。IDC 的文件处理函数如下所示。
long fopen(string filename, string mode)
,返回一个整数文件句柄(如果发生错误,则返回 0),供所有 IDC 文件输入/输出函数使用。mode
参数与 C 语言的fopen
函数使用的模式(r
表示读取,w
表示写入,等等)类似。void fclose(long handle)
,关闭fopen
中文件句柄指定的文件。long filelength(long handle)
,返回指定文件的长度,如果发生错误,则返回-1。long fgetc(long handle)
,从给定文件中读取一个字节。如果发生错误,则返回-1。long fputc(long val, long handle)
,写入一个字节到给定文件中。如果操作成功,则返回 0;如果发生错误,则返回-1。long fprintf(long handle, string format, ...)
,将一个格式化字符串写入到给定文件中。long writestr(long handle, string str)
,将指定的字符串写入到给定文件中。string/long readstr(long handle)
,从给定文件中读取一个字符串。这个函数读取到下一个换行符为止的所有字符(包括非 ASCII 字符),包括换行符本身(ASCII 0xA)。如果操作成功,则返回字符串;如果读取到文件结尾,则返回-1。long writelong(long handle, long val, long bigendian)
,使用大端(bigendian=1
)或小端(bigendian=0
)字节顺序将一个 4 字节整数写入到给定文件。long readlong(long handle, long bigendian)
,使用大端(bigendian=1
)或小端(bigendian=0
)字节顺序从给定的文件中读取一个 4 字节整数。long writeshort(long handle, long val, long bigendian)
,使用大端(bigendian=1
)或小端(bigendian=0
)字节顺序将一个 2 字节整数写入到给定的文件。long readshort(long handle, long bigendian)
,使用大端(bigendian=1
)或小端(bigendian=0
)字节顺序从给定的文件中读取一个 2 字节整数。bool loadfile(long handle, long pos, long addr, long length)
,从给定文件的pos
位置读取length
数量的字节,并将这些字节写入到以addr
地址开头的数据库中。bool savefile(long handle, long pos, long addr, long length)
,将以addr
数据库地址开头的length
数量的字节写入给定文件的pos
位置。
15.4.5 操纵数据库名称
在脚本中,你经常需要操纵已命名的位置。下面的 IDC 函数用于处理 IDA 数据库中已命名的位置。
string Name(long addr)
,返回与给定地址有关的名称,如果该位置没有名称,则返回空字符串。如果名称被标记为局部名称,这个函数并不返回用户定义的名称。string NameEx(long from, long addr)
,返回与addr
有关的名称。如果该位置没有名称,则返回空字符串。如果from
是一个同样包含addr
的函数中的地址,则这个函数返回用户定义的局部名称。bool MakeNameEx(long addr, string name, long flags)
,将给定的名称分配给给定的地址。该名称使用flags
位掩码中指定的属性创建而成。这些标志在帮助系统中的MakeNameEx
文档中有记载描述,可用于指定各种属性,如名称是局部名称还是公共名称、名称是否应在名称窗口中列出。long LocByName(string name)
,返回一个位置(名称已给定)的地址。如果数据库中没有这个名称,则返回 BADADDR (-1)。long LocByNameEx(long funcaddr, string localname)
,在包含funcaddr
的函数中搜索给定的局部名称。如果给定的函数中没有这个名称,则返回 BADADDR (-1)。
15.4.6 处理函数的函数
许多脚本专用于分析数据库中的函数。IDA 为经过反汇编的函数分配大量属性,如函数局部变量区域的大小、函数的参数在运行时栈上的大小。下面的 IDC 函数可用于访问与数据库中的函数有关的信息。
long GetFunctionAttr(long addr, long attrib)
,返回包含给定地址的函数的被请求的属性。请参考 IDC 帮助文档了解属性常量。例如,要查找一个函数的结束地址,可以使用GetFunctionAttr(addr, FUNCATTR_END)
;。string GetFunctionName(long addr)
,返回包含给定地址的函数的名称。如果给定的地址并不属于一个函数,则返回一个空字符串。long NextFunction(long addr)
,返回给定地址后的下一个函数的起始地址。如果数据库中给定地址后没有其他函数,则返回-1。long PrevFunction(long addr)
,返回给定地址之前距离最近的函数的起始地址。如果在给定地址之前没有函数,则返回-1。
根据函数的名称,使用 LocBy Name
函数查找该函数的起始地址。
15.4.7 代码交叉引用函数
交叉引用已在第 9 章讨论过 。IDC 提供各种函数来访问与指令有关的交叉引用信息。要确定哪些函数能够满足你的脚本的要求,可能有些令人困惑。它要求你确定:你是否有兴趣跟从离开给定地址的流,是否有兴趣迭代引用给定地址的所有位置。下面我们将介绍执行上述两种操作的函数。其中几个函数用于支持对一组交叉引用进行迭代。这些函数支持交叉引用序列的概念,并需要一个 current
交叉引用,以返回一个 next
交叉引用。使用交叉引用迭代器的示例请参见 15.5.3 节。
long Rfirst(long from)
,返回给定地址向其转交控制权的第一个位置。如果给定的地址没有引用其他地址,则返回 BADADDR (-1)。long Rnext(long from, long current)
,如果current
已经在前一次调用Rfirst
或Rnext
时返回,则返回给定地址(from
)转交控制权的下一个位置。如果没有其他交叉引用存在,则返回 BADADDR 。long XrefType()
,返回一个常量,说明某个交叉引用查询函数(如Rfirst
)返回的最后一个交叉引用的类型。对于代码交叉引用,这些常量包括fl_CN
(近调用)、fl_CF
(远调用)、fl_JN
(近跳转)、fl_JF
(远跳转)和fl_F
(普通顺序流)。long RfirstB(long to)
,返回转交控制权到给定地址的第一个位置。如果不存在对给定地址的交叉引用,则返回 BADADDR (-1)。long RnextB(long to, long current)
,如果current
已经在前一次调用RfirstB
或RnextB
时返回,则返回下一个转交控制权到给定地址(to
)的位置。如果不存在其他对给定位置的交叉引用,则返回 BADADDR (-1)。
每次调用一个交叉引用函数,IDA 都会设置一个内部 IDC 状态变量,指出返回的最后一个交叉引用的类型。如果需要知道你收到的交叉引用的类型,那么在调用其他交叉引用查询函数之前,必须调用 XrefType
函数。
15.4.8 数据交叉引用函数
访问数据交叉引用信息的函数与访问代码交叉引用信息的函数非常类似。这些函数如下所示。
long Dfirst(long from)
,返回给定地址引用一个数据值的第一个位置。如果给定地址没有引用其他地址,则返回 BADADDR (-1)。long Dnext(long from, long current)
,如果current
已经在前一次调用Dfirst
或Dnext
时返回,则返回给定地址(from
)向其引用一个数据值的下一个位置。如果没有其他交叉引用存在,则返回 BADADDR 。long XrefType()
,返回一个常量,说明某个交叉引用查询函数(如Dfirst
)返回的最后一个交叉引用的类型。对于数据交叉引用,这些常量包括dr_0
(提供的偏移量)、dr_W
(数据写入)和dr_R
(数据读取)。long DfirstB(long to)
,返回将给定地址作为数据引用的第一个位置。如果不存在引用给定地址的交叉引用,则返回 BADADDR (-1)。long DnextB(long to, long current)
,如果currnet
已经在前一次调用DfristB
或DnextB
时返回,则返回将给定地址(to
)作为数据引用的下一次位置。如果没有其他对给定地址的交叉引用存在,则返回 BADADDR 。
和代码交叉引用一样,如果需要知道你收到的交叉引用的类型,那么在调用另一个交叉引用查询函数之前,必须调用 XrefType
函数。
15.4.9 数据库操纵函数
有大量函数可用于对数据库的内容进行格式化。这些函数如下所示。
void MakeUnkn(long addr, long flags)
,取消位于指定地址的项的定义。这里的标志(参见 IDC 的MakeUnkn
文档)指出是否也取消随后的项的定义,以及是否删除任何与取消定义的项有关的名称。相关函数MakeUnknown
允许你取消大块数据的定义。long MakeCode(long addr)
,将位于指定地址的字节转换成一条指令。如果操作成功,则返回指令的长度,否则返回 0。bool MakeByte(long addr)
,将位于指定地址的项目转换成一个数据字节。类似的函数还包括MakeWord
和MakeDword
。bool MakeComm(long addr, string comment)
,在给定的地址处添加一条常规注释。bool MakeFunction(long begin, long end)
,将由begin
到end
的指令转换成一个函数。如果end
被指定为 BADADDR (1),IDA 会尝试通过定位函数的返回指令,来自动确定该函数的结束地址。bool MakeStr(long begin, long end)
,创建一个当前字符串(由GetStringType
返回)类型的字符串,涵盖由begin
到end-1
之间的所有字节。如果end
被指定为 BADADDR ,IDA 会尝试自动确定字符串的结束位置。
有许多其他 Make XXX
函数可提供类似于上述函数的操作。请参考 IDC 文档资料了解所有这些函数。
15.4.10 数据库搜索函数
在 IDC 中,IDA 的绝大部分搜索功能可通过各种 FindXXX
函数来实现,下面我们将介绍其中一些函数。 FindXXX
函数中的 flags
参数是一个位掩码,可用于指定查找操作的行为。3 个最为常用的标志分别为 SEARCH_DOWN
,它指示搜索操作扫描高位地址; SEARCH_NEXT
,它略过当前匹配项,以搜索下一个匹配项; SEARCH_CASE
,它以区分大小写的方式进行二进制和文本搜索。
long FindCode(long addr, long flags)
,从给定的地址搜索一条指令。long FindData(long addr, long flags)
,从给定的地址搜索一个数据项。long FindBinary(long addr, long flags, string binary)
,从给定的地址搜索一个字节序列。字符串binary
指定一个十六进制字节序列值。如果没有设置SEARCH_CASE
,且一个字节值指定了一个大写或小写 ASCII 字母,则搜索仍然会匹配对应的互补值。例如,“41 42”将匹配“61 62”(和“61 42”),除非你设置了SEARCH_CASE
标志。long FindText(long addr, long flags, long row, long column, string text)
,在给定的地址,从给定行(row
)的给定列搜索字符串text
。注意,某个给定地址的反汇编文本可能会跨越几行,因此,你需要指定搜索应从哪一行开始。
还要注意的是, SEARCH_NEXT
并未定义搜索的方向,根据 SEARCH_DOWN
标志,其方向可能向上也可能向下。此外,如果没有设置 SEARCH_NEXT
,且 addr
位置的项与搜索条件匹配,则 FindXXX
函数很可能会返回 addr
参数传递给该函数的地址。
15.4.11 反汇编行组件
许多时候,我们需要从反汇编代码清单的反汇编行中提取出文本或文本的某个部分。下面的函数可用于访问反汇编行的各种组件。
string GetDisasm(long addr)
,返回给定地址的反汇编文本。返回的文本包括任何注释,但不包括地址信息。string GetMnem(long addr)
,返回位于给定地址的指令的助记符部分。string GetOpnd(long addr, long opnum)
,返回指定地址的指定操作数的文本形式。IDA 以零为起始编号,从左向右对操作数编号。long GetOpType(long addr, long opnum)
,返回一个整数,指出给定地址的给定操作数的类型。请参考GetOpType
的 IDC 文档,了解操作数类型代码。long GetOperandValue(long addr, long opnum)
,返回与给定地址的给定操作数有关的整数值。返回值的性质取决于GetOpType
指定的给定操作数的类型。string CommentEx(long addr, long type)
,返回给定地址处的注释文本。如果type
为 0,则返回常规注释的文本;如果type
为 1,则返回可重复注释的文本。如果给定地址处没有注释,则返回一个空字符串。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论