logo资料库

the beginner's guide to idapython 中文版.pdf

第1页 / 共56页
第2页 / 共56页
第3页 / 共56页
第4页 / 共56页
第5页 / 共56页
第6页 / 共56页
第7页 / 共56页
第8页 / 共56页
资料共56页,剩余部分请下载后查看
一些基础的函数
函数
指令
交叉引用
搜寻
数据选取
注释 & 重命名
处理原数据
打补丁
输入输出
批量处理解析文件
执行脚本
实例
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 的函数类型,
分享到:
收藏