N:重命名地址符号 Y:修改变量类型 U:代码转换为数据 C:数据到代码 P:转化为函数 A:将数据转化为字符串 Edit->String->Unicode:将数据转化为宽字节码 *:将数据段的数据转化为数组的格式,必须先将第一个元素更改为合适的类型才可以 T:将数据转化为结构体 D:对数据db/dw/dd之间进行切换 U:取消定义
: 常规注释 ; 可重复注释,对该地址处的引用也会显示注释 G:跳转到指定位置 X:在光标所在地址,弹出cross reference对话框,列出所有引用它的地点,不一定是目标地 F5:HEXRAYS反C插件,直接看C代码 改变模块基地址:Edit->Segments->Rebase Program->改变Value数值为你想要的模块基地址
Edit-->Patch Program:修改字节码。由于IDA加载文件时会丢失一些数据,所以如果想把修改应用到程序中,只能先生成DIF文件,然后依据DIF文件使用010editor等编辑工具来修改exe文件
File-->Produce file:生成各种各样的文件 File-->Produce file-->Create DIF File...:生成描述修改字节内容的文件
Alt+T:字符串搜索 Ctrl+T:搜索下一个字符串匹配项 Alt+B: 二进制搜索 Ctrl+B:搜索下一个二进制匹配项
跳转到前一个窗口: Jump->Jump to Previous Position 或者 Esc 跳转到后一个窗口: Jump->Jump to Next Position 或者 Ctrl+Enter
View-->OpenSubviews-->Local Types-->INSERT 插入一个已存在的结构体 FILE-->LOAD FILE-->Parse C Header File 从头文件中导入结构体
添加结构体/枚举 (1)Shift+F1: 打开Local Types,增删改查当前结构体/枚举 (2)Shift+F9/Shift+F10: 打开Structures/Enums,按下INSERT,点击Add standard structure,选择在Local Types中添加的结构体/枚举 (3)显示静态数据区中的结构体: 选中数据区的某处地址,点击Edit->Structs->Struct var...,选择想要转换的结构体 (4)显示结构体偏移: 选中偏移或者选中代码段中包含结构体使用的那几行,按下T,选择要转换的类型,就会显示结构体偏移提示 (5)显示枚举: 选中数字,按下M,选择要转换的枚举值
Shift+F1:查看当前所有结构体 INSERT:插入一个结构体,或者从local types中导入standard D:插入一个结构体成员,并切换它们的字节类型 U-->Edit-->shrink struct type:删除一个结构体成员 Edit-->Expand struct type:在中间增加一个结构体成员 U:在结构体ENDS一行,可删除最后一个结构体成员
对偏移右键-->struct offset:应用新建的结构体 选择stack frame起始地址-->edit-->struct var:应用新建的结构体
View-->Open Subviews-->Function Calls:所有调用当前地址的函数,和子窗口中该函数调用的所有函数 View-->Open Subviews-->Cross-References:必须是cross view的目标地,即该引用的本身 View-->Graphs-->Xrefs To:引用该符号的函数图 View-->Graphs-->Xrefs From:该符号引用的函数图
F12:将光标放在一个函数里,按F12,得到该函数的流程图 CTRL+F12:函数调用关系图 Shift+F12:字符串表,右键选择Setup,指定字符串类型
(数组:a + index sizeof (a[0])) (结构体:a + offset) (结构体数组:a +index sizeof(a[0]) +offset)
常用调用约定: (1) cdecl (C调用约定) 从右到左传参数,调用函数清理栈 (2) stdcall (标准调用约定) 从右到左传参数,被调用函数清理栈 (3) fastcall (快速调用约定) x86上ecx、edx传前两个参数,之后的参数从右到左传递,被调用函数清理栈 x64上rcx、rdx、r8、r9传前四个参数,之后的参数从右到左传递,被调用函数清理栈 (4) thiscall (C++调用约定) 将this传递到ecx寄存器中
运行脚本文件: File->Script File
运行脚本命令: File->Script command.../Shift+F2 注:由于ida会将语句包装在一个函数中,所以不能在命令对话框中定义函数
关联idc脚本与热键: 1.打开<IDADIR>/idc/ida.idc,运行ida时会自动执行该脚本 2.添加文件开头添加 #include<my_amazing_script.idc> 3.在static main()函数中添加 AddHostKey("z", "MyAmazingFunc") 4.如果该热键已经分配给另一项ida操作,则AddHostKey会失败
IDAPython的API文档: https://www.hex-rays.com/products/ida/support/idapython_docs/
IDAPython分为三个模块:
idaapi 负责访问核心IDA API
idc 提供idc中所有函数的功能
idautils 提供大量实用函数
注:所有IDAPython脚本会自动导入idc和idautils模块,但idaapi模块必须手动导入
最小地址/最大地址
idc.MinEA()
idc.MaxEA()
光标所在地址
ea=idc.here()
段名
idc.get_segm_name(ea)
第一个段地址/下一个段地址
idc.get_first_seg()
idc.get_next_seg(ea)
段开始/段结束
idc.get_segm_start(ea)
idc.get_segm_end(ea)
反汇编指令
idc.GetDisasm(ea)
助记符
idc.GetMnem(ea)
第一个操作数/第二个操作数
idc.GetOpnd(ea, 0)
idc.GetOpnd(ea, 1)
验证地址是否存在 idaapi.BADADDR
if BADADDR != here():
print "valid address"
遍历所有段
for seg in idautils.Segments():
print idc.SegName(seg), hex(idc.SegStart(seg)), hex(idc.SegEnd(seg))
遍历所有已知函数
for func in idautils.Functions():
print hex(func), idc.GetFunctionName(func)
通过开始地址和结束地址搜索函数
for func in idautils.Functions(start_addr, end_addr):
......
地址所在的函数
func = idaapi.get_func(ea)
dir(func)
print "Start: 0x%x"%(func.startEA)
print "End: 0x%x"%(func.endEA)
前一个函数/后一个函数
idc.PrevFunction(ea)
idc.NextFunction(ea)
函数边界
start = idc.GetFunctionAttr(ea, FUNCATTR_START)
end = idc.GetFunctionAttr(ea, FUNCATTR_END)
遍历地址范围内的指令
cur_addr = start
while cur_addr <= end:
print hex(cur_addr), idc.GetDisasm(cur_addr)
cur_addr = idc.NextHead(cur_addr, end)
函数标志
for func in idautils.Functions():
flags = idc.GetFunctionFlags(func)
if flags & FUNC_NORET:
print hex(func), "FUNC_NORET"
if flags & FUNC_FAR:
print hex(func), "FUNC_FAR"
if flags & FUNC_LIB:
print hex(func), "FUNC_LIB"
if flags & FUNC_STATIC:
print hex(func), "FUNC_STATIC"
if flags & FUNC_FRAME:
print hex(func), "FUNC_FRAME"
if flags & FUNC_USERFAR:
print hex(func), "FUNC_USERFAR"
if flags & FUNC_HIDDEN:
print hex(func), "FUNC_HIDDEN"
if flags & FUNC_THUNK:
print hex(func), "FUNC_THUNK"
if flags & FUNC_LIB:
print hex(func), "FUNC_BOTTOMBP"
FUNC_NORET 这个标志用来标识一个函数没有执行一个返回指令。它的内部表示等于 1 FUNC_FAR 这个标志很少出现,除非逆向软件使用分段内存。它的内部表示为一个整数 2 FUNC_USERFAR 这个标志是罕见的,具有非常小的文件。hexrays 描述标志为“用户已指定远性功能”。 它的内部值为 32 FUNC_LIB 此标志用于查找库代码。识别库代码非常有用,因为它是在执行分析时通常可以忽略的 代码。它的内部表示为整数 4 FUNC_STATIC 此标志用于标识作为静态函数编译的函数。在 C 函数中默认是全局的。如果作者定义了 一个函数为静态只能访问内部文件等功能。在有限的方式下,这可以用来帮助理解源代码是 如何构造的 FUNC_FRAME 这个标志表明该函数使用帧指针 EBP FUNC_BOTTOMBP 类似 FUNC_FRAME 此标记用于跟踪帧指针。它将确定帧指针等于堆栈指针函数 FUNC_HIDDEN 函数带 FUNC_HIDDEN 标志意味着他们是隐藏的将需要扩展到视图。如果我们转到一个 被标记为隐藏的函数的地址,它会自动扩展 FUNC_THUNK 这标志标识函数是 thunk 函数。一个简单的功能是跳到另一个函数
访问函数指令
dism_addr = list(idautils.FuncItems(here()))
for line in dism_addr:
print hex(line), idc.GetDisasm(line)
print dism_addr
打印出所有的call或者jmp指令
for func in idautils.Functions():
flags = idc.GetFunctionFlags(func)
if flags & FUNC_LIB or flags & FUNC_THUNK:
continue
dism_addr = list(idautils.FuncItems(func))
for line in dism_addr:
m = idc.GetMnem(line)
if m == 'call' or m == 'jmp':
op = idc.GetOpType(line, 0)
if op == o_reg:
print "0x%x %s" % (line, idc.GetDisasm(line))
下一条指令地址和前一条指令地址
idc.NextHead(ea)
idc.PrevHead(ea)
下一个地址和前一个地址
idc.NextAddr(ea)
idc.PrevAddr(ea)
获取地址处汇编语句的第n个操作数的类型
idc.GetOpType(ea, n)
o_void 没有任何操作数它将返回 0 o_reg 如果一个操作数是一个普遍的寄存器将返回此类型。这个值在内部表示为 1 o_mem 如果一个操作数是直接内存引用它将返回这个类型。这个值在内部表示为 2 o_phrase这个操作数被返回则这个操作数包含一个基本的寄存器或一个索引寄存器。这个值在内部表示为 3 o_displ 这个操作数被返回则操作数包含寄存器和一个位移值,这个为位移值是一个整数。在内部,它表示为 4 的值 o_imm 操作数是整数。它在内部表示为 5 o_far 这个操作数不是很常见当逆向 x86 或 x86_64 时。它是用来寻找操作数的访问立即数远地址的。它在内部表示为 6 o_near 这个操作数不是很常见当逆向 x86 或 x86_64 时。它是用来寻找操作数的访问立即数近地址的。它在内部表示为 7
定位导入函数WriteFile的地址
wf_addr = idc.LocByName("WriteFile")
所有的重命名函数和APIs能够通过调用idautils.Names()访问
[x for x in Names()]
重命名地址处
idc.MakeName(ea, "RtlCompareMemory")
遍历代码引用
idautils.CodeRefsTo(wf_addr, 0)
idautils.CodeRefsFrom(ea, 0)
for addr in idautils.CodeRefsTo(wf_addr, 0):
print hex(addr), idc.GetDisasm(addr)
for addr in idautils.CodeRefsFrom(ea, 0):
print hex(addr), idc.GetDisasm(addr)
遍历数据引用
idautils.DataRefsTo(ea)
idautils.DataRefsFrom(ea)
for addr in idautils.DataRefsTo(ea):
print hex(addr), idc.GetDisasm(addr)
for addr in idautils.DataRefsFrom(ea):
print hex(addr), idc.GetDisasm(addr)
遍历所有引用
idautils.XrefsTo(ea, flags=0)
idautils.XrefsFrom(ea, flags=0)
for xref in idautils.XrefsTo(ea, 1):
print xref.type, idautils.XrefTypeName(xref.type), hex(xref.frm), hex(xref.to), xref.iscode
xref.type idautils.XrefTypeName(xref.type)
0 = 'Data_Unknown' 1 = 'Data_Offset' 2 = 'Data_Write' 3 = 'Data_Read' 4 = 'Data_Text' 5 = 'Data_Informational' 16 = 'Code_Far_Call' 17 = 'Code_Near_Call' 18 = 'Code_Far_Jump' 19 = 'Code_Near_Jump' 20 = 'Code_User' 21 = 'Ordinary_Flow'
搜索二进制字节
idc.FindBinary(startAddr, flag, searchstr, radix=16)
flag: SEARCH_UP = 0 SEARCH_DOWN = 1 SEARCH_NEXT = 2 SEARCH_CASE = 4 SEARCH_REGEX = 8 SEARCH_NOBRK = 16 SEARCH_NOSHOW = 32 SEARCH_UNICODE = 64 SEARCH_IDENT = 128 SEARCH_BRK = 256
Older versions of IDAPython do not support these
pattern = '55 8B EC'
addr = MinEA()
for x in range(0,5):
addr = idc.FindBinary(addr, SEARCH_DOWN|SEARCH_NEXT,pattern);
if addr != idc.BADADDR:
print hex(addr), idc.GetDisasm(addr)
搜索字符串
idc.FindText(startAddr, flag, y, x, searchstr)
cur_addr = MinEA()
end = MaxEA()
while cur_addr < end:
cur_addr = idc.FindText(cur_addr, SEARCH_DOWN, 0, 0,"Accept")
if cur_addr == idc.BADADDR:
break
else:
print hex(cur_addr), idc.GetDisasm(cur_addr)
cur_addr = idc.NextHead(cur_addr)
获得地址标志
f = idc.GetFlags(ea)
代码 idc.isCode(f) 数据 idc.isData(f) 尾部 idc.isTail(f) 未知 idc.isUnknown(f) 头部 idc.isHead(f)
找到下一个被标记为代码的地址
idc.FindCode(ea, flag)
找到下一个被标记为数据的地址
idc.FindData(ea, flag)
用来寻找的字节地址,IDA 没有识别代码或数据
idc.FindUnexplored(ea, flag)
用来寻找一个地址,IDA 确定为代码和数据
idc.FindExplored(ea, flag)
用来寻找汇编中操作的立即数
addr, operand = idc.FindImmediate(ea, flag, value)
获得选择数据的边界
start = idc.SelStart()
end = idc.SelEnd()
isSel, start, end = idaapi.read_selection()
添加普通注释
idc.MakeComm(ea, comment)
添加可重复注释(当一个指令引用一个包含重复注释的地址时,其他注释就会出现)
idc.MakeRptCmt(ea, comment)
为函数添加注释
idc.SetFunctionCmt(ea, cmt, repeatable)
获取普通/可重复注释
GetCommentEx(ea, repeatable)
获得函数的注释
idc.GetFunctionCmt(ea, repeatable)
更改/获取函数名
idc.MakeName(ea, name)
idc.MakeNameEx(ea, name, flag)
idc.GetFunctionName(ea)
读数据
idc.Byte(startAddr)
idc.Word(startAddr)
idc.Dword(startAddr)
idc.Qword(startAddr)
idc.GetFloat(startAddr)
idc.GetDouble(startAddr)
idc.GetString(startAddr)
idc.GetManyBytes(startAddr, size, use_dbg=False)
写数据
idc.PatchByte(ea, value)
idc.PatchWord(ea, value)
idc.PatchDword(ea, value)
询问文件
AskFile(forsave, mask, prompt)
下面是一个用于简单处理数据输入输出的类
import sys
import idaapi
class IO_DATA():
def __init__(self):
self.start = SelStart()
self.end = SelEnd()
self.buffer = ''
self.ogLen = None
self.status = True
self.run()
def checkBounds(self):
if self.start is BADADDR or self.end is BADADDR:
self.status = False
def getData(self):
'''get data between start and end put them into object.buffer'''
self.ogLen = self.end - self.start
self.buffer = ''
try:
for byte in idc.GetManyBytes(self.start, self.ogLen):
self.buffer = self.buffer + byte
except:
self.status = False
return
def run(self):
'''basically main'''
self.checkBounds()
if self.status == False:
sys.stdout.write('ERROR: Please select valid data\n')
return self.getData()
def patch(self, temp = None):
'''patch idb with data in object.buffer'''
if temp != None:
self.buffer = temp
for index, byte in enumerate(self.buffer):
idc.PatchByte(self.start+index, ord(byte))
def importb(self):
'''import file to save to buffer'''
fileName = idc.AskFile(0, "*.*", 'Import File')
try:
self.buffer = open(fileName, 'rb').read()
except:
sys.stdout.write('ERROR: Cannot access file')
def export(self):
'''save the selected buffer to a file'''
exportFile = idc.AskFile(1, "*.*", 'Export Buffer')
f = open(exportFile, 'wb')
f.write(self.buffer)
f.close()
def stats(self):
print "start: %s" % hex(self.start)
print "end: %s" % hex(self.end)
print "len: %s" % hex(len(self.buffer))
使用命令行对idb文件执行IDAPython脚本
idaapi.autoWait() 该函数运行之后会使脚本阻塞,一旦ida分析完毕,将会通过回调函数通知脚本,因此在脚本的一开始调用该函数就显得至关重要
idc.Exit() 该函数会停止执行的脚本,关闭idb数据库
执行一系列分析操作
运行命令 "D:\softwares\IDA 7.2\ida64.exe" -A -Scount.py cur-analysis.idb -A是自动模式 -S用于告诉ida,当打开idb是自动加载运行idapython脚本,注意-S之后没有空格
附件为IDAPython手册(中文版),由y0n翻译
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2020-5-7 19:25
被TechForBad编辑
,原因:
上传的附件: