\u200E
有了它,你还担心恶意软件吗?
发布日期:2021-08-04T11:28:00.000+0000 浏览量:904次


目前,Android是当前全球市场占有率最高的智能手机操作系统,但由于其开源性,Android系统的安全事件也层出不穷。因此,Android软件中恶意代码的检测和分析已经成为一个至关重要的课题。本文提出一种基于文本卷积神经网络的分类模型在Android恶意软件静态检测上的方法,使用飞桨开源框架进行项目实现,其中运用了词典压缩的词典构造方法,并使用文本卷积网络进行了不同尺寸和数量的滤子组的对比实验。实验结果显示,使用较小尺寸、较多数量的滤子组能达到更高的准确率。


相关研究




传统的Android恶意软件静态分析,通常是通过将Apk文件反编译为Java源文件或者Jar文件包,然后对其进行源代码级的分析。由于恶意软件作者会对代码运用混淆和加密的方法,在人工进行静态分析过程中,代码往往难以理解,很难对特征进行标注和筛选。

近几年国内外多有讨论采用机器学习和深度学习来进行Android恶意软件静态分析检测,目前大多都是依靠权限、API以及资源文件等作为特征来训练识别模型,这些特征展现了Android软件的部分特性,但不足以说明Android软件运行时所具备的特性,甚至在判别恶意软件与良性软件过程中选取的特征有相当大的交叉部分,会造成很高的误判率。受到McLaugh等人提出了一种使用卷积神经网络(CNN)来分析Android软件的方法[1]的启发,他们使用符号的局部模式(称为N-Gram)来提取反编译后的smali文件的特征。

本文基于张等人的论文[2]所提出的理论,改进了该方案。论文提出了一种对句子分类敏感的卷积神经网络,称它为文本卷积神经网络,它能够自动对句子中的各个位置进行评分,并选取出评分最高的区域。这样即使增大n-gram的尺寸也不会增加时间复杂度,增加n-gram的数目其时间复杂度增长也是O(n),而不是O(an)。考虑卷积网络感受野大小问题进行了不同尺寸、不同数量的滤子组的对比实验,最终发现,该方案有着很高的准确率,并且使用越小尺寸、数量越多的滤子组能够获得越高的准确率。



方案






3.1  数据准备


Android应用数据来自加拿大网络安全研究所的CICMalDroid 2020[3],该Android应用数据集收录了包括4033个良性软件(Benign)、1512个广告软件(Adware)、2467个网银木马(Banking Malware)、3896个手机风险软件(Mobile Riskware)以及4809个SMS恶意软件(SMS),共计5大类16,717个软件,将其按照0.64、0.16、0.2的比率划分为训练集、验证集和测试集。

使用Google提供的反编译工具—Apktool对Apk文件进行反编译,并获取了其中的用于在Dalvik虚拟机上运行的主要源码文件—smali文件。

由于Dalvik操作码有两百多条,对此进行了分类与精简,去掉了无关的指令和指令参数,只留下了移动、返回、跳转、判断、取数据、存数据、调用方法七大类核心的指令集合,简化为M、R、G、I、T、P、V指令集合,并使用分隔符|进行指令段的分割,随后去掉了连续的重复分隔符|。




3.2  词典压缩算法


实验中,发现对smali文件提取特征后的数据集中最短的特征数据只有10个指令,而最长的可以达到1,104,801个指令,我们统计了其数据长度分布,如图3.2所示,数据分布极不均衡且有较多数据长度在10万以上。

图3.2:特征向量长度分布图


对此,使用了一种简单的词汇压缩算法进行组词,以适当减小输入特征的长度。通过设置一个压缩比率rate,进行词典词汇的排列组合。

def vocab_compress(vocab_dict,rate=4):
    if rate<=0:
        return
    with open('dict.txt','w',encoding='utf-8'as fp:
        arr=np.zeros(rate,int)
        while True:
            pos=rate-1
            for i in range(rate):
                fp.write(vocab_dict[arr[i]])
            fp.write('\n')
            arr[pos]+=1
            while True:
                if arr[pos]>=len(vocab_dict):
                    arr[pos]=0
                    pos-=1
                    if pos<0:
                        return
                    arr[pos]+=1
                else:
                    break

除了七大类指令外,原始数据词典还包括了分隔符|以及填充符#,数据读取时同样会依照压缩比率进行词汇的划分,并使用填充符#进行末位单词的补足。



3.3  模型构造


使用飞桨能很容易的搭建一个属于自己的分类模型。

相关代码:
import paddle
import paddle.nn as nn
import paddle.nn.functional as F
import paddlenlp as ppnlp


class CNNModel(nn.Layer):
    def __init__(self,
                 vocab_size,
                 num_classes,
                 emb_dim=64,
                 padding_idx=0,
                 fc_hidden_size=32)
:

        super().__init__()

        # 首先将输入word id 查表后映射成 word embedding
        self.embedder = nn.Embedding(
            num_embeddings=vocab_size,
            embedding_dim=emb_dim,
            padding_idx=padding_idx)

        self.cnn_encoder = ppnlp.seq2vec.CNNEncoder(
            emb_dim,
            num_filter=12,
            ngram_filter_sizes=(1234),
            conv_layer_activation=nn.Tanh()
        )

        self.fc = nn.Linear(self.cnn_encoder.get_output_dim(), out_features=fc_hidden_size)

        # 最后的分类器
        self.output_layer = nn.Linear(fc_hidden_size, out_features=num_classes)

    def forward(self, text, seq_len):
        # text shape: (batch_size, num_tokens)
        #print('input :', text.shape,text)

        # Shape: (batch_size, num_tokens, embedding_dim)
        embedded_text = self.embedder(text)
        #print('after embedding:', embedded_text.shape)

        #Shape: (batch_size, num_filter*ngram_filter_size)
        cnn_out = self.cnn_encoder(embedded_text)
        #print('after cnn:', cnn_out.shape)

        # Shape: (batch_size, fc_hidden_size)
        fc_out = paddle.tanh(self.fc(cnn_out))
        # print('after Linear classifier:', fc_out.shape)

        # Shape: (batch_size, num_classes)
        logits = self.output_layer(fc_out)
        #print('output:', logits.shape)

        # probs 分类概率值
        probs = F.softmax(logits, axis=-1)
        #print('output probability:', probs.shape)
        return probs

图3.3 模型结构图


模型构造如上图展示,其中,嵌入层部分用于将词映射到向量空间中,它可以表征词与词之间的相互关系,相似的词在向量空间中往往离得更近。

文本卷积神经网络层关注在句子序列中某些关键部分所蕴藏的信息,并将这些信息提取出来作为该序列的关键特征点。

第一层全连接用于将文本卷积神经网络提取的信息进行汇聚整合,并使用tanh函数进行激活。

第二层全连接用于最终的分类。



实验过程与分析




为了追求更高的效率,将卷积神经网络的参数设置为filter_size=(1,2,3,4)、num_fliter=12,然后统计了使用词汇压缩算法前后,词典大小、批次大小、训练占用显存大小以及训练时长(1个Epoch)如表4.1所示:

表4.1 词汇压缩算法性能表


由表4.1可以看出,词汇压缩算法在不丢失时序特征的同时,在一定数值范围内能够有效地提升训练性能。本次实验,选取了压缩比率为6的词典,并使用6个连续的#做填充词,6个连续的|做未知词。

对于该文本卷积网络来说不同尺寸的n-gram滤子有着不同的感受野。在设计该网络结构的时候,对不同尺寸、不同数量的滤子组做了对比,发现更小、更多的滤子组在分类效果上更加突出。表4.2为采用不同尺寸、不同数量的滤子组设置梯度下降算法为Adam、学习率为10^(-5)、使用交叉熵计算损失的5分类模型,分别训练迭代50次后的最终准确率。

表4.2 不同滤子组得分表


根据4.2表,认为如同Oxford Visual Geometry Group在2014年ImageNet Large Scale Visual Recognition Challenge(ILSVRC)竞赛上提出的通过较小的卷积核进行纵向的深度叠加可以拥有大尺寸卷积核的效果[4]那样,使用小尺寸的文本滤子组在时序特征提取上进行横向的组合也拥有类似的效果。即使用2个1-gram滤子提取出的特征,经过一次全连接的线性变换将信息聚合后可以相当一个2-gram滤子提取出的特征。



由于使用小且多的文本滤子能够携带更多的信息,并且全连接层并不在意时序排列,我们认为使用更小、更多的n-gram滤子有助于提高模型的精度。

图4.1:小尺寸文本卷积组在时序特征提取上
横向组合如何代替大尺寸卷积效果图(以二分类为例)

采用filter_size=(1,2,3,4)、num_fliter=100的卷积结构,设置学习率为10^(-5),梯度下降算法为Adam,损失值计算方法为交叉熵,训练迭代50次并在每个Epoch训练完后进行一次验证。

主要过程如下:

1)加载词表
from paddlenlp.datasets import load_dataset

def read(data_path):
    with open(data_path, 'r', encoding='utf-8'as f:
        for line in f:
            l = line.strip('\n').split('\t')
            if len(l) != 2:
                print (len(l), line)
            words, labels = line.strip('\n').split('\t')
            if len(words)==0:
                continue
            yield {'tokens': words, 'labels': labels}

# data_path为read()方法的参数
train_ds = load_dataset(read, data_path='dataset/train.txt',lazy=False)
dev_ds = load_dataset(read, data_path='dataset/eval.txt',lazy=True)
test_ds = load_dataset(read, data_path='dataset/test.txt',lazy=True)
# 加载词表
vocab = load_vocab('dict.txt')
#print(vocab)

2)构造dataloder
# Reads data and generates mini-batches.
def create_dataloader(dataset,
                      trans_function=None,
                      mode='train',
                      batch_size=1,
                      pad_token_id=0,
                      batchify_fn=None)
:

    if trans_function:
        dataset_map = dataset.map(trans_function)

    # return_list 数据是否以list形式返回
    # collate_fn  指定如何将样本列表组合为mini-batch数据。传给它参数需要是一个callable对象,需要实现对组建的batch的处理逻辑,并返回每个batch的数据。在这里传入的是`prepare_input`函数,对产生的数据进行pad操作,并返回实际长度等。
    dataloader = paddle.io.DataLoader(
        dataset_map,
        return_list=True,
        batch_size=batch_size,
        collate_fn=batchify_fn)

    return dataloader

# python中的偏函数partial,把一个函数的某些参数固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。
trans_function = partial(
    convert_example,
    vocab=vocab,
    rate=rate,
    unk_token_id=vocab.get(unk),
    is_test=False)

# 将读入的数据batch化处理,便于模型batch化运算。
# batch中的每个句子将会padding到这个batch中的文本最大长度batch_max_seq_len。
# 当文本长度大于batch_max_seq时,将会截断到batch_max_seq_len;当文本长度小于batch_max_seq时,将会padding补齐到batch_max_seq_len.
batchify_fn = lambda samples, fn=Tuple(
    Pad(axis=0, pad_val=vocab[pad]),  # input_ids
    Stack(dtype="int64"),  # seq len
    Stack(dtype="int64")  # label
): [data for data in fn(samples)]

train_loader = create_dataloader(
    train_ds,
    trans_function=trans_function,
    batch_size=4,
    mode='train',
    batchify_fn=batchify_fn)
dev_loader = create_dataloader(
    dev_ds,
    trans_function=trans_function,
    batch_size=4,
    mode='validation',
    batchify_fn=batchify_fn)
test_loader = create_dataloader(
    test_ds,
    trans_function=trans_function,
    batch_size=4,
    mode='test',
    batchify_fn=batchify_fn)

3)模型配置和训练
model= CNNModel(
        len(vocab),
        num_classes=5,
        padding_idx=vocab[pad])

model = paddle.Model(model)

# 加载模型
#model.load('./checkpoints/final')
optimizer = paddle.optimizer.Adam(
        parameters=model.parameters(), learning_rate=1e-5)

loss = paddle.nn.loss.CrossEntropyLoss()
metric = paddle.metric.Accuracy()

model.prepare(optimizer, loss, metric)
# 设置visualdl路径
log_dir = './visualdl'
callback = paddle.callbacks.VisualDL(log_dir=log_dir)
model.fit(train_loader, dev_loader, epochs=50, log_freq=50, save_dir='./checkpoints', save_freq=1, eval_freq=1, callbacks=callback)
end=datetime.datetime.now()
print('Running time: %s Seconds'%(end-start))

4)计算模型准确率
results = model.evaluate(train_loader)
print("Finally train acc: %.5f" % results['acc'])
results = model.evaluate(dev_loader)
print("Finally eval acc: %.5f" % results['acc'])
results = model.evaluate(test_loader)
print("Finally test acc: %.5f" % results['acc'])

在验证集上的损失、准确率如下图所示:
    

图4.2 验证集上梯度下降图

可以看出,该网络依旧有继续拟合的余力。所以继续对模型做了微调,最终模型的准确率达到了训练集98.87%,验证集94.46%,以及测试集95.19%的效果。



结论




本文首先讨论了词汇压缩算法构造词典在此类基础词典小、但拥有超长输入向量的Android恶意软件静态检测上的可行性,并使用了压缩比率为6的词典进行了不同尺寸、不同数量的滤子组的对比实验,最终得出使用尺寸小、数量多的滤子组可以获得更高的准确率。

实验最后,使用了filter_size=(1,2,3,4)、num_fliter=100的文本卷积结构进行5分类的训练,收获了非常不错的成绩。

本实验只是将Dalvik Opcode进行分类精简,使用原指令集作为数据词典可能会获得更高的精度。




参考文献


[1] McLaughlin N, Martinez del Rincon J, Kang B J, et al. Deep android malware detection[C]//Proceedings of the Seventh ACM on Conference on Data and Application Security and Privacy. 2017: 301-308.

[2] Zhang Y ,  Wallace B . A Sensitivity Analysis of (and Practitioners' Guide to) Convolutional Neural Networks for Sentence Classification[J]. Computer Science, 2015.

[3] Samaneh Mahdavifar, Andi Fitriah Abdul Kadir, Rasool Fatemi, et al. Dynamic Android Malware Category Classification using Semi-Supervised Deep Learning[C]. IEEE 18th International Conference on Dependable, Autonomic, and Secure Computing (DASC), 2020: 17-24.

[4] Simonyan K ,  Zisserman A . Very Deep Convolutional Networks for Large-Scale Image Recognition[J]. Computer Science, 2014.



如有飞桨相关技术问题,欢迎在飞桨论坛中提问交流:
http://discuss.paddlepaddle.org.cn/

欢迎加入官方QQ群获取最新活动资讯:793866180

如果您想详细了解更多飞桨的相关内容,请参阅以下文档。

·飞桨官网地址·
https://www.paddlepaddle.org.cn/

·飞桨开源框架项目地址·
GitHub: https://github.com/PaddlePaddle/Paddle 
Gitee: https://gitee.com/paddlepaddle/Paddle

长按上方二维码立即star!


飞桨(PaddlePaddle)以百度多年的深度学习技术研究和业务应用为基础,集深度学习核心训练和推理框架、基础模型库、端到端开发套件和丰富的工具组件于一体,是中国首个自主研发、功能丰富、开源开放的产业级深度学习平台。飞桨企业版针对企业级需求增强了相应特性,包含零门槛AI开发平台EasyDL和全功能AI开发平台BML。EasyDL主要面向中小企业,提供零门槛、预置丰富网络和模型、便捷高效的开发平台;BML是为大型企业提供的功能全面、可灵活定制和被深度集成的开发平台。




END