中文译名:基于面向序列的神经模型的细粒度编译器识别
作者:ZHENZHOU TIAN
单位: 西安邮电大学
国家: #中国
年份: #2021年
来源: #IEEE_ACCESS
关键字: #编译 #神经网络 #二进制
代码地址:zztian007/NeuralCI (github.com)
笔记建立时间: 2023-02-23 15:37
摘要
- 现有的大多数方法都采用基于特征匹配或基于机器学习的策略来识别编译器细节,在检测精度或粒度上都有限制。
- 在这项工作中,我们提出了 NeuralCI (基于神经建模的编译器识别) 来推断这些编译器细节,包括编译器家族、优化级别和单个函数的编译器版本。
- 其基本思想是建立面向序列的神经网络来处理使用轻量级函数抽象策略生成的规范化指令序列。
- 为了评估 NeuralCI 的性能,构建了一个由从 19 个广泛使用的现实项目中收集的 854, 858 个独特函数组成的大型数据集。
- 实验表明,NeuralCI 识别编译器族的平均准确率为 98.6%,
- 识别优化级别的平均准确率为 95.3%,
- 识别编译器版本的平均准确率为 88.7%,
- 识别编译器族和优化级别的平均准确率为 94.8%,
- 同时识别所有编译器组件的平均准确率为 83.0%,在检测精度和全面性方面均优于现有的功能级编译器识别方法。
1引言
-
编译器识别方面的研究相对较少,主要分为两类:
- 基于签名匹配的方法[6]-[8]
- 在一些逆向工程工具中实现,如 IDA[6]和 PEiD[8],通过匹配通用签名和刚性签名的语料库来执行整个程序级别的识别。
- 这些方法的缺点是在构造足够好的特定于编译器的签名时需要严格的专业知识,以及它们的粗标识粒度。
- 和基于学习的方法[27]-[29],[39]。
- 后者将编译器识别定义为机器学习任务,训练模型以捕获特定于编译器的模式,并根据以前未见过的二进制文件推断编译器细节。
- 对于这类方法,语法或结构特征是基于人工定义的模板提取的,例如 idioms[29],它是带有通配符的短指令序列,或者 graphlets[28],它是 CFG (控制流图) 中的小子图。作为典型的基于特征工程的方法,其有效性在很大程度上取决于专家定义的特征提取模板的质量,而专家定义的特征提取模板需要更多的领域特定知识。
- 基于签名匹配的方法[6]-[8]
-
具体来说,我们为典型的卷积神经网络 (CNN) 和循环神经网络 (RNN) 结构提供标准化的汇编指令序列,以训练分类模型,用于推断编译器族、优化级别和编译器版本。我们的直觉是基于这样的观察: 共出现的指令及其在短指令序列中的顺序形成了足够好的信号,可以区分不同的编译器或优化级别,这可以被神经模型基本上捕捉到。
2 问题定义和设计概述
A 问题概述
略
B 问题定义
- 定义 1:给定一个二进制形式的个别函数 f,并剥离其调试和符号信息,我们通过一组学习模型 M 来推断编译这个函数的编译器设置 D。
- 操作对象是单个函数
- 设置 D 包括编译器系列、优化级别、编译器版本或它们的组合(例如编译器系列和优化级别都有)
C 设计概览
- 训练阶段 (上图)
- 第一步是构建一个由标记函数组成的高质量数据集
- 第二步将每个原始函数作为输入,并通过函数抽象模块中实现的轻量级抽象策略输出规范化的指令序列。
- 第三步将这些归一化序列及其真实值标签输入到基于神经网络的分类模块中,训练编译器识别模型。
- 检测阶段 (下图)。
- 检测阶段读入一个单独的函数,用函数抽象处理它,并利用训练过的模型来产生预测。
3 函数抽象
- 函数的抽象表示方法包括使用原始字节序列、汇编指令序列或控制流图
- 因为有相关工作表明短汇编指令序列成功地捕获了编译器相关的特性,所以我们选择使用函数体中的汇编指令序列作为每个函数的表示。
- 函数 f 表示为一个序列 S f = { i n s 1 , i n s 2 , ⋅ ⋅ ⋅ , i n s n } Sf = \{ins1, ins2,···,insn\} Sf={ins1,ins2,⋅⋅⋅,insn},其中 n 表示函数内的指令数
- 每个汇编指令 insi 由一个操作码 (即助记符) 和一个有序的操作数列表组成
- 同样,该序列确保 ∀ i , j ∈ [ 1 , n ] , a d d r ( i n s j ) > a d d r ( i n s i ) ∀i, j∈[1, n], addr (ins_j) > addr (ins_i) ∀i,j∈[1,n],addr(insj)>addr(insi) 如果 j > i,其中 addr (insi) 返回 insi 的地址。
- 但是同样有相关工作认为直接处理原始汇编指令是不明智的。
- 本文希望捕获反映编辑器细节的特性而不是函数功能
- 使用函数中出现的所有指令可能会让我们沉浸在太多的功能细节中,这可能会相应地增加表示学习的复杂性,降低指令嵌入阶段的嵌入质量 (因为保留了太多不同的指令),也会分散后续神经网络训练的注意力。另一方面,对指令的过度规范化将引入大量无意的人为偏差,并导致某些微妙但重要的特征的丢失
- 因此选择使用轻量级抽象策略来处理原始指令
- 助记符保持不变。
- 操作数中的所有寄存器保持不变。
- 操作数中的所有基内存地址都替换为符号 MEM。
- 所有绝对值低于特定阈值 (在当前设计中设置为 5000) 的孤立的立即数都保持不变,而在所有其他情况下,操作数中的立即数都用符号 IMM 代替。
4 编译器识别的神经模型
A指令嵌入
- 对于神经网络的输入,我们需要将汇编指令序列转化为数值向量
- 首先使用词嵌入为每条唯一的指令分配一个向量,然后在此基础上对整个指令序列建模和表示。
- 独热编码太稀疏,在计算上不可行,并且通常需要与后续的神经网络一起联合学习,使得所学习的单词语义显着具有任务特定性。
- NeuralCI 利用流行的 skip-gram 模型,以独立且无监督的方式学习更紧凑的向量表示,这些向量表示具有指令共现关系和词汇语义,以便在其他二进制分析任务中可以重复使用学习的向量。
- 具体而言,我们将每个基本块视为一个句子,并将基本块中的每个规范化指令视为一个单词,并将我们二进制收集的所有基本块馈送到 skip-gram 模型中,以通过最小化观察指令邻域(在窗口 w 内)的损失来学习每个唯一指令的 d 维向量,以其当前嵌入作为条件。skip-gram 的目标函数可以定义为
- 我们训练嵌入模型 100 个 epoch,学习率为 0.001,上下文窗口大小 w 为 5。
B 神经网络模型
Skip-gram 模型的限制:
- 为每个指令分配一个静态嵌入向量,不是上下文感知
- 由于指令序列是从函数中抽象出来的,因此它们可能不仅享有本地指令相关性,还可能具有全局或 long-range 指令依赖性。(啥意思)
需要靠序列学习模型来更好地从指令序列中捕获表达编译器特定模式和特征的信息以进行编译器识别。
1)CNN
CNN 能够关注那些在短序列中频繁出现的指令。在我们的模型公式中,我们进一步利用了不同的内核大小滤波器来全面提取不同指令 gram 之间的相互作用的显着特征,以捕捉编译器的行为。
在指令序列的基础上,将每个指令序列首先转换为原始特征矩阵
A
∈
R
l
×
d
A \in R^{l \times d}
A∈Rl×d,其中
l
l
l 是序列长度,
d
d
d 是指令嵌入维度。
其中,
e
i
∈
R
d
e_i \in R^d
ei∈Rd 是序列中指令
i
n
s
i
ins_i
insi 的对应嵌入。然后,在卷积层中,采用了
k
k
k 个形状为
n
×
d
n \times d
n×d 的卷积滤波器对原始特征矩阵
A
A
A 进行卷积操作,得到新的特征矩阵
c
∈
R
(
l
−
n
+
1
)
×
k
c \in R(l-n+1) \times k
c∈R(l−n+1)×k,其中
n
n
n 表示卷积核大小。为了提取特征模式的不同视图,我们使用大小分别为 2、3 和 4 的不同卷积核将
A
A
A 进行卷积(分别类比于 2、3 和 4 个指令克隆),然后通过 1D 最大池化层进行维数降低。最终的表示通过稠密层连接起来,馈送到 softmax 层进行编译器预测。
在输入层和卷积层之间引入了一个注意力层,引入这样一个注意力层的直觉是为了捕捉长期的上下文信息和非连续指令之间的相关性。
(这部分注意力机制看不懂,挖个坑,先去补补课)
对于两种注意机制,NeuralCI 将从注意力层输出的向量与初始嵌入向量串联为新的表示形式,以进一步处理卷积和后续层。为了简化起见,我们在实验评估中用 NeuralBS CNN,NeuralSD CNN 和 NeuralAD CNN 来表示基础 CNN,带有缩放点积注意力的增强 CNN 和带有附加注意力的增强 CNN。
2)RNN
- RNN 的特点在于可以学习顺序依赖性,可以捕捉到指令之间的 long-range 依赖关系。
- 门控循环单元用于解决 RNN 的梯度消失/爆炸问题
- 上图是 RNN 结构,通过 GRU 单元对输入矩阵进行读取,在每个时间步 i 上生成隐藏向量状态 hi 进行各种非线性变换。
- 在 GRU 的基础上改进的双向 GRU(BiGRU)通过正向和反向状态捕获先前和未来时间步长特征,利用相互反转的两个 GRU 单元处理序列。捕获指令序列中的前向和后向顺序依赖性和全局上下文信息。
- 在正向和反向读取整个输入序列之后
- A 结构将最后的时间步的隐藏状态 hi 送入后续的密集层和 softmax 层
- B 结构和 C 结构引入了注意力层,关注部分信息指令(可能更重要的用于编译器识别)而不是平等地关注所有指令。B 使用 CNN 中的两种注意力机制中的一种生成注意力向量。C 选择了另一种注意力机制,引入了每个句子中的单词的重要性机制
- 为了使这些 RNN 模型可区分,我们在以下实验评估中使用 NeuralBS GRU,NeuralSD GRU,NeuralAD GRU 和 NeuralQU GRU(第三种注意机制最令人印象深刻的特征是引入外部查询向量,因此我们将其简称为 QUerybased-attention)。
V 实验和评估
A 数据集的构建
- 使用三种不同的编译器,GCC(4.7,4.8,4.9,5.5,6.5,7.4),Clang(3.8,5.0)和 ICC 19.0,作为工具链设置来编译每个项目,并使用不同的编译器优化级别(O0,O1,O2,O3)。
- 然后使用IDA Pro从每个二进制文件中标识和提取函数。仅包含几条指令的存根函数等微不足道的函数被删除。在当前设置中,包含少于10条指令的函数被视为微不足道的函数。
- 在训练阶段,为避免神经模型看到与测试阶段中的函数太相似的函数,仅保留唯一的函数。具体而言,如果一个函数与其他函数具有相同的标准化指令,则认为该函数是冗余的。每个剩余的函数都用用于编译包含该函数的二进制文件的编译器设置进行标记。
使用这些设置,作者构建了一个由 4, 810 个二进制文件中的 854, 858 个唯一函数组成的数据集,每个函数平均包含约 260 条指令。函数大小的分布如图 6 所示,其中 50%的函数包含少于 100 条指令,近 90%的函数包含少于 500 条指令。
B 实现细节和实验设置
我们已经实现了 NeuralCI 作为原型工具。它利用 IDA Pro 解析二进制文件以获取函数及其原始汇编指令。函数抽象模块使用 Java 实现,神经建模模块使用 Python 和 TensorFlow 框架实现。使用 gensim 提供的 skip-gram 实现生成指令嵌入向量,嵌入大小 d 设置为 100。指令序列的最大长度设置为 500,以确保覆盖数据集中近 90%的函数的全部语义。基于卷积神经网络的模型中卷积滤波器的数量设置为 128,GRU 隐藏状态向量的维度也是 128。
对于实验设置,我们将数据集随机分为训练集、验证集和测试集,分别按 80%、10%和 10%的比例。神经网络模型使用 Tesla V100 GPU 卡进行训练,批处理大小为 128,初始学习率为 0.001(当验证损失连续 5 个 epoch 没有改善时,将学习率除以 10),使用 Adam 优化器。对于每个 epoch,训练样本都会被打乱,并计算验证集的准确率。此外,还使用 EarlyStopping 机制,在验证准确率不再上升的 epoch 后停止训练,以避免过度拟合、不收敛等问题。最后,我们将具有最佳准确率的模型作为最终模型,针对性能指标(包括准确率、精度、召回率和 f1-score)在测试集上进行进一步评估。
评估
我们分别评估了 NeuralCI 在识别编译器家族、优化级别、编译器版本和编译器设置组合方面的性能,并报告了神经网络模型之间的比较结果,以及与支持检测相应编译器设置的现有函数级方法的比较结果。
1)识别编译器系列的性能
如表 2 所示,NeuralCI 模型在识别编译器家族方面实现了相当高的精度和 f1-score。在编译器族识别任务中,NeuralSD GRU 模型表现最佳,准确率为 98.7%,f1-score 为 0.987。
2)识别优化级别的性能
- 将 4 个优化级别压缩为 2 个类别:“低”和“高”。
- O0 和 O1 被视为低优化类(OL),而 O2 和 O3 被视为高优化类(OH)。
- 此外,使用将优化级别压缩为 O0,O1 和 OH 的 3 级拆分方式测试模型的性能。
- 结果表明,NeuralCI 模型在 2 级和 3 级优化压缩情况下都表现出相对良好的检测结果,并且跨模型观察到很少的性能差异。
- 令人惊讶的是,对于某些模型,NeuralCI 在 3 级优化选项识别任务上的表现甚至比 2 级任务还要好。
- NeuralCI 在识别优化级别方面的表现因编译器而异,在 GCC 和 Clang 上观察到最佳和最差的表现。编译器优化级别的不同检测难度意味着不同的编译器采用不同的定义优化级别的方式。
3)识别编译器版本的性能
3)识别编译器设置组合的性能
- 从二进制文件中检测编译器设置组合通常可以通过联合应用多个模型来实现,每个模型针对不同的设置部分。
- 首先可以使用用于编译器系列识别的训练模型来检测编译该函数的系列,然后使用相应于已识别的系列的优化级别识别模型进一步检测优化级别。
- 另一种方法是训练一个可以同时检测这些设置的单个模型。
- 为了检查 NeuralCI 是否能够胜任这项具有挑战性的任务,我们首先评估其识别编译器系列和优化级别的性能。如表 6 所示的结果显示,NeuralCI 中的所有模型都表现出相对良好且类似的检测性能。此外,表 7 报告了 NeuralCI 在同时识别所有编译器设置组合方面的结果,其中在不同模型之间观察到的准确率在 81.5%至 83.8%之间。