获取当前光标所指向的地址

idaapi.here()
idc.get_screen_ea()
返回一个整型地址,为光标所在行的地址

  1. curEA = idc.here()
  2. print("CurEA: %08X" % curEA)

通过一个地址 获得命中的函数

idc.GetFunctionAttr(EA, idc.FUNCATTR_START)
idc.GetFunctionAttr(EA, idc.FUNCATTR_END)
分别得到IDA自动分析得到的函数头和函数尾部的地址

  1. dwFunStart = idc.GetFunctionAttr(curEA, idc.FUNCATTR_START)
  2. dwFunEnd = idc.GetFunctionAttr(curEA, idc.FUNCATTR_END)

通过一个地址 获得已分析的函数名

idc.GetFunctionName(ea) 获得指定地址的函数名

  1. Python>print hex(ea), idc.GetDisasm(ea)
  2. 0x401040 push ebp
  3. Python>idc.GetFunctionName(ea)
  4. sub_401040

通过一个地址,得到一个函数对象

idaapi.get_func(EA)
通过一个地址,得到一个函数对象,对象类型(idaapi.func_t)

  1. curFun = idaapi.get_func(dwFunStart)
  2. if curFun is not None:
  3. dwFunStart = curFun.start_ea
  4. dwFunEnd = curFun.end_ea
  5. print("GetFunSucess\r\nFunStart: %08X\r\nFunEnd: %08X" % (dwFunStart, dwFunEnd))
  6. else:
  7. print ("GetFunError")

通过一个地址,得到一个函数中,所有行的地址

idautils.FuncItems(EA)
传入一个函数的开始地址,然后返回一个生成器
每次调用生成器,返回一条指令地址

lstFunAddr = list(idautils.FuncItems(dwFunStart))

  1. lstFunAddr = list(idautils.FuncItems(dwFunStart))
  2. print ("ShowLst:")
  3. for i in lstFunAddr:
  4. print ('Addr: %08X' % i)

通过一个地址,获得当前行的注释

idaapi.get_cmt(EA, flag)
参数1,地址;参数2,是否获取重复,返回一个注释字符串

  1. cmt = idaapi.get_cmt(addr, 0)
  2. print(cmt)

给地址设置名称

idc.MakeNameEx(ea, name, flags)

地址, 名称字符串, 什么类型的名称

  1. idc.set_name(0x004086B9, "Test1", idc.SN_NOWARN)

扫描特征码

  1. 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' # 绝对地址特征码
  2. def get_decode_func_addr():
  3. for func in idautils.Functions(): # 获得已解析的函数
  4. func_code = idc.GetManyBytes( func, len(decode_func_code) ) # 得到特征码长度的二进制字符串
  5. #print 'func=%08X, func_code=%s, decode_code=%s' %(func,func_code,decode_func_code)
  6. if func_code == decode_func_code: # 比对特征码
  7. print 'decode addr = %08X' %( func )
  8. return func
  9. return idc.BADADDR # 返回一个错误值

通过一个地址得到数据

  1. idc.Byte(EA) # 得到指定地址的一个字节数据
  2. idc.Word(EA) # 得到一个DWORD
  3. idc.QWord(EA) # 得到一个QWORD

通过名称得到函数地址

idc.LocByName(str)

  1. Python>wf_addr = idc.LocByName("WriteFile")
  2. Python>print hex(wf_addr), idc.GetDisasm(wf_addr)
  3. 0x1000e1b8 extrn WriteFile:dword
  4. Python>for addr in idautils.CodeRefsTo(wf_addr, 0):\
  5. print hex(addr), idc.GetDisasm(addr)
  6. 0x10004932 call ds:WriteFile
  7. 0x10005c38 call ds:WriteFile
  8. 0x10007458 call ds:WriteFile

通过地址 得到汇编代码

idc.GetDisasm(ea)

  1. Python>ea = 0x10004932
  2. Python>print hex(ea), idc.GetDisasm(ea)
  3. 0x10004932 call ds:WriteFile
  4. Python>for addr in idautils.CodeRefsFrom(ea, 0):\
  5. print hex(addr), idc.GetDisasm(addr)
  6. Python>
  7. 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’
  1. def get_to_xrefs(ea):
  2. xref_set = set([])
  3. for xref in idautils.XrefsTo(ea, 1):
  4. xref_set.add(xref.frm)
  5. return xref_set
  6. def get_frm_xrefs(ea):
  7. xref_set = set([])
  8. for xref in idautils.XrefsFrom(ea, 1):
  9. xref_set.add(xref.to)
  10. 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 **

标记 ** 为旧版本不支持
这些标志位 可以 叠加 使用 | 运算

  1. Python>pattern = '55 8B EC'
  2. addr = MinEA()
  3. for x in range(0,5):
  4. addr = idc.FindBinary(addr, SEARCH_DOWN, pattern);
  5. if addr != idc.BADADDR:
  6. print hex(addr), idc.GetDisasm(addr)
  7. Python>
  8. 0x401000 push ebp
  9. 0x401000 push ebp
  10. 0x401000 push ebp
  11. 0x401000 push ebp
  12. 0x401000 push ebp
  13. # 可见每次搜索都从指定地址开始, 查找下一个 标志则为 SEARCH_NEXT
  1. Python>pattern = '55 8B EC'
  2. addr = MinEA()
  3. for x in range(0,5):
  4. addr = idc.FindBinary(addr, SEARCH_DOWN|SEARCH_NEXT,
  5. pattern);
  6. if addr != idc.BADADDR:
  7. print hex(addr), idc.GetDisasm(addr)
  8. Python>
  9. 0x401040 push ebp
  10. 0x401070 push ebp
  11. 0x4010e0 push ebp
  12. 0x401150 push ebp
  13. 0x4011b0 push ebp
  14. # 在设置Next标志位以后, 会自动顺延

字符串搜索

idc.FindText(ea, flag, y, x, searchstr) 查找字符串

  1. Python>cur_addr = MinEA()
  2. end = MaxEA()
  3. while cur_addr < end:
  4. cur_addr = idc.FindText(cur_addr, SEARCH_DOWN, 0, 0,
  5. "Accept")
  6. if cur_addr == idc.BADADDR:
  7. break
  8. else:
  9. print hex(cur_addr), idc.GetDisasm(cur_addr)
  10. cur_addr = idc.NextHead(cur_addr)
  11. Python>
  12. 0x40da72 push offset aAcceptEncoding; "Accept-Encoding:\n"
  13. 0x40face push offset aHttp1_1Accept; " HTTP/1.1\r\nAccept: */*
  14. \r\n "
  15. 0x40fadf push offset aAcceptLanguage; "Accept-Language: ru
  16. \r\n"
  17. ...
  18. 0x423c00 db 'Accept',0
  19. 0x423c14 db 'Accept-Language',0
  20. 0x423c24 db 'Accept-Encoding',0
  21. 0x423ca4 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() 获取当前所选的结束地址(选择结尾的下一条地址)

  1. # .text:00408E46 push ebp
  2. # .text:00408E47 mov ebp, esp
  3. # .text:00408E49 mov al, byte ptr dword_42A508
  4. # .text:00408E4E sub esp, 78h
  5. # .text:00408E51 test al, 10h
  6. # .text:00408E53 jz short loc_408E78
  7. # .text:00408E55 lea eax, [ebp+Data]
  8. Python>start = idc.SelStart()
  9. Python>hex(start)
  10. 0x408e46
  11. Python>end = idc.SelEnd()
  12. Python>hex(end)
  13. 0x408e58

idaapi.read_selection() 获得选区, 返回一个元组, 其中包含 获得成功与否, 选择的开始地址和结束地址

  1. Python>Worked, start, end = idaapi.read_selection()
  2. Python>print Worked, hex(start), hex(end)
  3. 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.
  1. Python>print hex(ea), idc.GetDisasm(ea)
  2. 0xa14380 mov ecx, hHeap
  3. Python>hex( idc.Byte(ea) )
  4. 0x8b
  5. Python>hex( idc.Word(ea) )
  6. 0xd8b
  7. Python>hex( idc.Dword(ea) )
  8. 0x6d0c0d8b
  9. Python>hex( idc.Qword(ea) )
  10. 0x6a5000a26d0c0d8bL
  11. Python>idc.GetFloat(ea) # Example not a float value
  12. 2.70901711372e+27
  13. Python>idc.GetDouble(ea)
  14. 1.25430839165e+204

idc.GetManyBytes(ea, size, use_dbg=False) 从一个地址开始获取一个缓冲区

  1. Python>for byte in idc.GetManyBytes(ea, 6):
  2. print "0x%X" % ord(byte),
  3. 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) 修改指定地址的一个双字
  1. # .data:1001ED3C aGcquEUdg_bUfuD db 'gcqu^E]~UDG_B[uFU^DC',0
  2. # .data:1001ED51 align 8
  3. # .data:1001ED58 aGcqs_cuufuD db 'gcqs\_CUuFU^D',0
  4. # .data:1001ED66 align 4
  5. # .data:1001ED68 aWud@uubQU db 'WUD@UUB^Q]U',0
  6. # .data:1001ED74 align 8
  7. # 100012A0 push esi
  8. # 100012A1 mov esi, [esp+4+_size]
  9. # 100012A5 xor eax, eax
  10. # 100012A7 test esi, esi
  11. # 100012A9 jle short _ret
  12. # 100012AB mov dl, [esp+4+_key] ; assign key
  13. # 100012AF mov ecx, [esp+4+_string]
  14. # 100012B3 push ebx
  15. # 100012B4
  16. # 100012B4 _loop: ;
  17. # 100012B4 mov bl, [eax+ecx]
  18. # 100012B7 xor bl, dl ; data ^ key
  19. # 100012B9 mov [eax+ecx], bl ; save off byte
  20. # 100012BC inc eax ; index/count
  21. # 100012BD cmp eax, esi
  22. # 100012BF jl short _loop
  23. # 100012C1 pop ebx
  24. # 100012C2
  25. # 100012C2 _ret: ;
  26. # 100012C2 pop esi
  27. # 100012C3 retn
  28. Python>start = idc.SelStart()
  29. Python>end = idc.SelEnd()
  30. Python>print hex(start)
  31. 0x1001ed3c
  32. Python>print hex(end)
  33. 0x1001ed50
  34. Python>def xor(size, key, buff):
  35. for index in range(0,size):
  36. cur_addr = buff + index
  37. temp = idc.Byte( cur_addr ) ^ key
  38. idc.PatchByte(cur_addr, temp)
  39. Python>
  40. Python>xor(end - start, 0x30, start)
  41. Python>idc.GetString(start)
  42. WSAEnumNetworkEvents

IDA_Python中的文件输入输出

AskFile(forsave, mask, prompt)

  1. # forsave: 值为0或1, 0为启动一个 "打开"对话框, 1为启动一个"保存"对话框
  2. # mask: 字符串, 为对话框的文件过滤器 比如: 过滤".dll" 则传入过滤字符串"*.dll"
  3. # prompt: 字符串, 为对话框的"Title"
  1. import sys
  2. import idaapi
  3. class IO_DATA():
  1. 数据获取方式为在IDA中选中一段数据, 则自动获得 选择的起始地址和结束地址
  2. 作用有限, 需自定义Decode()函数
  3. ```
  4. def __init__(self):
  5. self.start = SelStart() # 所选区间开始
  6. self.end = SelEnd() # 所选区间结尾
  7. self.buffer = '' # 内置缓冲区
  8. self.ogLen = None # 所选长度
  9. self.status = True # 状态
  10. self.run()
  11. def checkBounds(self):
  12. ```
  13. 判断获得的选择区间是否为无效区间
  14. ```
  15. if self.start is BADADDR or self.end is BADADDR:
  16. self.status = False
  17. def getData(self):
  18. '''
  19. 把选择区间内的数据, 保存到类内置缓冲区
  20. '''
  21. self.ogLen = self.end - self.start
  22. self.buffer = ''
  23. try:
  24. for byte in idc.GetManyBytes(self.start, self.ogLen):
  25. self.buffer = self.buffer + byte
  26. except:
  27. self.status = False
  28. return
  29. def run(self):
  30. '''
  31. 类的起始工作函数, 相当于Main()
  32. '''
  33. self.checkBounds()
  34. if self.status == False:
  35. sys.stdout.write('ERROR: Please select valid data\n')
  36. return
  37. self.getData()
  38. def patch(self, temp = None):
  39. '''
  40. 把缓冲区的数据, 替换掉IDB中的数据
  41. '''
  42. if temp != None:
  43. self.buffer = temp
  44. for index, byte in enumerate(self.buffer):
  45. idc.PatchByte(self.start+index, ord(byte))
  46. def importb(self):
  47. '''
  48. 导入一个文件, 初始化内置缓冲区
  49. '''
  50. fileName = idc.AskFile(0, "*.*", 'Import File')
  51. try:
  52. self.buffer = open(fileName, 'rb').read()
  53. except:
  54. sys.stdout.write('ERROR: Cannot access file')
  55. def export(self):
  56. '''
  57. 保存选择的数据到一个文件
  58. '''
  59. exportFile = idc.AskFile(1, "*.*", 'Export Buffer')
  60. f = open(exportFile, 'wb')
  61. f.write(self.buffer)
  62. f.close()
  63. def stats(self):
  64. '''
  65. 状态显示, 当前选择的起始地址和结束地址与长度
  66. '''
  67. print "start: %s" % hex(self.start)
  68. print "end: %s" % hex(self.end)
  69. print "len: %s" % hex(len(self.buffer))
  70. def decode(self):
  71. ```
  72. 解密函数
  73. ```
  74. pass
  1. ---
  2. <a name="sn4glk"></a>
  3. # 使用IDA生成一个程序的 `idb`和 `asm`文件
  4. 经测试我的`IDA7`版本的`idat.exe`程序`QT`框架上有些问题, 可使用`IDA68`版本的`idaw.exe`程序使用参数`-B`命令生成文件全文的`ASM`文件和`IDB`文件
  5. ```python
  6. import os
  7. import subprocess
  8. import glob
  9. paths = glob.glob("*")
  10. ida_path = os.path.join(os.environ['PROGRAMFILES'], "IDA",
  11. "idaw.exe")
  12. for file_path in paths:
  13. if file_path.endswith(".py"):
  14. continue
  15. subprocess.call([ida_path, "-B", file_path])
  1. C:\injected>dir
  2. 0?/**/____ 09:30 AM <DIR> .
  3. 0?/**/____ 09:30 AM <DIR> ..
  4. 0?/**/____ 10:48 AM 167,936 bad_file.exe
  5. 0?/**/____ 09:29 AM 270 batch_analysis.py
  6. 0?/**/____ 06:55 PM 104,889 injected.dll
  7. C:\injected>python batch_analysis.py
  8. Thank you for using IDA. Have a nice day!
  9. C:\injected>dir
  10. 0?/**/____ 09:30 AM <DIR> .
  11. 0?/**/____ 09:30 AM <DIR> ..
  12. 0?/**/____ 09:30 AM 506,142 bad_file.asm
  13. 0?/**/____ 10:48 AM 167,936 bad_file.exe
  14. 0?/**/____ 09:30 AM 1,884,601 bad_file.idb
  15. 0?/**/____ 09:29 AM 270 batch_analysis.py
  16. 0?/**/____ 09:30 AM 682,602 injected.asm
  17. 0?/**/____ 06:55 PM 104,889 injected.dll
  18. 0?/**/____ 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) 设置某个地址分析为代码


获取工作目录

  1. idc.get_idb_path() # 获得当前IDB所在文件路径
  2. idc.get_input_file_path() # 获得创建IDB时, 被分析的目标文件所在路径