IDAPython脚本(7.5以上)

IDAPython是很强大的功能. 但是在7.5支持python3之后很多函数都改变了. 所以从头开始学一下.

IDAPython官方函数文档: IDAPython官方文档函数查询

IDC函数官方文档查询: IDC函数

IDA版本与版本之间的差异化函数查询: IDA版本函数差异化

指令相关

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
from idaapi import *
from ida_dbg import *
from ida_bytes import *
# 返回目标地址指向的指令的前一条指令的地址
# 参数一是查找的开始地址,参数二是往前查找的最小地址(在范围内)
prev_1 = prev_head(0x00007FF6A1AA2577, 1)
prev_2 = prev_head(0x00007FF6A1AA257a, 0x00007FF6A1AA2578) # fail
print(hex(prev_1), hex(prev_2))
>> 0x7ff6a1aa2573 0xffffffffffffffff

# 返回目标地址指向的指令的后一条指令的地址
# 参数一是查找开始地址,参数二是往后查找的最大地址(不在范围内)
next_1 = next_head(0x00007FF6A1AA257a, 1) # fail
next_2 = next_head(0x00007FF6A1AA257a, 0x00007FF6A1AA2580)
>> 0xffffffffffffffff 0x7ff6a1aa257b

# 输出目标地址的反汇编语句(就是有一点点乱码x)
a = generate_disasm_line(0x00007FF6A1AA2585)
print(a)
>> b'\x01\x05movzx\x02\x05 \x01)\x01!eax\x02!\x02)\x01\t,\x02\t \x01*\x01 byte ptr\x02 \x01\t[\x02\t\x01!r10\x02!\x01\t]\x02\t\x02*'

# 获取目标地址指令
a = print_insn_mnem(0x00007FF6A1AA2585)
print(a)
>> movzx

# 获取目标地址的某个操作数(按索引取)
a = print_operand(0x00007FF6A1AA2585, 0)
print(a)
>> b'\x01)\x01!eax\x02!\x02)'

# 获取目标地址某个操作数的值(按索引取)
print(hex(get_operand_value(0x00007FF6A1AA2577, 1)))
print(type(get_operand_value(0x00007FF6A1AA2577, 1)))
>> 0x10
>> <class 'int'>

# 获取目标地址字符串,参数二是长度,参数三是string的类型
a = get_strlit_contents(0x00007FF6A1AC28A8, 4, 0)
b = get_strlit_contents(0x00007FF6A1AC28A8, 4, 1)
print(a)
print(b)
>> b'Erro'
>> b'\xe7\x89\x85\xe6\xbd\xb2'

# 在目标地址添加注释,参数三为True则不会替换comment
a = set_cmt(0x00007FF6A1AA2577, "this is a comment", True)
print(a)
>> True

# 给目标地址的变量改名
a = set_name(0x00007FF6A1AC28A8, "err")
print(a)
>> True

# 获取当前光标处地址
print(hex(get_screen_ea()))
>> 0x7ff6a1ac28a8

# 统计目标地址所在函数有多少个基本块
print(FlowChart(get_func(0x00007FF6A1AA21B0)).size)
>> 149

功能+调试相关:

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
# 设置断点
add_bpt(0x00007FF6A1AA2577)

# 删除断点
del_bpt(0x00007FF6A1AA2577)

# 设置断点是否开启
enable_bpt(0x00007FF6A1AA2577, False)

# 设置idapython为默认语言,在设置条件断点时不设置会默认使用idc
load_and_run_plugin("idapython", 3)

def condition():
print(">>> rip:", get_reg_val("rip"))
return True
add_bpt(0x00007FF6011D2581)
# 设置条件断点,每次运行到这个断点停下会触发condition
set_bpt_cond(0x00007FF6011D2581, "condition()")

# 查看交叉引用,返回一个迭代器,包含idautils._xref对象
a = XrefsTo(0x00007FF6011D2577, flags=0)
for addr in a:
print(hex(addr.frm)) # .frm 返回交叉引用地址

# 读/写 1/2/4/8字节, wide是宽字节
get_byte(addr)
get_word(addr)
get_dword(addr)
get_qword(addr)
get_wide_byte(addr)
get_wide_word(addr)
get_wide_dword(addr)
get_wide_qword(addr)
patch_byte(addr, val)
patch_word(addr, val)
patch_dword(addr, val)
patch_qword(addr, val)

# get_byte 是老 32-bit 接口,
# get_wide_byte 是新版、兼容 x64 的统一接口。

# 获取寄存器rip的值
get_reg_val("rip")

# 设置寄存器rax的值为0x10
set_reg_val("rax", 0x10)

# 开始调试
start_process()
# continue
continue_process()
# 运行到目标地址
run_to(addr)
# 获取并清除调试器事件代码,普通代码返回0x20,断点、ret返回0x10,程序结束返回负数
# 必须在进程执行的代码后面调用该函数,以便检索调试器的事件代码,否则可能会阻止后续尝试
wait_for_next_event(EVENT_TYPE, flags)
# 单步
step_over()
wait_for_next_event(WFNE_SUSP, -1) # 每次step_over都要调用一次,continue这些也是
# 事件 WFNE_SUSP 将等待导致被调试进程挂起的事件,比如异常或断点
# 事件 WFNE_CONT 可以恢复被挂起的进程,继续执行
# example:
wait_for_next_event(WFNE_SUSP, -1)
wait_for_next_event(WFNE_ANY | WFNE_CONT, -1)

常用的脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import idaapi
import idc

def dump_bytes_before_address(address, output_file='output.zip'):
# 获取程序的起始地址
start_addr = 0x538

# 打开输出文件
with open(output_file, 'wb') as f:
# 读取指定地址之前的字节并写入文件
for addr in range(start_addr, address):
byte = idaapi.get_byte(addr) # 使用get_byte方法读取字节
f.write(byte.to_bytes(1, byteorder='little')) # 将字节写入文件

print(f"Dumped bytes before address {hex(address)} to {output_file}")


# 示例:把地址0x1597C3之前的字节写入output.zip
address_to_dump = 0x1597C3
dump_bytes_before_address(address_to_dump)

1
2
3
4
5
6
7
import idc

start = 0x140093020 # 起始地址
count = 16 # 要定义多少个 dd

for i in range(count):
idc.create_data(start + i * 4, idc.FF_DWORD, 4, idc.BADADDR)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import ida_bytes

start = 0x140093020 # 起始地址
count = 16 # 要读取多少个 dword(4 字节)

output = []

for i in range(count):
ea = start + i * 4
val = ida_bytes.get_dword(ea)
output.append(val)

# 打印为 Python 列表形式
print(output)

IDAPython核心有如下3个python模块:

  1. idc模块负责提供IDC中所有的函数功能。
  2. idautils提供大量的实用函数,其中许多函数可生成各种数据库相关对象(如函数或交叉引用)的python列表。
  3. idaapi 允许使用者通过类的形式,访问更多底层的数据 。
  4. ida_bytes 专门负责读写二进制字节/数据的模块
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import idc
def clear(start_ea,end_ea):
s_o_h=[0x74,0x05,0x75,0x03,0xe8,0x11,0x00]
while start_ea<end_ea:
if idc.get_bytes(start_ea,7)==bytes(s_o_h):
for i in range(7):
idc.patch_byte(start_ea+i,0x90)
start_ea+=1


start_ea=0x00411DC0
end_ea=0x00411E1E
clear(start_ea,end_ea)
print("ok")