IDAPython

IDA Python

IDAPython的API基本使用

IDAPython教程

指令相关

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
#next_addr(addr),prev_addr(addr)//获取当前地址的后一个地址,获取当前地址的前一个地址
print(hex(prev_addr(0x4011E9)),end='\n')
print(hex(next_addr(0x4011E9)),end='\n')
#0x4011e8
#0x4011ea

#next_head()获取下一条指令的地址

#generate_disasm_line(addr,flags=0)得到addr的反汇编语句
print(generate_disasm_line(0x4011e8,flags=0),end="\n")
#sub esp, 40h

#print_insn_mnem(addr)获取addr地址处的指令
#print_operand(addr,num)获取addr地址处的第num+1个操作数
print(print_insn_mnem(0x0401227),end="\n")
print(print_operand(0x0401227,0),end="\n")
print(print_operand(0x0401227,1),end="\n")
#xor
#byte ptr [eax]
#54h

#get_operand_value(addr,num)获取addr处第num+1个操作数的值
print(get_operand_value(0x0401227,1),end="\n")
#84

#get_strlit_contents(addr)获取addr处的字符串
print(get_strlit_contents(0x403140),end='\n')
#b'Please input your flag:'

set_cmt(addr, comm, rptble) 对addr处添加批注comm; rptble=True时若存在批注则不会更改,为False时会替换批注
print(set_cmt(0x40156F, "This is comm", False))
# True
# 0x40156F call puts ; This is comm

set_name(addr, name, flags=SN_CHECK) 对addr处变量改名
print(set_name(0x404000, "MyBuffer", SN_CHECK))

功能相关

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#add_bpt(addr,0,BPT_SOFT)在addr处设置断点
#del_bpt(addr)删除addr位置处的断点

add_bpt(0x403140,0,BPT_SOFT)
del_bpt(0x403140)

#enable(addr,True/False)设置addr处的断点是否开启
enable_bpt(0x403140,True)

#get_wide_byte(addr)获得addr处一字节的值
#get_wide_word(addr)获得addr处两字节的值
#get_wide_dword(addr)获得addr处四字节的值
#get_qword(addr)获得addr处八字节的值
print(hex(get_qword(0x403140)))
#0x6920657361656c50

#patch_byte(addr,val)将addr处patch一个字节为val
#patch_word(addr,val)将addr处patch两个字节为val
#patch_dword(addr,val)将addr处patch四个字节为val
#patch_qword(addr,val)将addr处patch八个字节为val
patch_byte(0x0403140,ord('U'))

还可以使用idaapi中的get_bytes

1
2
import idaapi
data = idaapi.get_bytes(addr, 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
#get_reg_value(string Register)通过寄存器名称获得寄存器的值
print(hex(get_reg_value("EIP")))

#set_reg_value(long Value,String Register)通过寄存器名称修改寄存器的值
set_reg_value(get_reg_value("EIP")+16,"EIP")


start_process("","","")#开启调试,中间的参数好像是命令行之类的
continue_process()#继续调试

run_to(addr)#运行到指定地址

wait_for_next_event(EVENT_TYPE,flags)
#获取并清除调试器事件代码,普通代码返回0x20,断点、ret指令返回0x10,程序结束返回负数
#必须在导致进程执行的每个函数之后调用,以便检索调试器的事件代码,否则可能会阻止后续尝试单步执行或运行进程

step_over()#单步步过,我们需要在调用step_over之后调用一次wait_for_next_event函数

step_over()
wait_for_next_event(WFNE_SUSP, -1) #是否获取返回值都可以清除调试器事件代码,从而继续执行调试
step_over()
wait_for_next_event(WFNE_SUSP, -1)
step_over()
wait_for_next_event(WFNE_SUSP, -1)

#事件类型“WFNE_SUSP”将等待导致被调试进程挂起的事件,例如异常或断点
#事件类型“WFNE_CONT”可以恢复被挂起的进程,继续执行

下面是个简单的案例,只要程序寄存器中的值为0x7FF7C6320967就停止执行

1
2
3
4
5
6
7
start_process("","","")

while(1):
step_over()
wait_for_next_event(WFNE_SUSP,-1)
if get_reg_value("RIP") == 0x7FF7C6320967:
break

SUSCTF-tttree

idapython脚本去花

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
#首先先去除连续的push和pop
start=0x140001000
end=0x014001C694
addr_list=[0]*11
now_addr=start

addr_target=['push rax','push rax','pushfq','call $+5','pop rax','add rax,','mov ','popfq','pop rax','retn']
#这是第一段patch,将连续的push和pop全部nop掉
while(now_addr<=end):
addr_list[0]=now_addr#获取当前指令的地址
addr_list[1]=next_head(now_addr)#获取下一条指令的地址
if print_insn_mnem(addr_list[0])=='push' and print_insn_mnem(addr_list[1])=='pop' and print_operand(addr_list[0],0)==print_operand(addr_list[1],0):
patch_byte(addr_list[0],0x90)
patch_byte(addr_list[1],0x90)
now_addr+=2
continue
now_addr+=1

def check():
cnt=0
for i in range(10):
if i==5 or i==6:
cnt+=generate_disasm_line(addr_list[i],flags=0).find(addr_target[i])!=-1#没找到就返回-1
#print(cnt)
else:
cnt+=addr_target[i]==generate_disasm_line(addr_list[i],flags=0)
return cnt
now_addr=start
while(now_addr<=end):
#先把后面的几条指令的地址存储起来,方便待会进行比对
#对于已经nop的可以跳过
if generate_disasm_line=="nop":
now_addr+=1
continue
addr_list[0]=now_addr
for i in range(1,10):
addr_list[i]=next_head(addr_list[i-1])
if check()==10:
#print(hex(addr_list[0]))找到对应的地址,可以开始进行patch了
patch_byte(addr_list[0],0xE9)
data=get_operand_value(addr_list[5],1)+addr_list[4]#获取add第二个参数的值
data-=(addr_list[0]+5)#使其跳转的是绝对地址,jmp+地址的计算是jmp+当前jmp指令到要跳转位置的相对距离,所以需要减去基地址获取相对距离,并且jmp指令长度为5
patch_dword(addr_list[0]+1,data&0xFFFFFFFF)
for i in range(addr_list[0]+5,addr_list[9]+1):
patch_byte(i,0x90)
now_addr=addr_list[9]

elif check()==9:#说明最后一个不是ret,那么对应的就是push指令
patch_byte(addr_list[0],0x68)
data=get_operand_value(addr_list[5],1)+addr_list[4]#获取add第二个参数的值
patch_dword(addr_list[0]+1,data&0xFFFFFFFF)
for i in range(addr_list[0]+5,addr_list[8]+1):
patch_byte(i,0x90)
now_addr=addr_list[9]
else:
#print("FAILED",end='\n')
now_addr+=1