0x00 前言
本文主要介绍了在IDA中编写IDApython插件的框架,还有利用IDApython脚本批量分析二进制文件的方法。
0x01 IDApython插件编写
介绍
在IDA中有很多插件供逆向分析人员使用,插件可以实现对二进制文件的自动化分析。通过插件的使用,可以大大提高分析的效率和准确率。同时,IDA也提供了IDApython的编程接口,让我们可以编写自己的插件脚本,实现自定义的插件功能。
IDApython主要有三个模块(这个我忘记在哪里看的了,之前摘的笔记,侵删):
- idc:兼容IDA Pro中idc函数的模块;
- idautils:逆向分析中常用的一个模块,大多数处理方法都是需要依托于这个模块;
- idaapi:该模块允许使用者通过类的形式,访问更多底层的数据;
框架
插件的代码可以分为两部分,这两个部分在IDA安装目录下的 plugins
和python
文件夹中:
- plugins目录下的插件类,用于实例化插件对象,以及作为插件调用的入口,用来调用自定义的插件模块中的功能代码;
- 另一个是在python目录中定义的插件模块,实现了自定义的插件的功能;
代码
文件结构为:
- IDA Pro安装目录
- plugins
listFuncPlugin.py
- python
listFunc
__init__.py
listFuncImpl.py
- plugins
让我们从示例代码来理解,假设我们要实现一个列举出二进制文件中所有函数名的插件,在plugins文件夹中创建文件listFuncPlugin.py
,在python文件夹下,创建一个文件夹叫listFunc
,在listFunc
文件夹下,创建__init__.py
,listFuncImpl.py
。
首先看一下插件类listFuncPlugin.py
的代码。在该文件中,需要实现插件类ListFunc
,以及一个PLUGIN_ENTRY
函数,该函数用于生成一个插件实例对象。在插件类中,除了定义插件的名称,快捷键等信息,还需要实现三个方法:
init()
方法:初始化操作,比如打印提示信息,导入功能模块;run()
方法:插件的入口函数,调用功能模块的函数;term()
方法:结束时调用的方法;
# -*- coding:utf-8 -*-
# ======= import =======
import idautils
import idaapi
import idc
from datetime import datetime
class ListFunc(idaapi.plugin_t): # 继承 idaapi.plugin_t
"""
插件类
"""
flags = idaapi.PLUGIN_UNL
comment = "List all functions in this binary file."
wanted_name = "listfunc" # 插件的名称,在IDA界面导航栏中显示 Edit->Plugins->myplugin
wanted_hotkey = "Alt-F6" # 插件的快捷键
help = "Coming soon..."
def init(self):
"""
初始化方法
"""
idaapi.msg(">>> My plugin starts. {0}\n".format(datetime.now()))
# 导入python目录下的功能模块
idaapi.require("listFunc")
idaapi.require("listFunc.listFuncImpl")
return idaapi.PLUGIN_OK # return PLUGIN_KEEP
def run(self, arg):
listFunc.listFuncImpl.main() # 注意这里的调用方式是从python中模块的文件夹开始
def term(self):
idaapi.msg(">>> My plugin ends. {0}\n".format(datetime.now()))
def PLUGIN_ENTRY():
"""
实例化插件对象
"""
return ListFunc()
接下来在python文件夹的listFunc
目录中,先创建__init__.py
文件,因为IDApython是Python2.7,所以添加该文件表明该目录是一个模块,如果没有这个文件的话,在运行插件的时候会报错:No module named xxx
;
然后编写目录下的listFuncImpl.py
,这里的main()方法就是在插件类中run()方法调用的函数。
# -*- coding:utf-8 -*-
# ======= import =======
import idautils
import idaapi
import idc
from datetime import datetime
def main():
for i, func in enumerate(idautils.Functions()):
func_name = idc.GetFunctionName(func) # 函数名
print("{0} function name is: {1}".format(i, func_name))
if __name__ == '__main__':
main()
到这里,插件的实现就完成了,接下来只需要用IDA打开二进制文件以后,按下插件的快捷键,就能执行自定义的插件了。
0x02 脚本批量分析
介绍
既然我们可以用IDApython编写的脚本对二进制文件进行自动化分析,那么,在实际情况中,我们可能会遇到需要分析很多个二进制文件的情况。在这个应用场景下,就需要用IDA提供的命令行接口来调用IDApython脚本,进行二进制文件的批量分析处理。
思路
思路就是,先用python编写一个调用脚本,在该脚本中,利用python的命令行接口,调用IDApython的分析程序,对遍历得到的二进制文件进行分析处理。
代码
还是一样,通过代码来理解如何使用哈。假设我们这里实现的是一个对很多二进制文件进行分析,得到二进制文件中的函数名。
首先是分析脚本,analysis.py
。需要注意的是,由于分析脚本是被调用的,所以要在文件末尾添加if __name__=='__main__'
,从而能够调用该文件中的分析方法。
def analysis():
# 这里是分析的代码
pass
def main():
"""
控制器
"""
idc.Wait() # 等IDA分析完后才执行
analysis()
idc.Exit(0) # 关闭IDA
if __name__ == "__main__":
main()
然后就是调用脚本,run.py
。
# -*- coding:utf-8 -*-
# =======Import =======
import os
import subprocess
dir_path = "D://transfer/" # 原始数据的文件夹
ida64_path = "D://ProgramFiles/IDA/ida64.exe" # ida64的路径
ana_file = "D://listFunc/analysis.py" # 分析文件的路径
def run():
for root, dirs, files in os.walk(dir_path):
for file_name in files:
file_path = os.path.join(root, file_name)
cmd = "{0} -LD:/mylog.log -c -A -S{1} {2}".format(ida64_path, ana_file, file_path)
p = subprocess.Popen(cmd)
p.wait()
if __name__ == "__main__":
run()
-L
表示输出的日志路径-c
表示对二进制文件进行反汇编-A
表示自动模式,IDA不会提示一些信息,会自动处理,-S
后面的路径是分析脚本的路径- 具体内容可以查看参考资料4
其中,-L
和-S
与后面的路径之间是没有空格的。
比如,cmd
为D://ProgramFiles/IDA/ida64.exe -LD:/mylog.log -c -A -SD://listFunc/listFuncImpl.py D://transfer/m64-O0\aes-x86_64.o
调试的小技巧: 由于我们是通过命令接口进行调试的,这样有一个很不方便的地方就是,如果分析脚本中出错了,很难拿到报错信息。这里我提供两个思路,第一个就是在run.py
中代码所示的那样,将运行结果输出到log文件中。还有一种思路就是在分析脚本中,使用python中的traceback
模块捕获信息。
0x03 后记
明天晚上就要回学校了,下午写了这篇最近一直想总结,但是没时间做的内容。在这边实习了大半年,学到了很多知识。组里的氛围真的很好,师兄师姐和导师都很nice! 在这边也意识到,科研没有自己想象中的那么理想,不过真正的英雄主义就是认清科研的本质并热爱她?希望自己研究生能在专业方面做出点成绩吧。好好努力,好好耍哈哈哈!
烦心的毕设快要结束了,红公保庇我能顺利毕业吧!还有两三个月就要毕业了,我的马鸭!高中毕业典礼的场景还历历在目,不瞒你说我是最经受不住分别的人了。接下来应该会写一篇大学四年的总结吧。