IDApython&IDC IDApython 官方文档:IDAPython | Hex-Rays Docs
IDC 官方文档:IDC | Hex-Rays Docs
常用模块 1.IDC 与 IDA 命令行进行交互的模块,提供了很多 IDA 中指令相关的函数。
idc.get_name(ea)
:获取地址 ea
处的名称。
idc.get_func_name(ea)
:获取函数地址处的函数名称。
idc.set_name(ea, name)
:为地址 ea
设置一个新的名称。
idc.add_entry(ea, name, start, end)
:在 ea
处添加一个入口点。
idc.get_func(ea)
:获取 ea
处的函数结构。
2.IDAAPI 与 IDA 的核心 API 交互的主要模块。包括了对 IDA 核心对象的访问,涵盖了地址、函数、节等许多底层操作。
idaapi.get_func(ea)
:返回地址 ea
处的函数对象。
idaapi.get_flags(ea)
:返回地址 ea
的标志,表示它是代码、数据还是其他类型。
idaapi.create_data(ea, size, type)
:在地址 ea
处创建数据。
idaapi.get_strlit_contents(ea)
:获取 ea
处的字符串内容。
idaapi.add_func(ea)
:将地址 ea
添加为一个函数。
3.IDAUTILS 工具函数,帮助在反汇编中自动化和简化分析过程。
idautils.Functions(start, end)
:遍历在 start
到 end
地址范围内的所有函数。
idautils.DecodeInstruction(ea)
:解码地址 ea
处的指令。
idautils.FindBinary(start, end, pattern, flags)
:在指定范围内查找与模式匹配的二进制数据。
idautils.Heads(start, end)
:遍历地址范围内的所有指令地址。
4.ida_bytes ida_bytes
提供了对内存区域字节级别操作的函数。
ida_bytes.get_byte(ea)
:获取 ea
处的一个字节
ida_bytes.get_word(ea)
:获取 ea
处的一个字.
ida_bytes.get_dword(ea)
:获取 ea
处的一个双字。
5.ida_struct ida_struct
处理 IDA 中的结构体。你可以使用它来读取、创建、修改和设置结构体。
ida_struct.get_struc_name(struc)
:获取结构体的名称。
ida_struct.add_struc()
:添加一个新的结构体。
ida_struct.set_struc_member()
:设置结构体成员的属性。
6.ida_diskio ida_diskio
提供了与磁盘 IO 操作相关的函数,例如读取或写入文件。
ida_diskio.get_input_file_path()
:获取当前输入文件的路径。
ida_diskio.save_input_file()
:保存当前输入文件。
常用技巧 获取基本块 ida_get_bb ida_get_bb
函数的功能是获取指定地址 ea
所在的基本块(Basic Block)。基本块是指在程序中连续的一组指令序列,这些指令在程序的控制流图上不会被中断,只有从头到尾的顺序执行,并且只有在块的结束处才能跳出这个块。
1 2 3 4 5 6 def ida_get_bb (ea ): f_blocks = idaapi.FlowChart(idaapi.get_func(ea), flags=idaapi.FC_PREDS) for block in f_blocks: if block.start_ea <= ea and ea < block.end_ea: return block return None
函数功能概述
输入 :虚拟地址 (ea
),即目标指令的地址。
输出 :包含该地址的基本块 (block
) 或者 None
(如果未找到)。
函数逻辑
利用 IDA Pro 的 idaapi.FlowChart
类生成当前函数的控制流图(Flow Chart),即函数内的所有基本块。
遍历每个基本块,检查该基本块的起始地址 (start_ea
) 和结束地址 (end_ea
),判断目标地址 (ea
) 是否在该基本块范围内。
如果找到符合条件的基本块,则返回该基本块对象。
如果遍历完所有基本块都没有找到符合条件的,返回 None
。
创建 / 删除函数 1 2 idc.add_func(0x401000 , 0x401050 ) idc.del_func(0x401000 )
获取函数的名称
遍历所有函数并打印其有效地址和名称 1 2 3 for func_ea in idautils.Functions(): func_name = idc.get_func_name(func_ea) print (hex (func_ea), func_name)
列出地址的交叉引用: 1 2 for xref in idautils.XrefsFrom(0x401000 ): print (f"Xref from 0x401000 to {hex (xref.to)} " )
遍历所有交叉引用并打印 1 2 for ref in idautils.XrefsTo(ea): print(hex(ref.frm))
获取段地址 idautils.Segments()
返回 所有段的起始地址 。
1 2 3 4 5 6 7 8 9 10 for seg in idautils.Segments(): seg_name = idc.get_segm_name(seg) if seg_name != ".text" and seg_name != "UPX0" : continue print (f"正在处理段: {seg_name} (0x{seg:X} )" ) start_ea = seg end_ea = idc.get_segm_end(seg)
文本 / 指令搜索 1 2 3 4 5 6 7 8 9 10 11 import idautilsimport idcdef search_string (pattern ): for ea in idautils.Strings(): if pattern in ea: print (f"Found string at {hex (ea)} : {ea} " ) search_string("Hello" )
1 2 3 4 5 6 7 8 9 10 11 12 13 import idautilsimport idcdef search_asm_instruction (pattern ): for ea in idautils.Heads(idc.get_segm_by_name(".text" ), idc.get_segm_end(idc.get_segm_by_name(".text" ))): disasm = idc.GetDisasm(ea) if pattern in disasm: print (f"Found instruction at {hex (ea)} : {disasm} " ) search_asm_instruction("mov eax, ebx" )
循环左移 / 右移 1 2 3 4 5 6 7 8 9 10 11 def ROR (i,index ): tmp = bin (i)[2 :].rjust(8 ,"0" ) for _ in range (index): tmp = tmp[-1 ] + tmp[:-1 ] return int (tmp, 2 ) def ROL (i,index ): tmp = bin (i)[2 :].rjust(8 , "0" ) for _ in range (index): tmp = tmp[1 :] + tmp[0 ] return int (tmp, 2 )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 实例 ```python def ROR(i,index): tmp = bin(i)[2:].rjust(8,"0") for _ in range(index): tmp = tmp[-1] + tmp[:-1] return int(tmp, 2) addr1=0x140001085 addr2=0x140001d00 for i in range(addr2-addr1): PatchByte(addr1+i,ROR(Byte(addr1+i),3)^90) print('successful')
列出 segment 函数(以及它们的交叉引用) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 import ida_kernwinimport ida_segmentimport ida_funcsimport ida_xrefimport ida_autodef main (): ida_auto.auto_wait() ea = ida_kernwin.get_screen_ea() seg = ida_segment.getseg(ea) if not seg: print ("No segment found at 0x%x" % ea) return func = ida_funcs.get_func(seg.start_ea) if not func: func = ida_funcs.get_next_func(seg.start_ea) while func and func.start_ea < seg.end_ea: funcea = func.start_ea print ("Function %s at 0x%x" % (ida_funcs.get_func_name(funcea), funcea)) xb = ida_xref.xrefblk_t() if xb.first_to(funcea, ida_xref.XREF_ALL): while xb.iscode: print (" called from %s(0x%x)" % (ida_funcs.get_func_name(xb.frm), xb.frm)) if not xb.next_to(): break func = ida_funcs.get_next_func(funcea) main()
列出 segment 中的所有函数 (和交叉引用) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import ida_kernwinimport ida_idaapiimport ida_segmentimport ida_funcsimport idautilsdef main (): ea = ida_kernwin.get_screen_ea() if ea == ida_idaapi.BADADDR: print ("Could not get get_screen_ea()" ) return seg = ida_segment.getseg(ea) if seg: for funcea in idautils.Functions(seg.start_ea, seg.end_ea): print ("Function %s at 0x%x" % (ida_funcs.get_func_name(funcea), funcea)) for ref in idautils.CodeRefsTo(funcea, 1 ): print (" called from %s(0x%x)" % (ida_funcs.get_func_name(ref), ref)) else : print ("Please position the cursor within a segment" ) if __name__=="__main__" : main()
获取并打印当前光标所在函数(或代码块)的详细信息 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 import binasciiimport ida_kernwinimport ida_funcsdef dump_flags (fn ): """打印函数的标志信息""" print ("函数标志: %08X" % fn.flags) if fn.is_far(): print (" 远函数" ) if not fn.does_return(): print (" 该函数不会返回" ) if fn.flags & ida_funcs.FUNC_FRAME: print (" 该函数使用帧指针" ) if fn.flags & ida_funcs.FUNC_THUNK: print (" 该函数是 Thunk 函数" ) if fn.flags & ida_funcs.FUNC_LUMINA: print (" 该函数信息由 Lumina 提供" ) if fn.flags & ida_funcs.FUNC_OUTLINE: print (" 该函数是伪代码函数(非真实函数)" ) def dump_regvars (pfn ): """打印函数中的重命名寄存器信息""" assert ida_funcs.is_func_entry(pfn) print ("该函数有 %d 个重命名寄存器" % pfn.regvarqty) for rv in pfn.regvars: print ("%08X..%08X '%s'->'%s'" % (rv.start_ea, rv.end_ea, rv.canon, rv.user)) def dump_regargs (pfn ): """打印寄存器传递的参数信息""" assert ida_funcs.is_func_entry(pfn) print ("该函数有 %d 个寄存器参数" % pfn.regargqty) for ra in pfn.regargs: print (" 寄存器编号=%d, 参数名称=\"%s\", (序列化类型)=\"%s\"" % ( ra.reg, ra.name, binascii.hexlify(ra.type ))) def dump_tails (pfn ): """打印函数的尾块信息""" assert ida_funcs.is_func_entry(pfn) print ("该函数有 %d 个尾块" % pfn.tailqty) for i in range (pfn.tailqty): ft = pfn.tails[i] print (" 尾块 %i: %08X..%08X" % (i, ft.start_ea, ft.end_ea)) def dump_stkpnts (pfn ): """打印函数的栈调整点信息""" print ("该函数有 %d 个栈调整点" % pfn.pntqty) for i in range (pfn.pntqty): pnt = pfn.points[i] print (" 栈调整点 %i @%08X: %d" % (i, pnt.ea, pnt.spd)) def dump_frame (fn ): """打印函数的栈帧信息""" assert ida_funcs.is_func_entry(fn) print ("帧结构 ID: %08X" % fn.frame) print ("局部变量区大小: %8X" % fn.frsize) print ("保存寄存器区大小: %8X" % fn.frregs) print ("返回时清理的字节数: %8X" % fn.argsize) print ("帧指针偏移量: %8X" % fn.fpd) def dump_parents (fn ): """打印函数尾块的所有者和引用信息""" assert ida_funcs.is_func_tail(fn) print ("所属函数: %08X" % fn.owner) print ("该尾块被 %d 个函数引用" % fn.refqty) for i in range (fn.refqty): print (" 引用者 %i: %08X" % (i, fn.referers[i])) def dump_func_info (ea ): """打印指定地址所在的函数信息""" pfn = ida_funcs.get_fchunk(ea) if pfn is None : print ("该地址 %08X 处没有函数!" % ea) return print ("当前代码块范围: %08X..%08X" % (pfn.start_ea, pfn.end_ea)) dump_flags(pfn) if ida_funcs.is_func_entry(pfn): print ("这是一个入口代码块" ) dump_tails(pfn) dump_frame(pfn) dump_regvars(pfn) dump_regargs(pfn) dump_stkpnts(pfn) elif ida_funcs.is_func_tail(pfn): print ("这是一个尾代码块" ) dump_parents(pfn) ea = ida_kernwin.get_screen_ea() dump_func_info(ea)
向现有函数帧添加新成员 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 import ida_funcsimport ida_frameimport ida_typeinfimport ida_rangeimport idcdef add_frame_member (func_ea ): name = "my_stkvar" tif = ida_typeinf.tinfo_t(ida_typeinf.BTF_UINT64) tif.create_ptr(tif) func = ida_funcs.get_func(func_ea) if not func: print ('Failed to get function!' ) return print (f"Function @ {func.start_ea:x} " ) frame_tif = ida_typeinf.tinfo_t() if not ida_frame.get_func_frame(frame_tif, func): print ('Failed to get frame!' ) return print (f"{frame_tif._print ()} " ) rs = ida_range.rangeset_t() sp_offset = 0 if frame_tif.calc_gaps(rs): for range in rs: if range .start_ea <= 0 : continue elif (range .end_ea - range .start_ea) >= tif.get_size(): sp_offset = range .start_ea print (f"Range [{range .start_ea:x} , {range .end_ea:x} [ selected." ) break if sp_offset > 0 : sval = ida_frame.calc_frame_offset(func, sp_offset, None , None ) if ida_frame.add_frame_member(func, name, sval, tif): print (f"Frame member added at frame offset {sval} !" ) else : print ("Failed adding frame member" ) else : print ("Could not find gaps in current frame..." ) add_frame_member(idc.here())
常用API idaapi.MinEA()
:获取载入程序的最小的有效地址。
idaapi.MaxEA()
:获取载入的程序最大的有效地址。
idaapi.Assemble(head, line)
:从head地址开始反汇编,寻找直到遇到line这条指令时停止,返回两个变量,一个表示是否成功,另外一个是最后这条line指令的字节表示,例如:ret
就会是'\xc3'
这一个字节来表示,有些指令可能由多个字节表示。
idaapi.GetMnem(addr)
:获取addr地址处的指令。
idaapi.MakeCode(addr)
:从addr地址开始尝试将数据转换为汇编代码。
idaapi.next_not_tail(addr)
:往下走一个指令,如果不是尾部,则返回下一条指令的起始地址。
idaapi.GetDisasm(addr)
:获取addr地址开始的一条汇编指令。
idaapi.GetFlags(addr)
:获取addr地址处的一系列标志位,可用来判断属于code还是data 。
idaapi.isCode(Flags)
:通过Flags判断是否是汇编代码。
idaapi.MakeUnkn(addr, size)
:取消对addr地址处的size大小的定义,暂不清楚该地址是代码还是数据时可以使用。
idaapi.GetOpnd(addr, index)
:取addr地址处的指令的第index个操作数,从零开始,从左开始,依次为intel汇编语法中的目的操作数、源操作数。
idaapi.get_name_ea(min_ea, name)
:从min_ea地址开始,寻找名为name的有效地址,该name可以为函数名、label名。
idaapi.get_dword(addr)
:从addr地址处获取一个dword数据。
idaapi.MakeDword(addr)
:将addr开始的一个DWORD大小的数据定义为双字形式,举一反三,Q代表四字节数据,API形式一致。
idaapi.Segname(addr)
:得到addr地址所处的区段名。
idaapi.MakeFunction(addr)
:将addr地址处定义为一个函数,相当于快捷键P。
BADADDR: 常量,代表错误的地址。
idaapi.GetSpd(addr)
:获取addr地址处的栈指针SP的值,而在IDA中显示的值则是SP到BP基址针的差值,例如获取到的值为-4,在IDA中显示栈指针的情况时则为4。
idaapi.SetSpDiff(addr, diff)
:设置addr地址处的Sp指针与Bp指针的差值,在平衡堆栈时需要用到。
idaapi.next_head(head, BADADDR)
:遍历下一条指令,除非遇到BADADDR,返回下一条指令的地址。
idaapi.ua_mnem(addr)
:返回addr地址处的指令类型。
idaapi.MakeName(addr, '')
:给addr地址处一个标记label。