前言 ida有三个重要库 -IDC 封装IDA与IDC函数的兼容性模块 -Idautils IDA提供的一个高级实用模块 -idaapi 允许访问更加底层的数据
使用idapython脚本的方式:
shift + F2
脚本文件引用(Script file) 本篇环境为ida7.5+python3
基本使用 获取地址 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 print (hex(idc.here())) print (hex(idc.get_screen_ea())) print (hex(ida_ida.inf_get_min_ea())) print (hex(ida_ida.inf_get_max_ea())) print (hex(idc.read_selection_start())) print (hex(idc.read_selection_end())) if idaapi.BADADDR == idc.here(): print ("BadAddress addr invalid" )else : print ("addr is ok" )`
新旧函数对比
老版函数
当前 7.5 支持函数
作用
idc.ScreenEA()
idc.get_screen_ea()
获取当前指令地址
idc.MinEA()
, idc.StartEA()
ida_ida.inf_get_min_ea()
获取当前最小地址。其中老版的三个函数都替换为了新版,使用的是同一个函数
idc.MaxEA()
ida_ida.inf_get_max_ea()
获取当前最大地址
idc.SelStart()
idc.read_selection_start()
获取当前光标选择的块中的起始地址
idc.SelEnd()
idc.read_selection_end()
同上,返回结束地址
数值获取
老版函数
新版函数
Byte(addr)
idc.get_wide_byte(addr)
Word(addr)
idc.get_wide_word(addr)
Dword(addr)
idc.get_wide_dword(addr)
Qword(addr)
idc.get_qword(addr)
老版判断
新版判断
idc.isByte()
ida_bytes.is_byte
idc.isWord()
ida_bytes.is_word
idc.isDwrd()
ida_bytes.is_dword
idc.isQwrd()
ida_bytes.is_qword
eg.
1 2 3 4 5 import idc ea = idc.get_screen_ea() value = idc.get_wide_byte(ea) print("当前指令的硬编码为 {}".format(hex(value)));
数值操作
旧指令
新指令
说明
idc.PatchByte(addr,value)
ida_bytes.patch_byte(addr,value)
修改addr地址的值为value.每次修改一个字节
idc.PatchWord(addr,value)
ida_bytes.patch_word(addr,value)
修改addr地址的值为value.每次修改两个字节
idc.PatchDword(addr,value)
ida_bytes.patch_Dword(addr,value)
修改addr地址的值为value.每次修改四个字节
idc.PatchQword(addr,value)
ida_bytes.patch_Qword(addr,value)
修改addr地址的值为value.每次修改八个字节
eg.
1 2 3 4 5 6 7 ea = idc.get_screen_ea() value = idc.get_wide_byte(ea) print("未修改= {}".format(hex(value))) ida_bytes.patch_byte(ea,0x90) value = idc.get_wide_byte(ea) print("被修改当前值 {} ".format(hex(value)))
实操–地址段间nop eg.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import idc import idaapi import idautils #获取当前选择的起始地址 StartSeclectAddr = idc.read_selection_start() #获取当前选择的终止地址 EndSeclectAddr = idc.read_selection_end() #计算出当前指令长度 SelLen = EndSeclectAddr - StartSeclectAddr; #从选择地址开始 - 选择地址结束进行遍历. 获取其指令字节. 如果是0x66 则替换成0xFF for index in range(SelLen): curaddr = StartSeclectAddr+index tmpValue = idc.get_wide_byte(curaddr) if (tmpValue == 0x66): ida_bytes.patch_byte(curaddr,0x90)
常见机器码 nop: 0x90 jmp: 0xEB 0xE9 0xEA call: 0xE8 + 4字节 jz/jnz: 0x74/0x75
指令
指令
使用以及作用
高版本替代函数
idc.GetDisasm(addr)
获取地址处的汇编语句 如: mov ebp,esp
无替代
idc.GetDisasmEx(addr,flags)
更高级的获取.带有标志. 一般是给一个0
idc.generate_disasm_line(addr,flags)
idc.GetOpnd(addr,index)
获取指定地址位置的操作数.参数1是地址.参数2是操作数索引.如 mov ebp,esp ebp是操作数1 esp是操作数2 mov则是汇编指令不是操作数
idc.print_operand(addr,index)
idc.GetMnem(addr)
操作汇编指令 mov ebp,esp 获取mov
idc.print_insn_mnem(addr)
idaapi.get_imagebase()
获取基地址
idc.GetOpType(ea,index)
获取操作数的类型
idc.get_operand_type(addr,index)
idc.GetOperandValue(addr,index)
获取指定索引操作数中的值: 如 call 0x00402004 对应汇编为: FF 15 04 20 40 00 FF15=Call 而操作数的值则为04 20 40 00 (小端) 使用函数之后获取则为地址 00402004
idc.get_operand_value(addr,index)
idc.NextHead
获取下一行汇编
idc.next_head(ea)
idc.PrevHead
获取上一行汇编
idc.prev_head(ea)
eg.
1 2 3 4 5 6 7 8 9 10 import idc import idaapi import idautils ea = idc.here(); print("当前模块基址为: {}".format(hex(idaapi.get_imagebase()))) print("当前的汇编语句为: {}".format(idc.GetDisasm(ea))) print("当前的汇编指令为: {}".format(idc.print_insn_mnem(ea))) print("当前的操作数为: {}".format(idc.print_operand(ea,0))) print("当前的操作数值为: {}".format(idc.get_operand_value(ea,0)))
段
指令
作用
新函数
idc.SegName(addr)
获取段的名字
idc.get_segm_name(addr)
idc.SegStart(addr)
获取段的开始地址
idc.get_segm_start(addr)
idc.SegEnd(addr)
获取段的结束地址
idc.get_segm_end(addr)
idautil.Segments()
返回一个列表记录所有段的地址
idc.FirstSeg()
获取第一个段
idc.get_first_seg(addr)
idc.NextSeg(addr)
获取下一个段 参数是当前段的地址 返回的是下一个段的地址
idc.get_next_seg(addr)
上述返回值如果是获取地址的函数 获取不到都会返回 0xFFFFFFF 也就是 BADADDR
eg.
1 2 3 4 5 6 7 8 9 import idc import idaapi import idautils for seg in idautils.Segments(): segname = idc.get_segm_name(seg) segstart = idc.get_segm_start(seg) segend = idc.get_segm_end(seg) print("段名 = {} 起始地址= {} 结束地址 = {} ".format(segname,hex(segstart),hex(segend)));
函数
老函数
作用
新函数
Functions(startaddr,endaddr)
获取指定地址之间的所有函数
无
idc.GetFunctionName(addr)
获取指定地址的函数名
idc.get_func_name(addr)
idc.GetFunctionCmt
获取函数的注释
get_func_cmt(ea, repeatable)
1是地址 2是0或1 1是获取重复注释 0是获取常规注释
idc.SetFunctionCmt
设置函数注释
set_func_cmt(ea, cmt, repeatable)
idc.ChooseFunction(title)
弹出框框要求用户进行选择 参数则是信息
idc.choose_func(title)
idc.GetFuncOffset(addr)
返回: addr 距离函数的偏移形式
idc.get_func_off_str(addr)
idc.FindFuncEnd(addr)
寻找函数结尾,如果函数存在则返回结尾地址,否则返回BADADDR
idc.find_func_end(addr)
idc.SetFunctionEnd(addr,newendaddr)
设置函数结尾
ida_funcs.set_func_end
ida_funcs.func_setstart(addr,newstartaddr)
设置函数开头
ida_funcs.set_func_start(addr, newstart)
idc.MakeFunction
创建函数
ida_funcs.add_func(ea)
idc.DelFunction
删除函数
ida_funcs.del_func(ea)
idc.MakeName(addr, name)
设置地址处的名字
idc.set_name(ea, name, SN_CHECK)
Ex函数也使用set_name
idc.PrevFunction
获取首个函数
idc.get_prev_func
idc.NextFunction
获取下一个函数
idc.get_next_func
eg.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import idc import idaapi import idautils for seg in idautils.Segments(): segname = idc.get_segm_name(seg) segstart = idc.get_segm_start(seg) segend = idc.get_segm_end(seg) print("段名 = {} 起始地址= {} 结束地址 = {} ".format(segname,hex(segstart),hex(segend))); if (segname == '.text'): for funcaddr in Functions(segstart,segend): funname = idc.get_func_name(funcaddr) funend = idc.find_func_end(funcaddr) funnext = idc.get_next_func(funcaddr) funnextname = idc.get_func_name(funnext) print("当前函数名 = {} 当前结束地址 = {} 下一个函数地址 = {} 下一个函数名= {} ".format(funname,hex(funend),hex(funnext),funnextname)) ea = idc.get_screen_ea() funnextoffset = idc.get_func_off_str(ea) print("当前选择地址距离当前函数的偏移为: {} ".format(funnextoffset))
参考链接 IDAPython入门教程 基于IDA7.5_Python3 第一讲 简介与地址获取 - iBinary - 博客园 基于python3.x IDAPython第二讲 段 函数 汇编指令等操作 - iBinary - 博客园