竇建民,師智斌,于孟洋,霍 帥,張舒娟
(中北大學 計算機科學與技術學院,山西 太原 030051)
隨著惡意軟件的急劇增加,自動化檢測成為主流方法。靜態分析方法易受代碼混淆、加殼等影響[1]。而動態分析通過軟件的實際運行狀態來提取其行為特征實現檢測,如API(application program interface)函數調用等行為[2],特別是敏感API調用直接反映了軟件的良性或不良意圖[3],能有效解決代碼混淆等技術的影響。
目前在軟件行為特征的提取方法中,機器學習依賴人工提取特征且需要有完備的先驗知識[4]。而深度學習方法能實現特征的自動提取,但不能捕獲API序列全局語義信息,特別是API函數在不同序列的多種語義問題[5,6],同時深度學習方法依賴大量有標注數據,而惡意軟件數據集通常包含大量無標注數據,無法直接進行有監督學習。
本文基于BERT模型,在大量無標注API序列上利用無監督學習構建動態掩碼序列模型,使模型會考慮API序列的上下文和語義關系,獲得API序列的全局編碼表示,從而有效解決無法捕獲API序列全局語義信息和和處理API函數在不同序列的多種語義問題以及帶標注數據不足的問題。主要工作如下:①為避免其它領域不相關的分詞干擾,重新定義詞級編碼器和規范器等組件構建分詞器,將API序列解析為模型可識別的數值表示;②基于BERT模型,使用無監督學習在大量無標注API序列數據上利用動態掩碼機制構建惡意軟件動態掩碼序列模型(malware dynamic mask sequence model,MDMSM),從零訓練模型學習API序列的語義關系,獲取API序列的全局動態編碼表示,實現動態挖掘API序列的語義特征;③將該編碼作為檢測模型的詞嵌入,在少量有標注的API序列數據上微調模型完成檢測。
機器學習方法通過人工特征工程提取靜態和動態特征后訓練模型實現檢測[7,8]。PektaA等[9]通過n-gram算法樹提取惡意軟件常見API子序列用于檢測,也驗證了動態分析能更有效地獲取軟件的行為特征。Han H等[10]基于Android程序運行時的API調用序列,使用支持SVM來進行惡意軟件檢測。田東海等[11]使用n-gram算法優化惡意代碼中的操作碼特征,然后使用詞袋模型和TF-IDF算法優化API調用序列特征,最后使用決策樹進行分類。雖然機器學習方法通過學習淺層樣本特征能實現對惡意軟件進行自動化檢測,但該方法會依賴人工提取特征,存在特征表達受限等因素影響。
深度學習在模型構建和特征提取方面效果更好,深層神經網絡具有自動學習樣本特征的能力,有較高的準確率[12,13]。Li C等[14]提取軟件API調用序列,利用詞嵌入獲得序列的向量表示并將其輸入LSTM進行軟件分類。唐永旺等[15]使用Bi-LSTM學習樣本字節流序列并利用注意力機制獲取序列的深層特征完成分類任務。Cui Z等[16]將惡意軟件的特征用灰度圖像表示,然后利用卷積神經網絡提取惡意軟件的圖像特征進行分類。深度學習方法在一定程度上能提高任務性能,但該方法需要大量有標注數據,在小數據集上模型不易收斂且捕獲API序列的特征不全面。
對API函數進行語義特征提取及編碼表示,即詞嵌入方法,也是目前研究熱點,其特征表示和編碼方式將直接影響惡意軟件的檢測效果。Cakir B等[17]基于操作碼序列使用Word2vec作為特征提取方法表示軟件樣本,然后使用梯度提升機搜索算法實現分類任務。Zhang J等[18]利用Glove表征從程序中提取信息,然后使用CNN分析詞嵌入表示的特征圖進行分類。Feng L等[19]使用FastText作為序列表示方法,提取API序列特征實現軟件的分類任務。但傳統詞嵌入方法提取的詞向量不能根據上下文的不同而發生變化,無法解決API函數在不同調用序列中的多種語義問題。
以上方法雖然在一定程度上能提高任務效率,但都無法解決API函數的全局語義信息和處理API函數在不同序列的多種語義問題以及有標注數據集不足的問題。BERT是NLP任務中較最先進的語言模型[20],通過預訓練機制對大量無標注數據利用無監督學習獲得通用編碼表示,因為文本序列自身的順序性就是一種天然的標注,目前該方法也被應用到軟件檢測中。Oak R等[21]利用BERT模型在安卓軟件數據集上進行軟件分類任務。Alvares J等[22]通過BERT模型生成API序列的詞向量進行分類。由于現有方法都直接使用NLP領域的BERT模型[23],而API序列包含的是與系統調用相關的詞匯和標識符,與NLP領域文本序列的分詞規則有所差異,在軟件分類的應用中效果不佳,無法高效完成檢測任務。
本文基于BERT模型架構,在大量無標注API序列上使用動態掩碼序列機制,構建惡意軟件動態掩碼序列模型MDMSM,重新構造預訓練任務,實現對API序列的全局動態編碼表示,可有效解決動態編碼和帶標注數據不足的問題。模型結構如圖1所示,該模型由多種詞編碼組合的輸入和12個編碼層疊加的編碼器所組成。

圖1 惡意軟件動態掩碼序列模型結構
MDMSM的輸入是由符號嵌入、片段嵌入和位置嵌入3個不同層級詞向量組成。符號嵌入是用分詞器對API序列分詞后的結果,而片段嵌入和位置嵌入由模型隨機初始化。
采用Word2vec、Glove、FastText等傳統模型獲取的編碼表示通常忽略了API上下文關系,而同一個API函數在不同序列中往往會表征出不同的語義信息。MDMSM結構是由12個編碼層串聯疊加組成的編碼器,其中每個編碼層是由多頭注意力層、殘差連接、層歸一化、前饋層組成,前饋層的激活函數和自注意力的計算提供整個網絡模型的非線性變換。
MDMSM的核心是多頭注意力機制,它類似CNN中多個卷積核的概念,通過多個不同的Head分別進行注意力計算,從不同角度抽取API序列的特征使得信息表征更豐富,實現全局信息的采集。設頭數為i, 分頭后輸入矩陣為Ii, 3個中間變量矩陣為Qi、Ki、Vi, 其第2個維度分別為dq、dk、dv。 注意力機制公式為
(1)
3個中間變量矩陣公式為
(2)
(3)
(4)


(5)
其中,WO∈Rhdv×dmodel, 在模型中用h=8構建多頭自注意力模塊且限定dk=dv=dmodel/h, 使用多頭注意力的好處是相較原來dmodel的高維空間,每個Head都經過降維,可以利用矩陣并行化處理使得計算效率更高。
對于一條API序列,每經過一層多頭注意力,每個API的詞向量都是該序列中所有API函數詞向量的加權平均,即融合該序列所有的關鍵信息。因此,對于同一個API函數,不同上下文會讓該API融合不同的語義信息,從而更好地捕獲序列的上下文語義關系,使得該API在不同的上下文中有著不同的詞向量去表征不同的語義,從而解決API函數在不同序列的多種語義問題。采用以上機制,MDMSM模型可獲得API序列全局語義的編碼表示,能夠彌補傳統模型的不足。
本文基于API序列,利用預訓練的MDMSM進行惡意軟件檢測。該方法分以下4個步驟實現:首先進行數據預處理工作,將API序列進行數據清洗和剪枝;其次為了將數據解析為模型可識別的數值表示,需要重新定義字符編碼器和規范器等組件,構建分詞器MalTokenizer(malware tokenizer);然后基于BERT模型,利用動態掩碼機制在大量無標注API序列上構建惡意軟件動態掩碼序列模型進行無監督學習的預訓練,同時獲取API序列的全局動態編碼表示;最后將該編碼作為檢測模型的詞嵌入,通過微調技術完成檢測任務。雖然預訓練的計算成本比較昂貴,但預訓練后的模型可以用在各種基于API序列的計算成本低的下游任務。整體設計流程如圖2所示。

圖2 惡意軟件檢測模型流程
軟件行為分析報告中的API調用序列字段中包含很多無關的噪音信息和冗余數據,如圖3所示給出一份從分析報告中提取的原始API調用序列信息。

圖3 原始API調用序列
為了使API序列可直接用于模型的訓練,減少無關字段和噪音數據對模型效果的影響,需要對其進行數據清洗和剪枝。首先將加載的原始API序列數據使用data_clea-ning()函數按照指定的過濾規則從API序列中去除各種無關噪音并使用空格符號作為每個API之間的分割標識;然后使用deduplication()函數對過濾后的序列數據進行剪枝處理并將較短的序列固定為指定長度length。其中剪枝是指從某條API序列中去除位置相鄰且相同的冗余API函數,只保留最后一個。算法1為數據預處理的偽代碼。
算法1:數據預處理
輸入:原始API序列S
輸出:預處理后的API序列
(1)files=open(S)
(2)api_filter={‘#’,‘-’,…}
(3)length=fixed_length
(4)last_api=‘’
(5)forlineinfilesdo
(6)content=data_cleaning(line,api_filter)
(7)apis=deduplication(content,last_api,length)
(8)endfor
(9)returnapis
對圖3所示的API調用序列經過數據清洗和剪枝處理后,得到如圖4所示的序列數據。

圖4 預處理后的API序列
為了讓模型能處理API序列,需要使用分詞器將其解析為模型可識別的數值表示,因此分詞器需要完成分詞、初始編碼等功能。由于本文研究對象特定于軟件API序列,而不是普通文本,數據中包含與程序描述相關的詞匯和標識符,同時避免來自其它不相關領域通用詞匯的分詞干擾,因此需要重新構建基于API序列的分詞器,方法如下:
首先定義標記器Tokenizer,并使用詞層級WordLevel的字符編碼器;接著定義訓練器WordLevelTrainer,并重新制定針對API序列的規范化器、預標記器等組件;然后將訓練語料和訓練器傳入標記器中;最終訓練完成會生成vocab.json和merges.txt,即詞典文件。后續在模型中使用分詞器時,只需加載詞典文件,分詞器會將經過數據預處理的API序列拆分成詞典中對應的字段,然后通過查找表將其轉換為數值并進行標記化處理。算法2為構建分詞器的偽代碼。
算法2:構建分詞器
輸入:預處理后的apis
輸出:生成的vocab.json、merges.txt
(1)MalTokenizer=Tokenizer(WordLevel(unk_token))
(2)trainer=WordLevelTrainer(special_tokens)
(3)MalTokenizer.normalize&post_processor
(4)MalTokenizer.train(apis,trainer)
(5)MalTokenizer.save()
對圖4的API序列進行分詞后得到如圖5所示的結果。可以看到API序列已被解析為數值序列,后面將被直接輸入到MDMSM預訓練模型中。

圖5 分詞后的API序列
MDMSM模型通過在大量無標注API序列上構建動態掩碼序列任務進行預訓練,實現動態生成包含API序列上下文語義信息的編碼,并捕獲數據內部結構。動態掩碼序列任務是指隨機掩蓋API序列中一些API函數,然后訓練模型來預測這些被掩蓋的API函數,實現無監督學習。在模型預訓練的每輪次中,動態地把輸入的無標注API序列隨機掩蓋掉15%讓模型去預測,實現動態掩碼。由于預訓練階段采用[MASK]遮擋API函數,但在微調階段是沒有[MASK]的,因此為了解決該問題,對于被隨機掩蓋的API函數有80%的概率會被替換為[MASK],10%的概率被替換為其它API函數,10%的概率保持不變,其中較短的序列用[PAD]進行填充并在序列開頭添加[CLS]標志位。步驟如下:
首先使用3.2節中的詞典文件載入分詞器MalTokenizer,在每輪模型預訓練的過程中使用generate_positions()函數,根據API序列動態生成掩碼位置的索引值,在每輪訓練時會不斷更新被掩碼的位置,并用MalTokenizer的tokenize()方法把經過數據清洗的API序列做分詞處理,同時根據分詞后的數據使用generate_mlm_tokens()函數分別生成被掩碼的序列M、被填充的序列P和原始的序列T,從而完成對動態掩碼序列任務的構建;然后使用batch_samples()函數將M、P、T數據劃分批次,輸入到模型中進行預訓練,模型通過預測序列中[MASK]位置最有可能被替換的API函數來不斷擬合原有序列;最終獲得預訓練的惡意軟件動態掩碼序列模型。算法3為模型預訓練的偽代碼。
算法3:惡意軟件動態掩碼序列模型預訓練
輸入:預處理后的apis
輸出:預訓練后的MDMSM
(1)vocab=dictionary_file
(2)epoch=training_number
(3)MDMSM=Malware Dynamic Mask Sequence Model
(4)MalTokenizer=PreTrainedTokenizerFast(vocab)
(5)forindexinepochdo
(6)forapiinapisdo
(7)pos=generate_positions(api)
(8)tokens=MalTokenizer.tokenize(api)
(9)M,P,T=generate_mlm_tokens(tokens,pos)
(10)endfor
(11)batchs=batch_samples(M,P,T)
(12)MDMSM_file,loss=MDMSM(batchs)
(13)loss.backward()
(14)endfor
(15)returnMDMSM_file
模型預訓練采用交叉熵損失函數,公式為
(6)
其中,yi表示樣本i真實標簽,即真實的API函數,pi表示模型預測的API函數,兩者做交叉熵進行梯度更新。
模型微調是指通過無監督學習從大量無標注數據中訓練一個深層的預訓練模型后,得到一組預訓練的模型參數;然后針對不同的下游任務,把預訓練模型的輸出添加相應的網絡模型組成用于解決特定目標任務的新模型;最后在少量有標注數據上對模型進行有監督地微調來完成指定任務。在微調的過程中,預訓練模型的權重可以選擇更新或者凍結部分網絡層,以減少資源開銷。
在3.3節的預訓練模型基礎上,針對下游的軟件分類任務進行檢測模型的微調。步驟如下:首先在惡意軟件動態掩碼序列模型后面添加檢測模型,將MDMSM最后一層輸出的動態詞向量作為下游檢測模型的詞嵌入,從而構建出惡意軟件檢測模型;然后使用batch_samples()函數把少量有標注API序列劃分批次,輸入到惡意軟件檢測模型中進行有監督地微調,在微調的過程中選擇凍結一部分預訓練模型的參數防止模型過擬合;最終在小規模的API序列上訓練檢測模型完成分類任務。下游檢測模型是在MDMSM之上單獨添加的網絡結構,其中的權重參數是模型隨機初始化并需要從頭開始訓練,微調時MDMSM本身繼續訓練,預訓練模型的部分權重參數也被不斷更新從而更好地適應軟件分類任務。檢測模型仍然采用交叉熵損失函數。其中標簽0為良性,標簽非0為惡意。算法4為模型微調的偽代碼。
算法4:檢測模型微調
輸入:預訓練后的MDMSM,少量有標注API序列datasets
輸出:檢測模型和評價指標
(1)vocab=dictionary_file
(2)epoch=training_number
(3)model=MDMSM and Classification Model
(4)MalTokenizer=PreTrainedTokenizerFast(vocab)
(5)datas=MalTokenizer(datasets)
(6)forindexinepochdo
(7)batchs=batch_samples(datas)
(8)model_file,loss=model(batchs)
(9)loss.backward()
(10)endfor
(11)evaluation=evaluate(model,datas)
(12)returnmodel_file,evaluation
為了驗證惡意軟件動態掩碼序列模型預訓練和檢測模型微調的有效性,本文首先進行軟件API序列數據集的收集和整理工作。目前單一的API序列數據集都比較小,本文收集了8個不同來源的數據集,見表1。因為后續要和其它方法進行性能比較,本文全部采用了帶標簽數據,實際中可采用無標簽的數據進行預訓練。由于收集到大部分數據集提供的是軟件行為分析報告或是所提供數據集中存在很多無關噪音干擾字段,因此需要對收集的數據篩選與整理,并盡可能平衡不同類別樣本的比例來排除數量不一致對實驗結果的影響。表1中前7個數據集總共包含61 381條API序列,共同構成本文的數據集1,進行惡意軟件動態掩碼序列模型的預訓練和二分類任務的驗證,最后一個數據集來源于阿里云天池大賽惡意程序檢測的數據集,共有13 887條樣本,構成本文的數據集2,用于多分類任務的驗證。

表1 數據集來源
在本文數據集1上,分別選取20 000條惡意和良性API序列樣本用于惡意軟件動態掩碼模型的預訓練,再分別選取2000條用于檢測模型的微調做二分類任務,最后再選取1000條用于模型測試,共計46 000條數據。數據集1上的劃分見表2。

表2 數據集1劃分
在本文數據集2上,不同種類的惡意樣本分別選取500條,良性樣本選取1000條,共計4500條數據,然后將80%的數據作為訓練集,剩余20%的數據作為測試集做多分類任務。數據集2的劃分見表3。

表3 數據集2劃分
評價指標對分析模型有著至關重要的作用,通過計算Accuracy、Precision、Recall和F1評估模型的性能。當惡意樣本被檢測為惡意則標記為TP;當惡意樣本被識別為良性則標記為FN。當良性樣本被檢測為良性則標記為TN;當良性樣本被識別為惡意則標記為FP。公式如下
(7)
(8)
(9)
(10)
Precision和Recall是一對矛盾的指標,引入F1值能有效地平衡兩者,F1值越接近1代表模型性能越好。
本文選取4種機器學習、3種傳統詞嵌入和原始BERT作為對照實驗方法,其中機器學習方法選取K近鄰算法、決策樹、支持向量機、樸素貝葉斯,在評價指標中用K-NN、DT、SVM、NB表示,傳統詞嵌入方法為Word2Vec、Glove、FastText。為驗證MDMSM的效果。在數據集1中,將用于MDMSM預訓練和微調的數據樣本組合為對照實驗的訓練集,用于模型測試的數據樣本作為對照實驗的測試集,盡可能平衡訓練集和測試集所帶來的差異。
4.3.1 不同分詞器進行模型預訓練對比
由于NLP領域的通用語言序列和網絡安全領域軟件API序列的分詞規則有所不同,按照3.2小節所述,根據API序列的特點重新構建分詞器,在數據集1上,采用原始BERT模型的分詞器BertTokenizer和本文MDMSM的分詞器MalTokenizer分別對模型重新進行預訓練,然后進行二分類任務的實驗結果對比。
通過表4可知,采用MalTokenizer作為分詞器的預訓練模型進行分類任務要遠高于采用BertTokenizer作為分詞器的預訓練模型的檢測效果。主要是因為原始BERT模型的分詞器主要是針對通用NLP領域的通用語言樣本進行編碼的,而API函數幾乎不存在于BertTokenizer的詞典文件,并且API函數不存在子詞或者詞根詞綴,每一個API函數所具備的系統功能也不盡相同,實驗結果表明,基于API序列采用預訓練模型完成惡意軟件分類任務時,需要先重新訓練分詞器對API序列進行編碼的必要性。

表4 不同分詞器的結果對比/%
在數據集1上,采用不同分詞器的預訓練模型進行動態掩碼序列任務的預訓練,如圖6所示,其中橫坐標的數據進行了縮放,一個單位代表實際5000的步長。可以看出,MalTokenizer模型的損失值隨著訓練步長很快減小,在一定步數后開始趨于穩定狀態接近0。然而MalTokenizer模型的損失值降至4后不再變化,表明該模型無法很好地收斂。

圖6 不同分詞器進行模型預訓練的loss曲線
4.3.2 與現有方法作二分類對比
針對不同檢測模型在二分類任務上的實驗結果進行分析,各項指標見表5。

表5 不同方法在數據集1上的實驗結果對比/%
由表5可知,本文方法在二分類問題上,相較于其它方法有著更高的準確率達到97.57%,通過對比發現F1值也是高于其它方法。與BERT相比,本文提出基于API序列利用動態掩碼序列任務針對BERT模型重新進行無監督學習的預訓練,同時獲得API序列的全局動態編碼表示,然后將該編碼表示作為檢測模型詞嵌入用于軟件檢測的方法,結果顯示準確率提高了25.09%,其它指標也有較高的提升,驗證基于API序列,對BERT模型利用動態掩碼序列任務進行無監督學習的預訓練,然后進行惡意軟件分類任務的可行性;該方法也優于現有機器學習方法的結果,在準確率上比分類效果最好的K-NN算法高出5.84%,間接驗證該模型能更準確地捕捉到API序列的上下文語義特征并為軟件分類提供有力依據;雖然本文方法也屬于詞嵌入模型的范疇,但相較于傳統詞嵌入模型,其準確率平均提升了8.17%,驗證本文方法在捕獲API序列特征時會考慮到樣本數據的全局信息,不再只局限于利用滑動窗口捕獲局部信息,有效地解決API函數在不同調用序列中的語義表示問題,使得惡意軟件分類任務更加有效地完成。本文方法在捕獲API序列特征方面表現出色,充分考慮全局信息使得分類準確率顯著提高。
4.3.3 與現有方法作多分類對比
在數據集2上直接使用已經預訓練的MDMSM做多分類任務,雖然準確率等指標相較于二分類有所降低,但仍優于其它算法。各項指標見表6。

表6 不同方法在數據集2上的實驗結果對比
由表6可知,本文方法在各項指標上仍均高于其它模型。圖7為不同方法的多分類和二分類準確率變化曲線。

圖7 多分類和二分類的準確率對比
在對比實驗中,使用機器學習方法進行多分類任務比二分類任務的各項指標波動較大,這是因為數據集2中不同種類的樣本數據量較少,而多分類任務需要更多的特征數量和和更全面的特征表達,而且數據噪聲也容易影響模型性能。這使得模型在多分類任務中難以準確地提取用于軟件分類的特征,從而導致效果不佳。相比之下,使用傳統詞嵌入方法的準確率變化不大,但整體效果仍不盡如人意。這是因為數據集2的多分類任務需要更復雜的模型和特征表示以及更多的數據標簽,而在數據量有限的情況下,復雜的模型可能導致過擬合,降低多分類任務的性能,也無法有效地捕獲API函數的全局語義信息和處理API函數在不同序列的多種語義問題。本文方法是基于大量無標注的API調用序列,通過模型預訓練的過程來擬合原始API序列的目標任務,從而使預訓練后的模型能夠準確關注序列中每個API的關鍵特征,并提取到融入API序列全局語義信息的編碼表示用于分類任務。該方法的優勢在于能夠更好地捕獲API序列的上下文語義關系,從而有效地提取到軟件分類相關的特征,使得檢測效果顯著提升。
本文通過動態掩碼機制在大量無標注API序列上構建惡意軟件動態掩碼模型,獲取API序列的動態編碼表示,解決了傳統方法無法捕獲API序列全局語義信息和帶標注數據不足的問題,然后在少量有標注API序列上微調模型完成檢測。該方法在二分類和多分類任務中均優于其它方法,顯著提高軟件檢測的準確率。然而在軟件分析報告中只利用到API調用序列的字段,在后續中將嘗試結合其它數據展開工作,此外本文用于模型預訓練的數據量仍然有限,接下來計劃搜集更大規模的軟件樣本進行分析。