idapython常用api记录7.0

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)
未完…

posted @ 2021-06-27 21:40  koflfy  阅读(699)  评论(0编辑  收藏  举报