一种由二进制代码转换源代码的方法
技术领域
本发明涉及计算机软件逆向分析及网络安全领域,具体涉及一种由二进制代码转换源代码的方法。
背景技术
随着社会对软件需求的不断增长,必须确保软件能够保护用户的敏感数据并可靠地执行关键功能。在网络安全领域,为了确保软件的安全性,网络安全专家通常使用逆向工程 (Reverse Engineering, RE)来分析二进制软件,以查找软件漏洞并了解恶意软件。但是对于单个程序,此过程可能需要数天、数周或数月,随着软件使用量的增长,这对二进制分析提出了重大挑战。
二进制分析,是指从可执行的二进制文件出发,运用解密、反汇编、系统分析、程序理解等多种计算机技术,对软件的结构、流程、算法、代码等进行逆向拆解和分析,推导出软件产品的源代码、设计原理、结构、算法、处理过程、运行方法及相关文档等。软件的逆向分析有多种应用,它对恶意软件分析及软件漏洞分析起到至关重要的作用。但是,这是一项很难学习的技能,需要研究人员花费大量的时间才能掌握一些软件分析的基本技能,即使是经验丰富的专业人士,也往往需要花费很长时间才能获得有意义的结果。
目前,二进制分析的过程在很大程度上仍然依赖于从事逐行分析的分析师。分析师在进行软件逆向工作时一般经过三个阶段:优先级确定,假设生成和实验。人类逆向工程师需要根据经验猜测软件的执行过程,然后遵循这些过程中的流程,使用过程间模式,最后将所有这些可能的过程组合在一起,以形成对被检查可执行文件的目的和用法的全局理解。虽然对于不同的二进制任务逆向分析过程会有所差异,但研究人员仍无法克服不同逆向任务中的低效率的问题。
一些专家已经在某些领域内的开展了二进制分析的研究,如安全分析和软件开发。起初,二进制分析中的许多工作是基于认知的,即需要分析师通过经验来推断软件的运行过程,这需要分析人员对二进制代码进行溯因推理(Abductive Reasoning):分析人员需要根据观察产生假设,再根据假设推断代码的功能,然后寻找信息来验证或推翻他们的假设或推导的结论。虽然,这样的方法准确、精度高,但是当源代码使用不同的编译器、或使用优化选项以及选择不同的目标操作系统和 CPU 架构时,生成的二进制代码都可能会发生显著变化。除此以外,代码混淆也使得软件逆向变得更加棘手。源代码程序员和开发人员通常为了保持软件的健壮性可扩展性,他们通常会在源代码中使用有意义的变量或符号来帮助他们理解程序行为,而软件逆向后这些有意义的变量符号则会被优化,分析人员只能分析机器可读的二进制代码,而二进制代码去除了有意义的元素,如变量和函数名称,这也在很大程度上提高了人工分析二进制文件的难度。
最近,基于机器学习的二进制分析技术也被提出来自动重建二进制的代码表示。虽然传统的人工分析方法在特定任务中显示出很高的准确性,但基于机器学习的方法在快速变化的计算环境中往往更有优势:只要提供训练数据,一个模型就可以在多个平台和架构上重用,并且可以随着新输入数量的不断增加而不断改进。然而,机器学习中方法通常采用图匹配算法来对二进制代码和源代码进行匹配,这种方法速度慢且不准确,并且现有的利用机器学习的方法仍然专门用于解决某个领域的问题,即为不同类型的二进制分析绘制模型。近年来,基于神经网络的方法取得了很大的进展,基于深度学习的方法大多将二进制代码表示为带有人工选择特征的控制流图(Control Flow Graph, CFG),然后采用图神经网络(Graph Neural Network, GNN)计算图嵌入。虽然这些方法是有效和高效的,但它们不能充分捕获二进制代码的语义信息。
虽然二进制分析技术已有一定的发展,但是,由于导致现有技术仍存在几点局限:首先,现有许多研究都是基于认知的,即需要根据人工经验来对二进制文件进行分析,而由于二进制文件的特殊性,面对不同的二进制文件往往需要跟换不同的分析过程,并且二进制代码中通常去除了有意义的元素,如变量和函数名,这就导致在分析一个二进制文件时,分析人员往往需要花费大量时间才能获得一个有意义的结果。其次,基于机器学习或者深度学习的方法尽管已经取得了很多成就,但仍有一些重要的事情没有被考虑进去。比如使用人工选择特征的低维嵌入来表示二进制代码,这会导致大量语义信息的丢失。最后,虽然某些二进制分析工具支持二进制代码的自动化分析,但是也仅仅是为了方便分析人员阅读二进制代码,将二进制代码的结构形式转换成某些高级语言的结构形式,虽然这种分析方法考虑到了高级编程语言的可阅读性,尝试将二进制代码转换为高级编程语言,但是这种自动化分析仍局限在代码的结构转换上,未能再深入到代码的语义转换。而代码语义往往包含了二进制文件的某些功能,能更好地为二进制分析提供帮助。
发明内容
本发明为了克服以上技术的不足,提供了一种基于双向自编码器和自回归解码器的由二进制文件生成源代码方法。
本发明克服其技术问题所采用的技术方案是:
一种由二进制代码转换源代码的方法,包括如下步骤:
(a)收集若干不同编程语言的源代码构成数据集,其中为源代码集合,为源代码集合中第个源代码文本表示,为源代码文本总数,为编程语言集合,为第个源代码文本表示所对应的编程语言;
(b)使用编译器将第个源代码文本表示编译成其对应的二进制文件,构建二进制文件集合;
(c)提取第个二进制文件的中间表示,将其中间表示记为,得到二进制中间表示集合;
(d)构建包含源代码、编程语言和中间表示的数据集;
(e)拼接第个源代码文本表示、第个源代码文本表示所对应的编程语言以及第个二进制文件的中间表示,构建序列和序列,其中为插入的固定文本,为分隔标记,为分类标记,为结束标记;
(f)构建一个模型;
(g)定义预训练任务和预训练任务;
(h)预训练任务将序列进行破坏,为序列添加噪声,将破坏后的序列作为模型的输入,模型输出替换的程序指令;
(i)预训练任务将序列输入模型中,模型通过第个二进制文件的中间表示和其源代码所对应的编程语言作为提示输出二进制的第个源代码文本表示;
(j)计算得到总损失函数;
(k)使用梯度下降法最小化预训练任务的总损失函数,通过反向传播更新模型的参数,更新模型的参数直至收敛,得到训练后的模型;
(l)将待测试的二进制文件提取出其二进制文件的中间表示,构建模型的输入序列,将序列输入至模型,输出得到二进制文件的对应编程语言编写的源代码。
进一步的,步骤(a)中表示的编程语言为C++或Java或PHP。
优选的,步骤(c)使用 LLVM-IR工具提取第个二进制文件的中间表示。
进一步的,步骤(c)使用Jlang工具或Polyglot工具或RetDec工具提取第个二进制文件的中间表示。
优选的,步骤(f)中模型为seq2seq Transformer模型。
进一步的,步骤(f)中模型为长短期记忆网络。
进一步的,步骤(h)包括如下步骤:
(h-1)将序列中的第个源代码文本表示以及第个二进制文件的中间表示随机抽样程序指令并替换为特殊符;
(h-2)随机将序列中的第个源代码文本表示或第个二进制文件的中间表示的程序指令片段用一个特殊符替换;
(h-3)将破坏后的序列作为模型的输入,模型输出被替换的程序指令。
进一步的,步骤(j)包括如下步骤:
(j-1)通过公式计算得到预训练任务的目标函数,式中为所有源代码和中间表示的程序指令集合,为预训练任务中模型输出的程序指令,为为原始程序指令的概率,为在中间表示中被特殊符替换的程序指令,为在源代码文本中被特殊符替换的程序指令;
(j-2)通过公式计算得到预训练任务的目标函数,式中为预训练任务中模型输出的源代码程序指令,为为源代码原始程序指令的概率;
(j-3)通过公式计算得到总损失函数。
本发明的有益效果是:(1)提供了一种端到端的二进制文件到源代码转换的方法,通过利用二进制文件的中间表示,效地缓解了不同平台下二进制文件的差异;(2)设计预训练任务使得模型能够学习到二进制文件的中间表示和源代码的上下文语义联系;(3)引入提示学习的思想,在无需人工干预的情况下,生成二进制文件的任意编程语言编写的源代码,并且无需再对预训练模型的下游任务进行微调,优化了模型参数,同时也简化了二进制文件分析过程,提高了二进制文件分析效率,也能为软件分析人员提供多语言的源代码支持。
附图说明
图1为本发明的方法流程示意图;
图2为本发明的预训练任务进行模型预训练的流程示意图;
图3为本发明的预训练任务进行模型预训练的流程示意图。
具体实施方式
下面结合附图1、附图2、附图3对本发明做进一步说明。
本发明提供一种基于双向自编码器和自回归解码器的由二进制文件生成源代码方法。首先使用大规模二进制代码中间表示训练预训练模型,预训练有助于模型有效的捕捉二进制中间表示程序指令上下文中的结构和语义相关性,然后基于提示学习的思想利用二进制中间表示生成任意高级编程语言编写的源代码。具体的,包括如下步骤:
(a)收集若干不同编程语言的源代码构成数据集 ,其中为源代码集合,为源代码集合中第个源代码文本表示,为源代码文本总数,为编程语言集合,为第个源代码文本表示所对应的编程语言。
(b)使用编译器将第个源代码文本表示编译成其对应的二进制文件,构建二进制文件集合。
(c)提取第个二进制文件的中间表示(Intermediate Representation,IR),将其中间表示记为,得到二进制中间表示集合。
(d)构建包含源代码、编程语言和中间表示的数据集。
(e)拼接第个源代码文本表示、第个源代码文本表示所对应的编程语言以及第个二进制文件的中间表示,构建序列和序列,其中为插入的固定文本,两端文本段之间由一个特殊分隔标记连接,每个序列的第一个和最后一个程序指令总是分别用一个特殊的分类标记和一个结束标记填充。
(f)构建一个模型。模型包含一个双向的编码器和一个自回归的解码器。模型的编码器部分为一个双向特征表示的自编码模型,包含N层Transformer block;而解码器部分为一个单向特征表示的自回归模型,也包含N层Transformer block。Transformer的编码器可以对二进制中间表示和源代码进行嵌入,而Transformer的解码器可以用于生成二进制中间表示所对应的源代码。Transformer基于自注意机制,已成为一种流行的源代码嵌入模型。
(g)定义预训练任务和预训练任务。
(h)预训练任务将序列进行破坏,为序列添加噪声,将破坏后的序列作为模型的输入,模型输出替换的程序指令。目的是使模型学习程序指令在上下文中的语义关系。
(i)如附图3所示,预训练任务将序列输入模型中,模型通过第个二进制文件的中间表示和其源代码所对应的编程语言作为提示输出二进制的第个源代码文本表示。预训练任务的目标是通过二进制中间表示生成某种高级编程语言编写的源代码,目的是使模型学习二进制中间表示和其对应源代码的相关性。
(j)计算得到总损失函数。
(k)使用梯度下降法最小化预训练任务的总损失函数,通过反向传播更新模型的参数,更新模型的参数直至收敛,得到训练后的模型。
(l)将待测试的二进制文件提取出其二进制文件的中间表示,构建模型的输入序列,将序列输入至模型,输出得到二进制文件的对应编程语言编写的源代码。
在本发明的一个实施例中,步骤(a)中表示的编程语言为C++或Java或PHP。
在本发明的一个实施例中,步骤(c)使用 LLVM-IR工具提取第个二进制文件的中间表示。
除此之外,所用的提取工具可用其他具有相似功能的二进制提取工具代替,如使用Jlang工具或Polyglot工具或RetDec工具提取第个二进制文件的中间表示。
在本发明的一个实施例中,模型为seq2seq Transformer模型,步骤(f)中模型除了使用的Transformer 模型外也可用其他循环神经网络替代,长短期记忆网络(LongShort-Term Memory)等。
如附图2所示,预训练任务使用两种方法模块原始序列,为序列添加噪声,具体的,步骤(h)包括如下步骤:
(h-1)第一种方法为:将序列中的第个源代码文本表示以及第个二进制文件的中间表示随机抽样程序指令并替换为特殊符。例如在一个具体的实现中,将中"load"指令替换为,中"for"指令替换为特殊符。
(h-2)第二种方法为:随机将序列中的第个源代码文本表示或第个二进制文件的中间表示的程序指令片段用一个特殊符替换。例如在一个具体的实现中,假设被替换的程序指令长度服从泊松分布()的随机抽样,从选取长度为3的程序指令"i += 1",用特殊符替换程序指令"i += 1"。如果此时被替换的程序指令长度为0,则相当于直接往原始序列中插入一个特殊符。
(h-3)将破坏后的序列作为模型的输入,模型输出被替换的程序指令。
在本发明的一个实施例中,步骤(j)包括如下步骤:
(j-1)通过公式计算得到预训练任务的目标函数,式中为所有源代码和中间表示的程序指令集合,为预训练任务中模型输出的程序指令,为为原始程序指令的概率,为在中间表示中被特殊符替换的程序指令,为在源代码文本中被特殊符替换的程序指令。
(j-2)通过公式计算得到预训练任务的目标函数,式中为预训练任务中模型输出的源代码程序指令,为为源代码原始程序指令的概率。
(j-3)通过公式计算得到总损失函数。
最后应说明的是:以上所述仅为本发明的优选实施例而已,并不用于限制本发明,尽管参照前述实施例对本发明进行了详细的说明,对于本领域的技术人员来说,其依然可以对前述各实施例所记载的技术方案进行修改,或者对其中部分技术特征进行等同替换。凡在本发明的精神和原则之内,所作的任何修改、等同替换、改进等,均应包含在本发明的保护范围之内。