idapython 主要依赖的的包由以下三部分组成:
idc
idautils
idaapi
一些基础的函数
位置相关函数(用于获取光标,或特殊数据的地址)
idc.ScreenEA() or here()返回当前光标位置
MinEA() 返回 idb 的起始地址
MaxEA() 返回 idb 的结束地址
hex(MinEA()) 将起始地址以 16 进制输出,如下所示:
idc.SegName(ea) 获取 ea 地址的段,比如 text
idc.GetDisasm(ea) 获取 ea 地址的反汇编指令
ida.GetMnem(ea) 获取 ea 地址的反汇编指令操作码
ida.GetOpnd(ea,n) 获取 ea 地址的反汇编指令的第 n 个操作数,由 0 开始,
实例如下:
有时需要用于判断一个地址是否无效,可通过 idaapi.BADADDR 实现,该函数返
回一个内置的无效地址,具体使用如下:
if idaapi.BADADDR != here():
print " vaild addr"
节
idapython 的强大之处在于对 idb 数据库的循环迭代,其中包括指令,交叉
引用等,这些会在下文中提到,反编译文件的数据节迭代会是一个不错的开始,
代码如下所示:
import idautils
for seg in idautils.Segments():
print idc.SegName(seg),idc.SegStart(seg),idc.SegEnd(seg)
idautils.Segments()函数返回该反编译文件中所有的节信息,托 python
的福,这里我们可以非常方便的直接对这些节信息进行循环迭代,每一次迭代会
对一个节进行处理。
每层迭代中使用到以下函数
idc.SegName(seg) 获取节名
idc.SegStart(seg)获取该节的开始
idc.SegEnd(seg)获取该节的结束
通过函数 idc.NextSeg(ea)可以获取下一个节,参数为当前节地址范围内任
意地址皆可。
函数
继数据节之后我们下一个目标是函数。
import idautils
for func in idautils.Functions():
print hex(func),idc.GetFunctionName(func)
idautils.Functions()会返回一个所有函数的起始地址列表,该函数也支持
区域查找,通过给函数传入起始和终止地址,可以控制搜寻的范围。
如 idautils.Functions(start_addr,end_addr),idc.GetFunctionName(ea)
返回一个函数名,ea 可以为该函数中的任意二进制地址。
通过函数 idaapi.get_func(ea),获取一个 idaapi.func_t 的类,该类定义
了函数的一些属性,如下脚本所示,通过该返回的数据结构获取该函数的边界值。
import idautils
ea = idc.ScreenEA()
print ea
func = idaapi.get_func(ea)
print type(func)
print "Start:0x%x,End:0x%x" %(func.startEA,func.endEA)
idaapi.get_func(ea)会返回一个函数类对象,通过命令 dir(class)可以
查看该类的导出函数和属性。
通过 idc.NextFunciton(ea)和 idc.PrevFunction(ea)分别可以获取当前地
址前后的两个函数的地址,传入的 ea 参数为当前函数区间的所有合法地址。
import idautils
ea = idc.ScreenEA()
print ea
next = idc.NextFunction(ea)
pre = idc.PrevFunction(ea)
print "the next is 0x%x,the pre is 0x%x" %(next,pre)
idapython 提过了多种获取数据信息的方式,另一种获取函数边界值的方式
可以通过以下两个函数。
通过函数 idc.GetFunctionAttr(ea,FUNCATTR_START),
idc.GetFunctionAttr(ea,FUNCATTR_END)。获取地址 ea 所在函数的开始和结尾。
通过函数 idc.GetDisasm(ea)获取当前地址的反汇编代码。
通过函数 idc.NextHead(ea)获取当前地址之后下一条指令的起始地址。
但是有时候当一个函数中存在一个大跳时,通过以上两种方式获取的函数边
界值就会出现问题,这个时候最好的方式是通过函数 idautils.FuncItems(ea)
实现该功能,具体的使用会在后文中提到。
import idautils
ea = idc.ScreenEA()
print ea
start = idc.GetFunctionAttr(ea,FUNCATTR_START)
end = idc.GetFunctionAttr(ea,FUNCATTR_END)
cur_addr = start
while cur_addr < end:
print hex(cur_addr),idc.GetDisasm(cur_addr)
cur_addr = idc.NextHead(cur_addr,end)
类似于 idc.GetFunctionAttr(ea, attr),idc.GetFuntionAttr(ea)也是
用于获取函数的一些信息。
通过函数 idc.GetFuntionAttr(ea)返回该函数的类型,idapython 中一共提
供了 9 类函数类型的标签。
可通过下列脚本列举函数标签,
import idautils
ea = idc.ScreenEA()
print ea
for func in idautils.Functions():
flags = idc.GetFunctionFlags(func)
if flags & FUNC_NORET:
print hex(func),"FUNC_NORET"
if flags & FUNC_FAR:
print hex(func), "FUNC_FAR"
if flags & FUNC_LIB:
print hex(func), "FUNC_LIB"
if flags & FUNC_STATIC:
print hex(func), "FUNC_STATIC"
if flags & FUNC_FRAME:
print hex(func), "FUNC_FRAME"
if flags & FUNC_USERFAR:
print hex(func), "FUNC_USERFAR"
if flags & FUNC_HIDDEN:
print hex(func), "FUNC_HIDDEN"
if flags & FUNC_THUNK:
print hex(func), "FUNC_THUNK"
if flags & FUNC_LIB:
print hex(func), "FUNC_BOTTOMBP"
通过 idautils.Functions()获取所有函数的地址列表,通过
idc.GetFunctionGFlage(func)获取函数的 flag,通过 flags&来判断函数的类型。
所有函数的类型如下所述:
FUNC_NORET
标示没有返回值的函数,内部用 1 标示。
FUNC_FAR
该标识的函数很少使用除非被逆向的软件中包含内存节,内部用 2 标示
FUNC_USERFAR
同样很少见,ida 的文档中
“user has specif ied f ar-ness of the f unction”,内部用 3
标示
FUNC_LIB
该标识用于标记动态链接库的函数,内部用 4 标记。
以下脚本用于列举所有的链接库函数
import idautils
ea = idc.ScreenEA()
print ea
for func in idautils.Functions():
flags = idc.GetFunctionFlags(func)
if flags & FUNC_LIB:
print hex(func), "FUNC_LIB",GetFunctionName(func)
FUNC_STATIC
该标记用于标记 static 函数。
FUNC_FRAME
该标记用于标示使用了 ebp 作为栈帧的函数,此类函数有典型的开头。具体如下:
FUNC_BOTTOMBP
该标记和 FUNC_FRAME 类似。
FUNC_THUNK
该标记用于标示中转函数,即只用一个 jmp 的函数
因此一个函数可以拥有多个函数标记
指令
目前我们已经知道如何对 idb 中的函数进行操作,接下来通过函数
idautils.FuncItems(ea)获取一个函数中的所有指令地址,该函数会返回包含输
入地址所属函数内所有指令地址的一个类 list(不是 list)。
import idautils
ea = idc.ScreenEA()
print ea
dism_addr = list(idautils.FuncItems(ea))
print type(dism_addr)
print dism_addr
for line in dism_addr:
print hex(line),idc.GetDisasm(line)
现在已经知道如何循环迭代节,函数,以及指令,接下来看一个用于搜寻被
逆向软件中动态调用的脚本,所谓的动态调用即通过 call 或 jmp 直接跳转到寄
存器地址的调用。
import idautils
for func in idautils.Functions():
flags = idc.GetFunctionFlags(func)
if flags & FUNC_LIB or flags & FUNC_THUNK:
continue
dism_addr = list(idautils.FuncItems(func))
for line in dism_addr:
m = idc.GetMnem(line)
if m == 'call' or m =='jmp':
op = idc.GetOpType(line,0)
if op == o_reg:
print "0x%x %s" % (line, idc.GetDisasm(line))
在该脚本中通过调用 idautils.Functions 获取已知的函数地址列表,对于
每一个函数通过 idc.GetFunctionFlags()获取其对应的 flags,其中过滤掉 flag
为 FUNC_LIB 和 FUNC_THUNK 的函数类型,