获取当前光标所指向的地址
idaapi.here()idc.get_screen_ea()
返回一个整型地址,为光标所在行的地址
curEA = idc.here()print("CurEA: %08X" % curEA)
通过一个地址 获得命中的函数
idc.GetFunctionAttr(EA, idc.FUNCATTR_START)idc.GetFunctionAttr(EA, idc.FUNCATTR_END)
分别得到IDA自动分析得到的函数头和函数尾部的地址
dwFunStart = idc.GetFunctionAttr(curEA, idc.FUNCATTR_START)dwFunEnd = idc.GetFunctionAttr(curEA, idc.FUNCATTR_END)
通过一个地址 获得已分析的函数名
idc.GetFunctionName(ea) 获得指定地址的函数名
Python>print hex(ea), idc.GetDisasm(ea)0x401040 push ebpPython>idc.GetFunctionName(ea)sub_401040
通过一个地址,得到一个函数对象
idaapi.get_func(EA)
通过一个地址,得到一个函数对象,对象类型(idaapi.func_t)
curFun = idaapi.get_func(dwFunStart)if curFun is not None:dwFunStart = curFun.start_eadwFunEnd = curFun.end_eaprint("GetFunSucess\r\nFunStart: %08X\r\nFunEnd: %08X" % (dwFunStart, dwFunEnd))else:print ("GetFunError")
通过一个地址,得到一个函数中,所有行的地址
idautils.FuncItems(EA)
传入一个函数的开始地址,然后返回一个生成器
每次调用生成器,返回一条指令地址
lstFunAddr = list(idautils.FuncItems(dwFunStart))
lstFunAddr = list(idautils.FuncItems(dwFunStart))print ("ShowLst:")for i in lstFunAddr:print ('Addr: %08X' % i)
通过一个地址,获得当前行的注释
idaapi.get_cmt(EA, flag)
参数1,地址;参数2,是否获取重复,返回一个注释字符串
cmt = idaapi.get_cmt(addr, 0)print(cmt)
给地址设置名称
idc.MakeNameEx(ea, name, flags)
地址, 名称字符串, 什么类型的名称
idc.set_name(0x004086B9, "Test1", idc.SN_NOWARN)
扫描特征码
decode_func_code = '\x55\x8B\xEC\x51\xC7\x45\xFC\x00\x00\x00\x00\xEB\x09\x8B\x45\xFC\x83\xC0\x01\x89\x45\xFC\x8B\x4D\xFC\x3B\x4D\x0C\x73\x2C\x8B\x55\x08\x03\x55\xFC\x0F\xB6\x02\x8B\x4D\xFC\x81\xE1\x3F\x00\x00\x80' # 绝对地址特征码def get_decode_func_addr():for func in idautils.Functions(): # 获得已解析的函数func_code = idc.GetManyBytes( func, len(decode_func_code) ) # 得到特征码长度的二进制字符串#print 'func=%08X, func_code=%s, decode_code=%s' %(func,func_code,decode_func_code)if func_code == decode_func_code: # 比对特征码print 'decode addr = %08X' %( func )return funcreturn idc.BADADDR # 返回一个错误值
通过一个地址得到数据
idc.Byte(EA) # 得到指定地址的一个字节数据idc.Word(EA) # 得到一个DWORDidc.QWord(EA) # 得到一个QWORD
通过名称得到函数地址
idc.LocByName(str)
Python>wf_addr = idc.LocByName("WriteFile")Python>print hex(wf_addr), idc.GetDisasm(wf_addr)0x1000e1b8 extrn WriteFile:dwordPython>for addr in idautils.CodeRefsTo(wf_addr, 0):\print hex(addr), idc.GetDisasm(addr)0x10004932 call ds:WriteFile0x10005c38 call ds:WriteFile0x10007458 call ds:WriteFile
通过地址 得到汇编代码
idc.GetDisasm(ea)
Python>ea = 0x10004932Python>print hex(ea), idc.GetDisasm(ea)0x10004932 call ds:WriteFilePython>for addr in idautils.CodeRefsFrom(ea, 0):\print hex(addr), idc.GetDisasm(addr)Python>0x1000e1b8 extrn WriteFile:dword
交叉引用
idautils.CodeRefsTo(ea, 0) 获得代码引用idautils.DataRefsTo(ea) 获得数据引用
idautils.XrefsTo(ea, flag=0) 获得所有的引用(数据和代码??)idautils.XrefTypeName(xref.type) 获得引用的类型
xrefType类型如下:
| 数值 | 类型 |
|---|---|
| 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’ |
def get_to_xrefs(ea):xref_set = set([])for xref in idautils.XrefsTo(ea, 1):xref_set.add(xref.frm)return xref_setdef get_frm_xrefs(ea):xref_set = set([])for xref in idautils.XrefsFrom(ea, 1):xref_set.add(xref.to)return xref_set
已分析代码中 最大 最小界限
MinEA() 最小的可分析界限MaxEA() 最大的可分析界限
二进制搜索
idc.FindBinary(ea, flag, searchstr, radix=16)
| 参数 | 作用 |
|---|---|
| EA | 为起始地址 |
| flag | 为搜索方式(见下表) |
| searchstr | 为搜索内容 |
| radix | 暂未知 |
| 枚举 | 数值 | 作用 | 备注 |
|---|---|---|---|
| SEARCH_UP | 0 | 向上搜索 | |
| SEARCH_DOWN | 1 | 向下搜索 | |
| SEARCH_NEXT | 2 | 下一个匹配项 | |
| SEARCH_CASE | 4 | 大小写敏感? | |
| SEARCH_REGEX | 8 | 正则? | |
| SEARCH_NOBRK | 16 | ||
| SEARCH_NOSHOW | 32 | 不显示搜索状态?? | |
| SEARCH_UNICODE | 64 | 搜索结果未Unicode |
** |
| SEARCH_IDENT | 128 | ** | |
| SEARCH_BRK | 256 | ** |
标记 ** 为旧版本不支持
这些标志位 可以 叠加 使用|运算
Python>pattern = '55 8B EC'addr = MinEA()for x in range(0,5):addr = idc.FindBinary(addr, SEARCH_DOWN, pattern);if addr != idc.BADADDR:print hex(addr), idc.GetDisasm(addr)Python>0x401000 push ebp0x401000 push ebp0x401000 push ebp0x401000 push ebp0x401000 push ebp# 可见每次搜索都从指定地址开始, 查找下一个 标志则为 SEARCH_NEXT
Python>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)Python>0x401040 push ebp0x401070 push ebp0x4010e0 push ebp0x401150 push ebp0x4011b0 push ebp# 在设置Next标志位以后, 会自动顺延
字符串搜索
idc.FindText(ea, flag, y, x, searchstr) 查找字符串
Python>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:breakelse:print hex(cur_addr), idc.GetDisasm(cur_addr)cur_addr = idc.NextHead(cur_addr)Python>0x40da72 push offset aAcceptEncoding; "Accept-Encoding:\n"0x40face push offset aHttp1_1Accept; " HTTP/1.1\r\nAccept: */*\r\n "0x40fadf push offset aAcceptLanguage; "Accept-Language: ru\r\n"...0x423c00 db 'Accept',00x423c14 db 'Accept-Language',00x423c24 db 'Accept-Encoding',00x423ca4 db 'Accept-Ranges',0
地址类型判断
根据IDA的自动分析, 给每个地址作出标识
| 函数 | 功能 |
|---|---|
idc.isCode(flag) |
返回True 如果IDA自动分析, 判定地址为Code. |
idc.isData(flag) |
返回True 如果IDA自动分析, 判定地址为Data. |
idc.isHead(flag) |
返回True 如果IDA自动分析, 判定地址为Head(头部). |
idc.isTail(flag) |
返回True 如果IDA自动分析, 判定地址为Tail(尾部). |
idc.isUnknown(flag) |
返回True 如果IDA自动分析, 判定地址为Unknown, 此标记表示IDA无法分析此地址是Code还是Data. |
flag通过idc.GetFlags(ea)得到
获取选择目标的 开始和结束
idc.SelStart() 获得当前所选的开始地址idc.SelEnd() 获取当前所选的结束地址(选择结尾的下一条地址)
# .text:00408E46 push ebp# .text:00408E47 mov ebp, esp# .text:00408E49 mov al, byte ptr dword_42A508# .text:00408E4E sub esp, 78h# .text:00408E51 test al, 10h# .text:00408E53 jz short loc_408E78# .text:00408E55 lea eax, [ebp+Data]Python>start = idc.SelStart()Python>hex(start)0x408e46Python>end = idc.SelEnd()Python>hex(end)0x408e58
idaapi.read_selection() 获得选区, 返回一个元组, 其中包含 获得成功与否, 选择的开始地址和结束地址
Python>Worked, start, end = idaapi.read_selection()Python>print Worked, hex(start), hex(end)True 0x408e46 0x408e58
数据获取
| 函数 | 功能 |
|---|---|
| idc.Byte(ea) | 从指定地址, 获得一个Byte. |
| idc.Word(ea) | 从指定地址, 获得一个Word. |
| idc.Dword(ea) | 从指定地址, 获得一个Dword. |
| idc.Qword(ea) | 从指定地址, 获得一个Qword. |
| idc.GetFloat(ea) | 从指定地址, 获得一个Float. |
| idc.GetDouble(ea) | 从指定地址, 获得一个Double. |
Python>print hex(ea), idc.GetDisasm(ea)0xa14380 mov ecx, hHeapPython>hex( idc.Byte(ea) )0x8bPython>hex( idc.Word(ea) )0xd8bPython>hex( idc.Dword(ea) )0x6d0c0d8bPython>hex( idc.Qword(ea) )0x6a5000a26d0c0d8bLPython>idc.GetFloat(ea) # Example not a float value2.70901711372e+27Python>idc.GetDouble(ea)1.25430839165e+204
idc.GetManyBytes(ea, size, use_dbg=False) 从一个地址开始获取一个缓冲区
Python>for byte in idc.GetManyBytes(ea, 6):print "0x%X" % ord(byte),0x8B 0xD 0xC 0x6D 0xA2 0x0
Tips: idc.GetManyBytes(ea, size) 返回一个 字符序列, 并不像idc.Word(ea)或idc.Qword(ea)那样 返回一个整形
在IDB中修改字节码
| 函数 | 作用 |
|---|---|
idc.PatchByte(ea, value) |
修改指定地址的一个字节 |
idc.PatchWord(ea, value) |
修改指定地址的一个字 |
idc.PatchDword(ea, value) |
修改指定地址的一个双字 |
# .data:1001ED3C aGcquEUdg_bUfuD db 'gcqu^E]~UDG_B[uFU^DC',0# .data:1001ED51 align 8# .data:1001ED58 aGcqs_cuufuD db 'gcqs\_CUuFU^D',0# .data:1001ED66 align 4# .data:1001ED68 aWud@uubQU db 'WUD@UUB^Q]U',0# .data:1001ED74 align 8# 100012A0 push esi# 100012A1 mov esi, [esp+4+_size]# 100012A5 xor eax, eax# 100012A7 test esi, esi# 100012A9 jle short _ret# 100012AB mov dl, [esp+4+_key] ; assign key# 100012AF mov ecx, [esp+4+_string]# 100012B3 push ebx# 100012B4# 100012B4 _loop: ;# 100012B4 mov bl, [eax+ecx]# 100012B7 xor bl, dl ; data ^ key# 100012B9 mov [eax+ecx], bl ; save off byte# 100012BC inc eax ; index/count# 100012BD cmp eax, esi# 100012BF jl short _loop# 100012C1 pop ebx# 100012C2# 100012C2 _ret: ;# 100012C2 pop esi# 100012C3 retnPython>start = idc.SelStart()Python>end = idc.SelEnd()Python>print hex(start)0x1001ed3cPython>print hex(end)0x1001ed50Python>def xor(size, key, buff):for index in range(0,size):cur_addr = buff + indextemp = idc.Byte( cur_addr ) ^ keyidc.PatchByte(cur_addr, temp)Python>Python>xor(end - start, 0x30, start)Python>idc.GetString(start)WSAEnumNetworkEvents
IDA_Python中的文件输入输出
AskFile(forsave, mask, prompt)
# forsave: 值为0或1, 0为启动一个 "打开"对话框, 1为启动一个"保存"对话框# mask: 字符串, 为对话框的文件过滤器 比如: 过滤".dll" 则传入过滤字符串"*.dll"# prompt: 字符串, 为对话框的"Title"
import sysimport idaapiclass IO_DATA():
数据获取方式为在IDA中选中一段数据, 则自动获得 选择的起始地址和结束地址作用有限, 需自定义Decode()函数```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 = Falsedef getData(self):'''把选择区间内的数据, 保存到类内置缓冲区'''self.ogLen = self.end - self.startself.buffer = ''try:for byte in idc.GetManyBytes(self.start, self.ogLen):self.buffer = self.buffer + byteexcept:self.status = Falsereturndef run(self):'''类的起始工作函数, 相当于Main()'''self.checkBounds()if self.status == False:sys.stdout.write('ERROR: Please select valid data\n')returnself.getData()def patch(self, temp = None):'''把缓冲区的数据, 替换掉IDB中的数据'''if temp != None:self.buffer = tempfor index, byte in enumerate(self.buffer):idc.PatchByte(self.start+index, ord(byte))def importb(self):'''导入一个文件, 初始化内置缓冲区'''fileName = idc.AskFile(0, "*.*", 'Import File')try:self.buffer = open(fileName, 'rb').read()except:sys.stdout.write('ERROR: Cannot access file')def export(self):'''保存选择的数据到一个文件'''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))def decode(self):```解密函数```pass
---<a name="sn4glk"></a># 使用IDA生成一个程序的 `idb`和 `asm`文件经测试我的`IDA7`版本的`idat.exe`程序`QT`框架上有些问题, 可使用`IDA68`版本的`idaw.exe`程序使用参数`-B`命令生成文件全文的`ASM`文件和`IDB`文件```pythonimport osimport subprocessimport globpaths = glob.glob("*")ida_path = os.path.join(os.environ['PROGRAMFILES'], "IDA","idaw.exe")for file_path in paths:if file_path.endswith(".py"):continuesubprocess.call([ida_path, "-B", file_path])
C:\injected>dir0?/**/____ 09:30 AM <DIR> .0?/**/____ 09:30 AM <DIR> ..0?/**/____ 10:48 AM 167,936 bad_file.exe0?/**/____ 09:29 AM 270 batch_analysis.py0?/**/____ 06:55 PM 104,889 injected.dllC:\injected>python batch_analysis.pyThank you for using IDA. Have a nice day!C:\injected>dir0?/**/____ 09:30 AM <DIR> .0?/**/____ 09:30 AM <DIR> ..0?/**/____ 09:30 AM 506,142 bad_file.asm0?/**/____ 10:48 AM 167,936 bad_file.exe0?/**/____ 09:30 AM 1,884,601 bad_file.idb0?/**/____ 09:29 AM 270 batch_analysis.py0?/**/____ 09:30 AM 682,602 injected.asm0?/**/____ 06:55 PM 104,889 injected.dll0?/**/____ 09:30 AM 1,384,765 injected.idb
等待IDA分析结束
idaapi.autoWait() 用于等待IDA载入文件并自动分析全文结束
退出IDA脚本
idaapi.qexit(0) 或 idc.Exit(0) 用于退出当前脚本, 会连同IDA一起退出sys.exit(0) 会连同IDA程序一起退出
可使用 return 进行函数或脚本退出
自动分析
idaapi.auto_make_code(dwEA) 设置某个地址分析为代码
获取工作目录
idc.get_idb_path() # 获得当前IDB所在文件路径idc.get_input_file_path() # 获得创建IDB时, 被分析的目标文件所在路径
