当前位置: 首页 > 工具软件 > IDAPython > 使用案例 >

idapython常用api记录7.0

宁弘亮
2023-12-01

2019-02-13 idapython常用api记录

以下代码片段可以在ida的output窗口中测试用,需要引入相关的模块即可。

import idaapi
import idc
import idautils

后续需要使用的程序代码指令


0017C24                 CODE32
LOAD:00017C24
LOAD:00017C24 ; =============== S U B R O U T I N E =======================================
LOAD:00017C24
LOAD:00017C24 ; Attributes: thunk
LOAD:00017C24
LOAD:00017C24 ; int fstat(int fd, struct stat *buf)
LOAD:00017C24 fstat                                   ; CODE XREF: sub_136A44+64↓p
LOAD:00017C24                 ADRL            R12, 0x1C1C2C
LOAD:00017C2C                 LDR             PC, [R12,#(off_1C2B20 - 0x1C1C2C)]! ; __imp_fstat
LOAD:00017C2C ; End of function fstat
LOAD:00017C2C
....

0017C30
LOAD:00017C30 ; =============== S U B R O U T I N E =======================================
LOAD:00017C30
LOAD:00017C30
LOAD:00017C30                 EXPORT start
LOAD:00017C30 start                                   ; DATA XREF: LOAD:00000018↑o
LOAD:00017C30                                         ; LOAD:stru_66C↑o
LOAD:00017C30                 LDR             R0, =(unk_1C3000 - 0x17C3C)
LOAD:00017C34                 ADD             R0, PC, R0 ; unk_1C3000
LOAD:00017C38                 B               __cxa_finalize
LOAD:00017C38 ; End of function start
LOAD:00017C38
LOAD:00017C38 ; ---------------------------------------------------------------------------
LOAD:00017C3C off_17C3C       DCD unk_1C3000 - 0x17C3C
LOAD:00017C3C                                         ; DATA XREF: start↑r


0017C3C                                         ; DATA XREF: start↑r
LOAD:00017C40 ; ---------------------------------------------------------------------------
LOAD:00017C40
LOAD:00017C40 loc_17C40                               ; DATA XREF: LOAD:00017C5C↓o
LOAD:00017C40                                         ; LOAD:off_17C68↓o
LOAD:00017C40                 CMP             R0, #0
LOAD:00017C44                 BXEQ            LR
LOAD:00017C48                 BX              R0
LOAD:00017C4C ; ---------------------------------------------------------------------------
LOAD:00017C4C                 MOV             R1, R0
LOAD:00017C50                 LDR             R2, =(unk_1C3000 - 0x17C60)
LOAD:00017C54                 LDR             R0, =(loc_17C40 - 0x17C64)
LOAD:00017C58                 ADD             R2, PC, R2 ; unk_1C3000
LOAD:00017C5C                 ADD             R0, PC, R0 ; loc_17C40
LOAD:00017C60                 B               __cxa_atexit
LOAD:00017C60 ; ---------------------------------------------------------------------------
LOAD:00017C64 off_17C64       DCD unk_1C3000 - 0x17C60
LOAD:00017C64                                         ; DATA XREF: LOAD:00017C50↑r
LOAD:00017C68 off_17C68       DCD loc_17C40 - 0x17C64 ; DATA XREF: LOAD:00017C54↑r
LOAD:00017C6C                 CODE16
LOAD:00017C6C
LOAD:00017C6C ; =============== S U B R O U T I N E =======================================
LOAD:00017C6C
LOAD:00017C6C ; Attributes: bp-based frame
LOAD:00017C6C
LOAD:00017C6C sub_17C6C                               ; CODE XREF: sub_1BC30+8↓p
LOAD:00017C6C                                         ; LOAD:000303A6↓p ...
LOAD:00017C6C                 PUSH            {R7,LR}
LOAD:00017C6E                 ADD             R7, SP, #0
LOAD:00017C70                 BL              sub_17580
LOAD:00017C74                 POP             {R7,PC}
LOAD:00017C74 ; End of function sub_17C6C
LOAD:00017C74

获取当前光标位置地址

ea=here()
ea=ScreenEA()

遍历当前模块的segment

import idautils
import idc 

def loop_through_segments():
    '''
    遍历出所有的segment
    '''
    # ea=ScreenEA() ##获取当前光标指向的地址
    # print(hex(ea))
    for seg in idautils.Segments():
        print('segName:%s  0x%x-0x%x ' %(idc.SegName(seg),idc.SegStart(seg),idc.SegEnd(seg)))

segmentName:idc.SegName(seg) ##段名称
segmentStart:idc.SegStart(seg) ##段起始位置
segmentEnd:idc.SegEnd(seg) ## 段结束位置

获取段信息的其他api

idc.NextSeg(ea) ##获取当前段的下一个段
idc.SegByName(segname) ##根据名称获取段的地址,实际在测试的时候,调用这个函数只能返回的是segment的个数segment

输出:
Python>idc.SegByName('LOAD') 
2 ##表示有两个LOAD段

获取当前模块的导出所有函数

import idautils
import idc

def loop_module_funcs():
	for func in idautils.Functions(): ##获取所有的导出函数
		'''
		idautils.Functions列举出已知的函数,返回一个list对象,每个item是一个函数的首地址,
		通过idc.GetFunctionName(funcAddr)获取函数名称
		'''
		print(hex(func),idc.GetFunctionName(func))
		
		
idautils.Functions():返回所有的导出函数的函数地址,可以通过for来循环迭代每个函数

idautils.GetFuncntionName(ea):根据当前的函数地址,返回函数名称。
		

获取函数的起始和结束地址

有时候我们在读取一个函数的指令的时候,需要知道函数的起始地址和结束地址,使用idaapi.get_func(faddr)

def get_func_boundaries(ea):
	func=idaapi.get_func(ea)
	start = func.start_ea
	end	  =func.end_ea
	print('start: 0x%x end:0x%x' %(start,end))
	
def loop_module_funcs():
	for func in idautils.Functions():
		'''
		idautils.Functions列举出已知的函数,返回一个list对象,每个item是一个函数的首地址,
		通过idc.GetFunctionName(funcAddr)获取函数名称
		'''
		print(hex(func),idc.GetFunctionName(func))
		get_func_boundaries(func)
loop_module_funcs() 


idaapi.get_func(ea):返回一个func_t对象,其中包含了函数的起始地址和结束地址


使用dir函数查看func_t对象,如下
Python>func = idaapi.get_func(0x17C30)
Python>type(func)
<class 'ida_funcs.func_t'>
Python>dir(func)
['__class__', '__del__', '__delattr__', 
'__dict__', '__doc__', '__eq__', '__format__',
 '__getattribute__', '__gt__', '__hash__', '__init__',
  '__lt__', '__module__', '__ne__', '__new__', '__reduce__', 
  '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', 
  '__str__', '__subclasshook__', '__swig_destroy__', 
  '__weakref__', '_print', 'analyzed_sp', 'argsize', 
  'clear', 'color', 'compare', 'contains', 'does_return', 
  'empty', 'endEA', 'end_ea', 'extend', 'flags', 'fpd', 
  'frame', 'frregs', 'frsize', 'intersect', 'is_far', 
  'llabelqty', 'llabels', 'overlaps', 'owner', 'pntqty',
   'points', 'referers', 'refqty', 'regargqty', 'regargs',
    'regvarqty', 'regvars', 'size', 'startEA', 'start_ea', 
    'tailqty', 'tails', 'this', 'thisown']


其中的startEA,start_ea 和endEA ,end_ea表示函数的边界值

在ida中执行上述脚本,输出如下:

('0x173c4L', 'strerror')
start: 0x173c4 end:0x173d0
('0x173d0L', 'memcpy')
start: 0x173d0 end:0x173dc
('0x173dcL', 'sub_173DC')
start: 0x173dc end:0x173de
	

列出指定地址范围内的所有函数

有时候可能模块的很多区域的函数我们不需要,只需要指定一定范围内的并列举出该范围内的函数,如果全部列出可能会花费很大时间见,可以使用idautils.Functions(start_addr,end_addr)来读取



def loop_module_funcs(start_addr,end_addr):
	for func in idautils.Functions(start_addr,end_addr):
		print(hex(func),idc.GetFunctionName(func))
		get_func_boundaries(func)

loop_module_funcs(0x17514L,0x17560L)

idc.GetFunctionName(func):获取函数地址处的函数名称

上述脚本表述只列出了0x17514L~0x17560L之间的函数,如下

('0x17514L', '__aeabi_memset4')
start: 0x17514 end:0x17520
('0x17520L', 'j_clock_gettime')
start: 0x17520 end:0x17524
('0x17524L', 'clock_gettime')
start: 0x17524 end:0x17530
('0x17530L', 'sub_17530')
start: 0x17530 end:0x17532
('0x17534L', 'readlink')
start: 0x17534 end:0x17540
('0x17540L', 'j_sscanf')
start: 0x17540 end:0x17544
('0x17544L', 'sscanf')
start: 0x17544 end:0x17550
('0x17550L', 'sub_17550')
start: 0x17550 end:0x17552
('0x17554L', 'free')
start: 0x17554 end:0x17560

其他常用获取模块函数的api

可以对应查看开始处的代码片段

##获取下一个函数
idc.NextFunction(addr)

nfc=idc.NextFunction(0x17C30)
Python>idc.GetFunctionName(nfc)
sub_17C6C

---------------------
获取上一个函数
idc.PrevFunction(addr)

Python>pre = idc.PrevFunction(0x17C30)
Python>idc.GetFunctionName(pre)
fstat

获取函数的指令

  • 读取函数的起始地址和结束地址
  • 读取函数内的指令,包括 汇编指令 操作数

#coding:utf8

import idaapi
import idc
import idautils


def get_func_boundaries(ea):
	func=idaapi.get_func(ea)
	start = func.start_ea
	end	  =func.end_ea
	# print('start: 0x%x end:0x%x' %(start,end))
	return start,end

def get_disassm(addr):

	'''
	这里读取start函数内的指令即可
	'''
	start,end = get_func_boundaries(addr)
	while True: ##
		print('start: 0x%x' %start)
		mnemonic=idc.GetMnem(start)##获取指令的助记符
		op1=idc.GetOpnd(start,0) ###获取指令的操作数1,
		op2 = idc.GetOpnd(start,1) ### 获取指令的操作数2
		# print('mnemonic:%s  op1:%s op2:%s ' %(mnemonic,op1,op2))
		##完整的一条指令 
		print('disasm:')
		disasm = idc.GetDisasm(start) ###获取程序指令
		print(disasm)
		start +=4 ##每次地址递增4个字节
		if start>=end:
			break
		

get_disassm(0x17C30)

输出:

start: 0x17c30
disasm:
LDR             R0, =(unk_1C3000 - 0x17C3C)
start: 0x17c34
disasm:
ADD             R0, PC, R0; unk_1C3000
start: 0x17c38
disasm:
B               __cxa_finalize

以上就将start出的函数所有指令都提取出来

读取函数指令2

在上面的程序中,利用现有的知识点来读取指定函数的指令,当然,ida还提供来另一种方式来读取指定函数地址出的指令,如下

def get_disasm2(addr):
	'''
	读取指令的另一种方式
	'''
	start = idc.GetFunctionAttr(addr,FUNCATTR_START) ## 通过idc.GetFunctionAttr来获取函数的起始地址,起中第二个参数使用FUNCATTR_START表示起始
	end=idc.GetFunctionAttr(addr,FUNCATTR_END) ## 通过idc.GetFunctionAttr来获取函数的起始地址,起中第二个参数使用FUNCATTR_END表示结束
	cur_start=start
	print('start22: 0x%x end:0x%x' %(start,end))
	while cur_start<=end: ###结束条件为当前指令地址大于函数的结束地址
		print('addr2:0x%x'%cur_start)
		disasm = idc.GetDisasm(cur_start)
		print(disasm)
		cur_start=idc.NextHead(cur_start,end) ###读取下一个指令的地址,类似于我们的start+=4
		
运行后,输出结果为:

start22: 0x17c30 end:0x17c3c
addr2:0x17c30
LDR             R0, =(unk_1C3000 - 0x17C3C)
addr2:0x17c34
ADD             R0, PC, R0; unk_1C3000
addr2:0x17c38
B               __cxa_finalize		

读取指令3

使用idautils.FuncItems(addr)来获取函数的所有指令地址,这里比较好的是不用去判断某个指令地址大于函数的结束地址,如下指令


def get_disasm33(addr):

	'''
	使用idc.FuncItems(addr)函数类读取一个函数的指令和边界值,idc.FuncItems(addr)返回一个可迭代对象
	将这个对象转为了list即可。这个list内部是一个顺序的地址值,从函数的起始地址到结束地址开始
	'''
	pass
	disasm_addrs = list(idautils.FuncItems(addr))
	for d  in disasm_addrs:
		print(idc.GetDisasm(d))
		
输出:
LDR             R0, =(unk_1C3000 - 0x17C3C)
ADD             R0, PC, R0; unk_1C3000
B               __cxa_finalize
		

读取指令需要注意的问题

  • 有些地址不存在,需要处理,使用idaapi.BADADDR来做对比
Python>if n != idaapi.BADADDR:print ('valid addr ')
valid addr 
  • 上述两个遍历指令的方式缺点为:
    1、只能读取包括在函数边界边内的,如果存在跳转指令,则有可能导致结束地址小于跳转指令,那么将会导致不能全部读取指令值。
    2、当出现跳转地址大于函数的边界值时,这种在代码混淆中常常存在。

判断指令类型

通常在读取指令的时候,可能需要判断一下指令的操作数类型,是否是寄存器还是常数函数指向内存的指针等

  • idc.GetOpType(addr,index) 表示要获取的操作数类型,index表示第一个还是第二个
  • 常用的类型包括了

o_void     = ida_ua.o_void      # No Operand                           ----------
o_reg      = ida_ua.o_reg       # General Register (al,ax,es,ds...)    reg
o_mem      = ida_ua.o_mem       # Direct Memory Reference  (DATA)      addr
o_phrase   = ida_ua.o_phrase    # Memory Ref [Base Reg + Index Reg]    phrase
o_displ    = ida_ua.o_displ     # Memory Reg [Base Reg + Index Reg + Displacement] phrase+addr
o_imm      = ida_ua.o_imm       # Immediate Value                      value
o_far      = ida_ua.o_far       # Immediate Far Address  (CODE)        addr
o_near     = ida_ua.o_near      # Immediate Near Address (CODE)        addr
o_idpspec0 = ida_ua.o_idpspec0  # Processor specific type
o_idpspec1 = ida_ua.o_idpspec1  # Processor specific type
o_idpspec2 = ida_ua.o_idpspec2  # Processor specific type
o_idpspec3 = ida_ua.o_idpspec3  # Processor specific type
o_idpspec4 = ida_ua.o_idpspec4  # Processor specific type
o_idpspec5 = ida_ua.o_idpspec5  # Processor specific type
                                # There can be more processor specific types

# x86
o_trreg  =       ida_ua.o_idpspec0      # trace register
o_dbreg  =       ida_ua.o_idpspec1      # debug register
o_crreg  =       ida_ua.o_idpspec2      # control register
o_fpreg  =       ida_ua.o_idpspec3      # floating point register
o_mmxreg  =      ida_ua.o_idpspec4      # mmx register
o_xmmreg  =      ida_ua.o_idpspec5      # xmm register

# arm
o_reglist  =     ida_ua.o_idpspec1      # Register list (for LDM/STM)
o_creglist  =    ida_ua.o_idpspec2      # Coprocessor register list (for CDP)
o_creg  =        ida_ua.o_idpspec3      # Coprocessor register (for LDC/STC)
o_fpreglist  =   ida_ua.o_idpspec4      # Floating point register list
o_text  =        ida_ua.o_idpspec5      # Arbitrary text stored in the operand
o_cond  =        (ida_ua.o_idpspec5+1)  # ARM condition as an operand

# ppc
o_spr  =         ida_ua.o_idpspec0      # Special purpose register
o_twofpr  =      ida_ua.o_idpspec1      # Two FPRs
o_shmbme  =      ida_ua.o_idpspec2      # SH & MB & ME
o_crf  =         ida_ua.o_idpspec3      # crfield      x.reg
o_crb  =         ida_ua.o_idpspec4      # crbit        x.reg
o_dcr  =    

如何获取当前指令的上一个指令和下一个指令

有时候我们只有同一个地址值并且能读取到当前地址的指令,为了能获取到当前指令的上一个或者下一个指令,通过idc,PrevHead(addr)和idc.NextHead(addr)来
获取。如下

我们引用的是0x17C34处的地址,实际得到的是指令为

ADD             R0, PC, R0 
上一条指令为:
Python>pre = idc.PrevHead(0x17C34 )
Python>idc.GetDisasm(pre)
LDR             R0, =(unk_1C3000 - 0x17C3C)
------------------------------------------
读取下一条指令为:
Python>nt = idc.NextHead(0x17C34)
Python>idc.GetDisasm(nt)
B               __cxa_finalize


总结

  • 在ida的高版本中,6.95 -7.0以后,有一些api发生了变化。我们使用inspect查看函数

如下

Python>import inspect
Python>inspect.getsource(SegByName)
def SegByName(segname): return selector_by_name(segname) 


读取光标所在的位置:ScreenEA(),here()

读取段:

  • 读取段名称:idc.SegName(addr)
  • 遍历段:idautils.Segments()返回一个可迭代对象,通过这个对象来获取模块的segment,
  • 获取段首地址:idc.SegStart(seg)
  • 获取段结束地址:idc.SegEnd(seg)
  • 读取下一个段:idc.NextSeg(seg)
  • 根据段名称获取段个数:idc.SegByName(segname)

读取函数:

  • 获取所有导出的函数:idc.Functions()返回一个可迭代对象,利用for来迭代,每次返回一个函数地址func_addr

  • 获取指定返回的导出函数:idc.Functions(start,end) 会将start和end之间的函数迭代出来

  • 获取函数名称:idc.GetFunctionName(func_addr)

  • 获取函数的首地址和结束地址:

  • func=idaapi.get_func(func_addr)

  • 这里的func是一个func_t对象,

  • 可以通过访问start_ea和end_ea来获取到函数的起始地址和结束地址
    start=func.start_ea
    end=func.end_ea

  • 获取下一个函数:idc.NextFunction(addr)

  • 获取上一个函数:idc.PrevFunction(addr)

  • 读取函数的起始地址:idc.GetFunctionAttr(addr,FUNCATTR_START)

  • 读取函数的结束地址:idc.GetFunctionAttr(addr,FUNCATTR_END)

读取指令:

  • 读取助记符:idc.GetMnem(addr)
  • 读取操作数1:idc.GetOpnd(addr,0)
  • 读取操作数2:idc.GetOpnd(addr,1)
  • 读取操作数1的类型:idc.GetOpType(addr,0)
  • 读取操作数2的类型:idc.GetOpType(addr,1)
  • 读取汇编指令:idc.GetDisasm(addr)
  • 读取函数的所有指令地址:idautils.FuncItems(addr) 可以通过将得到的是强转为一个list对象,也可以直接迭代,推荐使用此方法来读取指令
  • 读取当前指令的上一条指令地址:idc.PrevHead(addr)
  • 读取当前指令的下一条指令地址:idc.NextHead(addr)

未完…

 类似资料: