整理到这儿,发现自己整理的顺序获取有点问题,应该是从粗粒度到细粒度这种自顶向下来进行整理,从段、函数、基本块、指令这样。
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 |