整理到这儿,发现自己整理的顺序获取有点问题,应该是从粗粒度到细粒度这种自顶向下来进行整理,从段、函数、基本块、指令这样。
API速查如下:
| 模块 | API | 功能 |
|---|---|---|
| idautils | Functions() | 获取所有函数起始地址集合 |
| idc | get_func_name(ea) | 获取地址ea所在函数的函数名 |
| idaapi | get_func(ea) | 获取地址ea所在的函数类 |
| idc | get_next_func(ea) | 获取地址ea所在函数的下一个函数 |
| idc | get_prev_func(ea) | 获取地址ea所在函数的上一个函数 |
| idc | get_func_attr(ea, attr) | 获取地址ea所在函数的attr函数属性 |
| idaapi | get_arg_addrs(ea) | 获取函数调用地址ea处,对传参进行操作的指令集合 |
函数基本操作和属性
在IDAPython中可以枚举所有的函数:
1 | import idautils |
idautils.Functions()会返回一个所有已知函数起始地址的列表,也可以通过idautils.Functions(start_addr, end_addr)来指定函数搜索的地址范围。idc.get_func_name(ea)则可以获取到函数名,ea可以是函数内的任一地址。
在IDAPython中,代表函数的数据结构可以使用idaapi.get_func(ea)来获得,ea是函数内的任一地址。此处依旧使得ea在cgibin的main函数中:
1 | Python>func = idaapi.get_func(ea) |
可以看到func是一个ida_funcs.func_t的类,可以进一步使用dir(fucn)查类中的成员。几个比较典型的成员(x86/64下没有测试过):
analyzed_sp():是否已经分析了SP(堆栈?)does_return():是否有返回值start_ea和end_ea:函数的起始和结束地址argsize:栈上保存的传参数量(MIPS下测试不准)flags:函数标志fpd:栈帧指针?frregs:栈帧中存放的寄存器数量frsize:栈帧中的局部变量argsize:寄存器传参数量referers:调用该函数的其他函数起始地址,列表refqty:referer的数量(我测试了感觉不准啊,不是我想象中的引用数量)regargqty:寄存器传参数量regargs:寄存器传参列表regvarqty:寄存器变量数量regvars:寄存器变量列表
在idc模块还提供了许多API用于对函数的相关属性进行查询
idc.get_next_func(ea)和idc.get_prev_func(ea):获取当前函数的下/上一个函数,从地址空间上而言idc.get_func_attr(ea, FUNCATTR_START)和idc.get_func_attr(ea, FUNCATTR_END):获取函数的起始地址和结束地址
如下是一个遍历函数所有指令的代码:
1 | import idc |
idc.get_func_attr(ea, attr)还可以查询到函数的需要重要属性,如下(未列举全):
FUNC_NORET:函数无返回FUNC_LIB:函数为库函数FUNC_THUNK:thunk函数(未理解)
其实在idc模块中还有许多对函数的操作,可以参考官方文档idc模块
重要!获取函数传参
这个是第一次看到,IDAPython中具有获取函数传参相关的API。在之前,分析函数传参我是采用的先获取到架构的传参方式,例如MIPS传参通过a0~a3寄存器,通过jr等调用指令来调用函数。然后定位到jr指令基本块,并进行回溯,找到所有对寄存器的最近赋值操作,分析函数传参。IDAPython中有idaapi.get_arg_addrs(ea)可以获取到函数原型并判断传参。
分析一个简单的MIPS程序,反编译结果如下:
1 | { |
汇编代码:在0x00405748处调用函数
1 | .text:0040572C move $t9, $v1 |
IDAPython识别传参:可以识别到最近一次对寄存器a0、a1的赋值操作,但是忽略了a2寄存器,因为a2是在指令延迟槽中。对于MIPS架构还是有点小瑕疵哈,但对x86/64这种应该识别率还是挺高的,毕竟反编译都能出来结果。
1 | Python>ea = 0x00405748 |