IDA脚本编程

基础信息

地址

1
2
3
idc.get_screen_ea() #获取当前光标所在地址
idc.get_inf_attr(INF_MAX_EA) #获取本文件最大地址
idc.get_inf_attr(INF_MIN_EA) #同上最小

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
idc.get_segm_name(ea) #获取地址所在段名
idc.get_segm_start(ea) #获取地址所在段起始地址
idc.get_segm_end(ea) #同上 结束地址
idautils.Segments() #获取所有的段首地址
idc.get_first_seg() #获取第一个段地址
idc.get_next_seg() #获取下一个段地址
idc.get_segm_attr(ea,attr) #获取函数属性
idc.get_segm_attr(ea,attr,value) #同上 设置
ida_segment.get_segm_by_name('.text') #通过名字获取段对象

#遍历所有的段
for i in idautils.Segments():
print(f"%s:\t0x%x\t0x%x" %(
idc.get_segm_name(i),
idc.get_segm_start(i),
idc.get_segm_end(i)))

# 通过名称来获取段对象
sg = ida_segment.get_segm_by_name('.text')
print(sg.start_ea,sg.end_ea,sg.size())

函数

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
idc.get_func_name(ea) #获取地址函数名
idc.get_next_func(ea) #获取当前函数前一个函数地址
idc.get_prev_func(ea) #同上 下一个
idautils.Functions(start,end) #获取所有函数首地址 若没有参数则从头到尾
idc.get_func_attr(func,FUNCATTR_FLAGS) #检索函数信息

#遍历所有函数
for func in idautils.Functions():
print(hex(func), idc.get_func_name(func),sep=':')

#获取函数的起始 / 结束地址
func = idaapi.get_func(ea)
print(func.start_ea, func.end_ea)

#检索关于函数的信息,来判断该函数是否是库中代码,或者函数是否有返回值等等
for func in idautils.Functions():#获取所有已知的函数首地址
flags = idc.get_func_attr(func, FUNCATTR_FLAGS)#获取标志
if flags & FUNC_NORET:
print(hex(func), get_func_name(func), "FUNC_NORET") #无返回值
if flags & FUNC_FAR:
print(hex(func), get_func_name(func),"FUNC_FAR") #是否使用分段内存
if flags & FUNC_LIB:
print(hex(func), get_func_name(func),"FUNC_LIB") #是否为用于寻找库函数的代码,分析一般跳过
if flags & FUNC_STATIC:
print(hex(func), get_func_name(func),"FUNC_STATIC") #静态函数
if flags & FUNC_FRAME:
print(hex(func), get_func_name(func),"FUNC_FRAME") #是否使用ebp
if flags & FUNC_USERFAR:
print(hex(func), get_func_name(func),"FUNC_USERFAR")
if flags & FUNC_HIDDEN:
print(hex(func), get_func_name(func),"FUNC_HIDDEN") #是否默认折叠 需要展开才能查看
if flags & FUNC_THUNK:
print(hex(func), get_func_name(func),"FUNC_THUNK") #是否是thunk函数
if flags & FUNC_LIB:
print(hex(func), get_func_name(func),"FUNC_BOTTOMBP") #ebp是否等于esp

操作数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
idc.get_operand_type(curr_addr,n) #获取第n+1个操作数的类型


# 判断在 curr_addr 的指令的第一个操作数的类型是否为利用寄存器和位移的寻址操作
insn = ida_ua.insn_t()
idaapi.decode_insn(insn, curr_addr)
if insn.Op1.type == idaapi.o_displ:
print("第一个操作数的类型是利用寄存器和位移的寻址操作!")

"""
o_void 没有任何操作数 retn
o_reg 寄存器
o_mem 直接寻址内存 ds:dword_xxxxxx
o_phrase 利用基址寄存器和变址寄存器的寻址操作 [edi_ecx]
o_displ 利用寄存器和位移的寻址操作 [edi+18h]
o_imm 确定的数值 0Ch
o_far 直接访问远端地址
o_near 直接访问近端地址
"""

搜索

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
idc.FindBinary(ea,flag,searchstr,radix=16)#字节或者二进制的搜索

pattern = '55 48 89 E5'
addr = ida_ida.inf_get_min_ea()
for x in range(0,5):
addr = idc.find_binary(addr, SEARCH_DOWN|SEARCH_NEXT, pattern)
if addr != idc.BADADDR:
print(hex(addr), idc.GetDisasm(addr))

"""
flag参数的含义:
SEARCH_UP 向上搜索
SEARCH_DOWN 向下搜索
SEARCH_NEXT 获取下一个已经找到的对象
SEARCH_CASE 是否区分大小写
SEARCH_REGEX 正则搜索
SEARCH_NOBRK
SEARCH_NOSHOW 是否显示搜索进度
SEARCH_UNICODE 将所有搜索字符串视为unicode
SEARCH_IDENT
SEARCH_BRK
"""

寄存器操作

1
2
idc.get_reg_value('rax')
idaapi.set_reg_value('rax',1234)

xmm寄存器

1
2
3
4
def read_xmm_reg(name):
rv=idaapi.regval_t()
idaapi.get_reg_val(name,rv)
return (struct.unpack('Q',rv.bytes())[0])

调试内存操作

1
2
3
4
5
6
7
8
idc.read_dbg_bytes(addr)
idc.read_dbg_memory(addr,size)
idc.read_dbg_dword(addr)
idc.read_dbg_qword(addr)
idc.patch_dbg_byte(addr,val)

idc.run_to(addr) #运行到指定地址停下
idc.wait_for_next_event(wfne,timeout) #等待下一个事件 此函数继续执行进程 等待调试器事件直至超时 timeout=-1无限大 wfne和返回值具体含义自己查

本地内存操作

1
2
3
4
5
6
7
8
idc.get_qword(addr)
idc.patch_qword(addr,val)
idc.patch_dword(addr,val)
idc.patch_word(addr,val)
idc.patch_byte(addr,val)
idc.patch_bytes(addr,val)
idc.get_db_byte(addr)
idc.get_bytes(addr,size)

反汇编操作

1
2
3
4
5
6
7
8
9
10
ea=here()
GetDisasm(ea) #获取反汇编文本 mov eax,[rbp+var_4]
idc.next_head(ea) #获取下一条指令地址
idc.prev_head(ea) #同上 上一条

if idaapi.BADADDR!=ea:
print("valid success") #判断当前地址在程序中是否存在

idc.print_insn_mnem(ea) #返回助记符 mov
idc.print_operand(ea,n) #返回第n+1参数 eax

交叉引用分析

1
2
for ref in idautils.XrefsTo(ea):
print(hex(ref.frm))

OLLVM批量断点设置

1
2
3
4
5
6
7
8
fn=0x401f60 #main函数
ollvm_tail=0x405d4b #汇聚点
f_blocks=idaapi.FlowChart(idaapi.get_func(fn),flags=idaapi.FC_PREDS)
for block in f_blocks:
for succ in block.succs(): #后继
if succ.start_ea==ollvm_tail:
print(hex(block.start_ea))
idc.add_bpt(block.start_ea)

杂项

1
2
3
4
5
idc.add_bpt(0x409437) #添加断点
idaapi.get_imagebase() #获取基地址
idc.create_insn(addr) #c,make code
ida_funcs.add_func(addr) #p, create function
ida_bytes.create_strlit(addr) #a, 创建字符串

函数遍历

1
2
for func in idautils.Functions():
print("0x%x,%s"%(func,idc.get_func_name(func)))

基本块

基本块遍历

1
2
3
4
fn=... #目标函数地址
f_blocks=idaapi.FlowChart(idaapi.get_func(fn),flags=idaapi.FC_PREDS)
for block in f_blocks:
print(hex(block.start_ea))

基本块前驱

1
2
for pred in block.preds():
print(hex(pred.start_ea))

基本块后继

1
2
for succ in block.succs():
print(hex(succ.start_ea))

指令遍历

1
2
for ins in idautils.FuncItems(0x401000):
print(hex(ins))

条件断点

编写断点函数脚本,IDA底部导入该函数。

1
2
3
def bp():
rax=idc.get_reg_value('rax')
return rax==16949 #当为true时停下,false不命中

设置普通断点,在call rand后设置。右键‘Edit Break…’中点击‘Condition’中‘…’。输入‘bp()’,并选择语言为Python。