IDA Pro 权威指南学习笔记(十四) - 操纵函数
IDA 无法定位一个函数调用,由于没有直接的方法到达函数,IDA 将无法识别它们
IDA 可能无法正确确定函数的结束部分,需要手动干预,以更正反汇编代码中的错误
如果编译器已经将函数分割到几个地址范围,或者在优化代码的过程中,编译器为节省空间,将两个或几个函数的共同结束序列合并在一起,这时 IDA 同样无法确定函数的结束部分
新建函数
在某些情况下,可以在没有函数的地方创建新函数
新函数可以由已经不属于某个函数的现有指令创建,或者由尚未被 IDA 以任何其他方式定义(如双字或字符串)的原始数据字节创建
将光标放在将要包含在新函数中的第一个字节或指令上,然后选择 Edit -> Functions -> Create Function,即可创建一个新函数,快捷键为 Alt+P
在必要时,IDA 会将数据转换成代码
接下来,它会向前扫描,分析函数的结构,并搜索返回语句
如果 IDA 能够找到正确的函数结束部分,它将生成一个新的函数名,分析栈帧,并以函数的形式重组代码
如果它无法找到函数的结束部分,或者发现任何非法指令,则这个操作将以失败告终
删除函数
可以使用 Edit -> Functions -> Delete Function 命令删除现有函数
点 Yes,当前函数将被删除
点击 Yes 后
函数块
在由 Microsoft Visual C++ 编译器生成的代码中,经常可以找到函数块
编译器移动不常执行的代码段,用以将经常执行的代码段“挤入”不大可能被换出的内存页,由此便产生了函数块
如果一个函数以这种方式被分割,IDA 会通过跟踪指向每个块的跳转,尝试定位所有相关的块
多数情况下,IDA 都能找到所有这些块,并在函数的头部列出每一个块
有时候,IDA 可能无法确定与函数关联的每一个块,或者函数可能被错误地识别成函数块,而非函数本身
在这种情况下,需要创建自己的函数块,或删除现有的函数块
在反汇编代码清单中,函数块就叫做函数块;在 IDA 的菜单系统中,函数块叫做函数尾(function tail)
要删除现有的函数块,将光标放在要删除的块中的任何一行上,然后选择 Edit -> Functions -> Remove Function Tail
在初次将文件加载到 IDA 时,取消选择 Create function tails 加载器选项,IDA 将不创建函数块
如果禁用了函数尾,已经包含函数尾的函数将包含指向函数边界以外区域的跳转,IDA 会在反汇编代码清单左侧的箭头窗口中用红线和箭头突出显示这些跳转
函数特性
IDA 为它识别的每一个函数提供许多特性
函数属性对话框可用于编辑其中的某些特性
Name of function 为函数名称,提供另外一种更改函数名称的方法
Start address 为起始地址,函数中第一条指令的地址
通常,IDA 会在分析过程中,或根据创建函数时所使用的地址,自动识别这个地址
End address 为结束地址,函数中最后一条指令之后的地址
通常,它是函数的返回语句之后的指令的地址
这个地址并不是函数的一部分,而是函数的最后一条指令之后的地址
Local variables area 为局部变量区,函数的局部变量(如下图)专用的栈字节数
多数情况下,IDA 会通过分析函数的栈指针的行为,自动计算出这个值
Save registers 为保存的寄存器
为调用方保存寄存器(如上图)所使用的字节数,IDA 认为保存的寄存器区域放在保存的返回地址顶部、与函数有关的所有局部变量的下方
一些编译器选择将寄存器保存在函数局部变量的顶部
IDA 认为保存这些寄存器所使用的空间属于局部变量区域,而非保存的寄存器区域
Purged bytes 为已删除字节
已删除字节表示当函数返回调用方时,IDA 从栈中删除的参数的字节数
对 cdecl 函数而言,这个值始终为0
对 stdcall 函数来说,这个值表示传递到栈上的所有参数占用的空间
在 x86 程序中,如果 IDA 观察到程序使用了返回指令的 RET N 变体,它将自动确定这个值
Frame pointer delta 为帧指针增量
有时候,编译器可能会对函数的帧指针进行调整,使其指向局部变量区域的中间,而不是指向保存在局部变量区域底部的帧指针
调整后的帧指针到保存的帧指针之间的这段距离叫做帧指针增量
多数情况下,IDA 会在分析函数的过程中自动计算出帧指针增量
使用增量的目的,是在离帧指针 1 字节(带符号)的偏移量(-128 ~ +127)内保存尽可能多的栈帧变量
复选框为 IDA 自动分析得到的结果,以下是可启用也可禁用的属性
Does not return 为不返回,函数不返回到它的调用方
如果调用这样的函数,在相关的调用指令之后,IDA 认为函数不会继续执行
Far function 为远函数
这个属性用于在分段体系结构上将一个函数标记为远函数
在调用该函数时,函数的调用方需要指定一个段和一个偏移值
是否使用远调用,应由程序中使用的内存模式决定
Library func 为库函数
这个属性将一个函数标记为库代码
库代码可能包括静态链接库中的编译器或函数所包含的支持例程
将一个函数标记为库函数后,该函数将以分配给库函数的颜色显示,从而与非库代码区分开来
Static func 为静态函数,静态函数只在函数的特性列表中显示静态修饰符
BP based frame 为基于BP的帧
这个特性表示函数利用了一个帧指针
如果选择了这个特性,一定要相应地调整保存的寄存器的大小(通常指根据保存的帧指针的大小增大)和局部变量的大小(通常指根据保存的帧指针的大小减少)
对基于帧指针的帧而言,使用帧指针的内存引用被格式化,以利用符号栈变量名称,而非数字偏移量
如果没有设置这个特性,则认为栈帧引用与栈指针寄存器有关
BP equals to SP 为 BP 等于 SP
它的作用等同于将帧指针增量的大小设置为等于局部变量区域
一些函数将帧指针配置为在进入一个函数时指向栈帧(以及栈指针)的顶端,这时候需要设置该属性
栈指针调整
IDA 会尽其所能跟踪函数内每一条指令上的栈指针的变化
如果 IDA 无法确定一条指令是否更改了栈指针,就需要手动调整栈指针
如果一个函数调用了另一个使用 stdcall 调用约定的函数,就会出现上述情况,这是最简单的一种情况
如果被调用的函数位于 IDA 无法识别的共享库中,IDA 不知道该函数使用了 stdcall 调用约定,也就无法认识到:被调用的函数会将栈指针修改后返回
进行栈调整,首先选中进行调整的地址,选择 Edit -> Functions -> Change Stack Pointer(快捷键为 Alt+K),然后指定栈指针更改的字节数