抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

t听学长说IDA python很有用,今天就来抽空学习一下。

环境

例子下载:

[](https://buuoj.cn/challenges#[网鼎杯 2020 青龙组]jocker)

工具 版本
python 3.11.3
ida pro 7.7

IDA python介绍

IDA在7.5版本更新后支持python3,原来的很多函数都有了改变,接下来就来介绍一下改变前后的函数以及它的作用。

获取地址

函数作用 函数
获取光标当前地址 idc.here() 或 idc.get_screen_ea()
获取最小地址(可以使用的) ida_ida.inf_get_min_ea()
获取最大地址(可以使用的) ida_ida.inf_get_max_ea()
获取所选范围的起始地址 idc.read_selection_start()
获取所选范围的结束地址 idc.read_selection_end()
判断地址是否存在 idaapi.BADADDR

这些函数都不需要参数

代码如下,我选中的区域就是部分main函数

1
2
3
4
5
6
7
8
9
10
print(hex(idc.here()))
print(hex(ida_ida.inf_get_max_ea()))
print(hex(ida_ida.inf_get_min_ea()))
print(hex(idc.read_selection_start()))
print(hex(idc.read_selection_end()))

if idc.here() == idaapi.BADADDR:
print("不存在")
else:
print("存在")

image-20240513170341564

note warning 注意

使用idc.read_selection_start()idc.read_selection_end()是需要使用光标选中两行及以上的,不然就会返回0xffffffff

获取地址的值

获取地址的数值
以1字节为单位获取值 idc.get_wide_byte(addr)
以2字节(字)的单位获取值 idc.get_wide_word(addr)
以4字节的单位获取值 idc.get_wide_dword(addr)
以8字节的单位获取值 idc.get_qword(addr)
得到运行断点之前的寄存器的值 ea=get_reg_value(“eax”)

代码如下

1
2
3
4
5
6
7
import idc

ea = idc.get_screen_ea()
print(hex(idc.get_wide_byte(ea)))
print(hex(idc.get_wide_word(ea)))
print(hex(idc.get_wide_dword(ea)))
print(hex(idc.get_qword(ea)))

image-20240513175906388

修改指令的值

作用 函数
修改addr地址的值为value.每次修改1个字节 ida_bytes.patch_byte(addr,value)
每次修改2个字节 ida_bytes.patch_word(addr,value)
每次修改4个字节 ida_bytes.patch_Dword(addr,value)
每次修改8个字节 ida_bytes.patch_Qword(addr,value)

测试代码

1
2
3
4
5
6
ea = idc.get_screen_ea()
value = idc.get_wide_byte(ea)
print("未修改={}".format(hex(value)))
ida_bytes.patch_byte(ea,0x90)
value = idc.get_wide_byte(ea)
print("修改后={} ".format(hex(value)))

image-20240513180639219

段操作

作用 函数
获取段的名字(参数为当前的地址) idc.get_segm_name(addr)
获取段的开始地址 idc.get_segm_start(addr)
获取段的结束地址 idc.get_segm_end(addr)
获取第一个段 idc.get_first_seg(addr)
获取下一个段 idc.get_next_seg(addr)
返回一个列表记录所有段的地址 idautil.Segments()

代码

1
2
3
4
5
for seg in idautils.Segments():
segname = idc.get_segm_name(seg)
segstart = idc.get_segm_start(seg)
segend = idc.get_segm_end(seg)
print("段名 = {} 起始地址= {} 结束地址 = {} ".format(segname,hex(segstart),hex(segend)));

image-20240513181924962

函数操作

作用 函数
获取指定地址之间的所有函数 idautils.Functions(startaddr,endaddr)
获取指定地址的函数名 idc.get_func_name(addr)
获取函数的注释 get_func_cmt(addr, repeatable) repeatable:0/1 0是获取常规注释 1是获取重复注释
设置函数注释 idc.set_func_cmt(ea, cmt, repeatable)
弹出框框要求用户进行选择 参数则是信息 idc.choose_func(title)
返回: addr 距离函数的偏移形式 idc.get_func_off_str(addr)
寻找函数结尾,如果函数存在则返回结尾地址,否则返回BADADDR idc.find_func_end(addr)
设置函数结尾 ida_funcs.set_func_end(ea, newend) newend:新的结束地址
设置函数开头 ida_funcs.set_func_start(addr, newstart)
设置地址处的名字 idc.set_name(ea, name, SN_CHECK) Ex函数也使用set_name
获取首个函数 idc.get_prev_func(ea)
获取下一个函数 idc.get_next_func(ea)

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
for seg in idautils.Segments():
segname = idc.get_segm_name(seg)
segstart = idc.get_segm_start(seg)
segend = idc.get_segm_end(seg)
if (segname == '.text'):
for funcaddr in Functions(segstart,segend):
funname = idc.get_func_name(funcaddr)
funend = idc.find_func_end(funcaddr)
funnext = idc.get_next_func(funcaddr)
funnextname = idc.get_func_name(funnext)
print("当前函数名 = {} 当前结束地址 = {} 下一个函数地址 = {} 下一个函数名= {} ".format(funname,hex(funend),hex(funnext),funnextname))


ea = idc.get_screen_ea()
funnextoffset = idc.get_func_off_str(ea)
print("当前选择地址距离当前函数的偏移为: {} ".format(funnextoffset))

image-20240513182446537

练手

网鼎杯 2020 青龙组 Jocker

还是最开始的那道程序

image-20240513182845930

可以看到encrypt函数去异或0x41,这就是一个很典型的SMC(self modifying code)操作。我们要做的就是让这段for循环运行起来,可以动态调试或者写ida python脚本运行。这里不再讲动态调试,我们要做的就是找到encrypt函数的地址,接着去获取该地址处的值,异或0x41来还原即可。

encrypt的地址:0x41500

image-20240513183319222

脚本:

1
2
3
4
addr = 0x401500
for i in range(187):
value = idc.get_wide_byte(addr + i)
ida_bytes.patch_byte(addr + i, value ^ 0x41)

image-20240513184428966

之后选中整段函数,重新分析(快捷键C),之后生成函数(快捷键P),即可还原函数

image-20240513184742895