陳 杰,馮秀芳,陳永樂
(太原理工大學信息與計算機學院,山西 晉中 030600)
軟件作者識別有助于在案件中對真實的作者和版權的爭議進行裁決。代碼作者署名在文本分析、軟件著作權調(diào)查[1]、軟件剽竊檢測[2]等方面有廣泛的應用。學術著作中作者身份歸屬預測的任務通常是出于計算機安全需求的驅動,它通過分析殘留的惡意代碼[3]識別惡意軟件程序[4]的作者。源代碼作者身份歸屬預測主要通過提取某一作者的編碼結構特征,根據(jù)這些結構特征分析源代碼是否由這一作者所編寫。此外,對于那些希望保持匿名的程序員來說,這是一個嚴重的隱私風險。
軟件取證可以使用編碼樣式識別源代碼的特定作者,研究界也越來越關注通過編程風格[5]識別作者的方式。代碼作者身份識別在學術界可以對學生編程作業(yè)進行剽竊檢測,在商業(yè)領域可以對軟件著作和專利侵權進行分析。因此,代碼作者歸屬預測在學術界和商業(yè)界具有重要的現(xiàn)實意義。本文針對源代碼的特征屬性展開研究,提出一種源代碼的作者歸屬模型。
Figure 1 Framework of source code author identification 圖1 源代碼作者判別框架圖
為了對代碼樣本[6]進行作者身份識別,已有的大多數(shù)代碼作者身份識別工作都采用統(tǒng)計分析技術或基于相似性度量的排序方法。特別是Krsul等人[7]率先探索了通過檢查手工制作的編程風格特征來識別程序作者的可能性,他們采用了一種稱為多元判別分析的統(tǒng)計分析技術來對29名程序員的代碼樣本進行分類。文獻[8,9]都從源代碼中提取了n-grams特征的配置文件,給源代碼的作者進行排名,結果表明n-grams功能也可以提取編碼風格。
此外,源代碼作者歸屬思想還可用于不同的編程集語言分類,如Java或C++,并獲得了較好的準確率。Dauber等人[10]提出了一種利用代碼抽象語法樹AST(Abstract Syntax Tree)來識別源代碼作者的技術,將源代碼作者歸屬建模為一個機器學習問題,通過向分類器提供抽象語法樹中的特征來分類代碼的作者。
Abuhamad等人[11]使用權重分析方法從代碼樣本中提取穩(wěn)健的作者屬性,然后利用作者屬性構造隨機森林分類器,并證明該模型的可行性。Ullah等人[12]提出了一種基于深度學習的程序依賴圖方法來識別不同程序源代碼的作者,對1 000名程序員數(shù)據(jù)的識別結果明顯優(yōu)于現(xiàn)有的作者身份判別技術。文獻[13]提取了AST中的路徑特征,然后分別采用隨機森林和code2vec網(wǎng)絡對50個項目包含的代碼進行作者判別,并指出時間環(huán)境對作者歸屬具有較大的影響。
盡管前面的工作已經(jīng)證明特征提取和深度學習結合的方法提高了源代碼作者歸屬的效率和準確率,但該方法仍有一些不足之處。之前關于作者歸屬研究僅僅是對詞法、句法、語法和語義的特征提取,并沒有對整個程序的各個模塊間的關聯(lián)程度(即耦合度(Coupling))[14]進行分析。然而,模塊與模塊之間有些操作是有關聯(lián)的,如果改動一個模塊,其他的模塊都有可能受到影響,模塊與模塊之間的關系越是緊密,獨立性就越差,程序的耦合度就越高,這段程序寫得就越差。由此可以得出,一個程序的模塊關聯(lián)程度能夠很好地反映一個作者的編碼水平。因此,本文采用代碼的耦合度和轉換的程序依賴圖PDG(Program Dependence Graph)特性結合的方式,提取每段源代碼的屬性特征,并嵌入到深度學習的模型去訓練,以提高作者身份判別的準確率。本文利用3種編程語言對本文模型進行了評估,以探究耦合度這一特征在作者歸屬的判別中的作用。
本節(jié)主要介紹源代碼屬性的特征表示方法和提取過程,如圖1所示。
首先,從不同的源代碼(即C++,Java,Python)中提取耦合度的特征并將其量化,它代表了源代碼的各個模塊間的耦合程度;其次,從源代碼中提取程序依賴圖特性,即數(shù)據(jù)流和控制流,這些是可以提取的高質量數(shù)據(jù)變化和控制流功能特征,代表著不同語句之間如何處理數(shù)據(jù)以及如何控制數(shù)據(jù)在不同語句之間轉移,顯示了不同編程代碼隱藏模式的重要特征。將PDG圖中的數(shù)據(jù)流和控制流轉換成路徑的令牌表示,并用詞頻-逆文檔頻率TF-IDF(Term Frequency-Inverse Document Frequency)方法分析權重。然后,將得到的耦合度與PDG特征合并生成代碼的向量表示,最后采用本文提出的基于代碼耦合度和PDG特征的神經(jīng)網(wǎng)絡模型CPNN (Coupling Program Neural Network)進行訓練和預測。具體操作流程如下所示:
(1)研究源代碼的預處理,規(guī)范化表現(xiàn)形式以及消除不明顯特征,如特殊符號、數(shù)字、停止字和標點符號;
(2)使用代碼生成包含數(shù)據(jù)流和控制流的程序依賴圖,然后從PDG圖中獲取代碼的依賴關系信息和函數(shù)調(diào)用信息;
(3)使用代碼工具從源代碼中提取參數(shù)個數(shù)、全局變量等指標,使用耦合公式計算代碼耦合度;
(4)將PDG的信息和耦合度通過令牌的方式轉換得到特征向量,把得到的特征向量嵌入到神經(jīng)網(wǎng)絡中訓練模型,并評價模型的準確率和F值大?。?/p>
(5)用測試集驗證模型判別作者屬性的準確率,并對源代碼作者歸屬進行判別。
本節(jié)主要描述源代碼特征提取的細節(jié)與方法,包括模塊間的耦合度特征提取、PDG特性提取及其權值分析。
耦合性也叫耦合度,是對模塊間關聯(lián)程度的度量。模塊間的耦合度是指模塊之間的依賴關系,包括控制關系、調(diào)用關系和數(shù)據(jù)傳遞關系。模塊間聯(lián)系越多,其耦合性越強。耦合的強弱取決于模塊間接口的復雜性、調(diào)用模塊的方式和通過界面?zhèn)魉蛿?shù)據(jù)的多少。
接口的復雜性指標由輸入數(shù)據(jù)參數(shù)個數(shù)、輸出數(shù)據(jù)參數(shù)個數(shù)、輸入控制參數(shù)個數(shù)和輸出控制參數(shù)個數(shù)決定。模塊調(diào)用方式由用來存儲數(shù)據(jù)的全局變量和用來控制的全局變量決定。傳送數(shù)據(jù)的多少由扇入扇出決定。因此,采用以下方法量化耦合度:
(1)用極差法將數(shù)據(jù)標準化。
程序的接口復雜度越高、數(shù)據(jù)傳輸量越大、模塊間調(diào)用越頻繁,模塊間耦合度就越高,因此呈正相關。正相關標準化如式(1)所示:
(1)
其中,xi表示每個模塊中輸入輸出數(shù)據(jù)參數(shù)的數(shù)量,mini表示參數(shù)數(shù)量的最小值,maxi表示參數(shù)數(shù)量的最大值。
(2)主成分分析,得到指標權重。
①去平均值(即去中心化),即標準化后的每一個數(shù)據(jù)減去該模塊數(shù)據(jù)的平均值;
④對特征值從大到小排序,選擇其中最大的k個,然后將其對應的k個特征向量分別作為行向量組成特征矩陣P;
⑤將協(xié)方差矩陣與特征矩陣相乘,建立k個特征向量的系數(shù)矩陣,即Y=PX;
針對接口復雜度、模塊調(diào)用數(shù)和數(shù)據(jù)傳輸量3個指標,使用上述主成分分析步驟計算它們的權重。
(3)利用求得的權重,使用式(2)~式(4)分別計算3個指標x,y,z的評價指數(shù)f(x)、g(y)和h(z)。
(2)
(3)
(4)
其中,m表示指標數(shù),ai,bi和ci分別表示3個指標的權重。xi,yi和zi分別表示第i個指標。
(4)最后,結合式(2)~式(4)計算最終的代碼耦合度C,如式(5)所示:
(5)
程序依賴圖PDG是源代碼的一種圖形表示,它包含了代碼的數(shù)據(jù)流和控制流。圖2是一段C++源代碼的PDG示例,其中節(jié)點表示代碼的編程表達式、變量、條件和方法調(diào)用;邊表示節(jié)點之間的依賴關系,即數(shù)據(jù)或控制依賴。其中數(shù)據(jù)依賴性可以用來捕獲源代碼函數(shù)的抄襲,控制流則顯示了源代碼的內(nèi)部邏輯。
Figure 2 Control flow and data flow of PDG圖2 PDG控制流和數(shù)據(jù)流
圖3所示為程序依賴圖的特征提取過程。首先使用預處理技術將PDG轉換為沒有噪聲數(shù)據(jù)的小實例。通過采用將PDG分解為令牌的方式,計算每個特征的頻率。其中,預處理步驟包括清理、實例、選擇和轉換。數(shù)據(jù)清洗用于刪除源代碼分類中不需要的數(shù)據(jù),如特殊數(shù)據(jù)符號、數(shù)字和標點符號。轉換過程將源代碼分解為有用的功能。其中,詞干、停用詞和頻率參數(shù)用于在轉換階段提取有價值的特征,詞干提取用于把一組單詞還原成它的詞根形式,頻率信息指示每個元素在不同的源代碼中出現(xiàn)的次數(shù)。
Figure 3 PDG feature extraction process圖3 PDG特征提取處理過程
預處理后的PDG特征包含清洗后的信息與頻率。為了達到更精確的分類,本文將PDG實例轉換為加權特征,用于放大單個文檔和多個文檔中每個特征的重要性。然后使用局部和全局加權技術檢索每個特征的權重。例如,比較3個源代碼(C++、Java和Python)文檔時,局部權重提取了一個文檔中包含的每個功能的重要性,而全局權重則反映了所有文檔中每個功能的重要性。
這些功能有助于對每種編程樣式的每個功能進行評分和排名。該過程為代碼作者預測提供了一個方向,即哪些特征對于準確地預測更有價值。本文將TF-IDF功能用于全局權重,將術語頻率的對數(shù)用于局部權重,則術語頻率表現(xiàn)為每個令牌的出現(xiàn)次數(shù),如式(6)所示:
(6)
其中,tf表示詞頻,t表示詞條,f表示頻率,d表示單個文檔,ft,f表示詞條t在局部條件下的頻率。逆文檔頻率如式(7)所示:
(7)
其中,t表示詞條,d表示單個文檔,D代表語料庫中所有文檔的集合,N代表文檔數(shù)量。數(shù)學上TF-IDF的定義如式(8)所示:
tfidf(t,d,D)=tf(t,d)×idf(t,D)
(8)
為了在較大的數(shù)據(jù)集上獲得更好的性能,本文改進了word2vec[15]的神經(jīng)網(wǎng)絡,提出了一種基于代碼耦合度和PDG特征結合的神經(jīng)網(wǎng)絡模型(CPNN)。與傳統(tǒng)的機器學習模型相比,當訓練樣本足夠多時,神經(jīng)網(wǎng)絡可以從結構化數(shù)據(jù)中獲得更復雜的概念和關系,并且擁有更快的訓練速度。
圖4所示為本文模型結構。首先,由于代碼中的數(shù)據(jù)流和控制流的數(shù)量很多,所以將PDG的多個實例特征作為輸入。為了加快計算速度,在訓練迭代時使用500個隨機樣本中的實例作為輸入。
Figure 4 Structure of CPNN model 圖4 CPNN模型結構
此外,本文將每個實例轉換為可以傳遞給網(wǎng)絡的數(shù)字形式,將令牌嵌入到矩陣Rd中,即將這些實例連接起來,形成一個三維的上下文向量。首先,形成的三維特征矩陣是隨機的,它們的值在神經(jīng)網(wǎng)絡模型訓練中調(diào)整。然后,具有雙曲正切(tanh)功能的全連接層將三維的原始實例特征向量轉換為一維的上下文向量,這樣可以加快模型的收斂速度。每段代碼對應一組向量,并且將這段代碼的耦合度與轉換后的向量合并為一組一維向量。其中,tanh雙曲正切函數(shù)是一種單調(diào)非線性的激活函數(shù),輸出的值在(-1,1)內(nèi),這樣增加了模型的表現(xiàn)力。
在下一步中,通過注意力機制[13]將單個的向量聚合為代碼片段的表示形式。注意力機制計算了組合上下文向量的加權平均值,其主要工作是計算每個向量的分量權重。本文使用了注意力的一個簡單版本,用一個可訓練的向量αatt表示。對于實例向量ctxk及其耦合度Ck,其權值wk的計算如式(9)和式(10)所示:
attk=ctxk·Ck·αatt
(9)
(10)
其中, |ctx|表示實例向量數(shù)量。每個代碼段向量都包含一組實例向量權值,其代碼段的表示形式如(11)所示:
(11)
最后,一個帶有Softmax激活的全連接層對源代碼作者做出預測。本文使用代碼向量來執(zhí)行標簽的預測,因此首先定義一個用來學習部分訓練集的標簽詞匯向量tagsi,然后用代碼向量和標簽之間的點積進行預測,如式(12)所示。
(12)
其中,J表示測試集的數(shù)量,tagsj表示第j個測試集的標簽詞匯向量。
本文數(shù)據(jù)集來源于谷歌編程競賽GCJ(Google Code Jam),其中包含3種不同的編程語言,即C++、Java和Python。采用2008年~2019年參加競賽的程序員的源代碼數(shù)據(jù),代碼數(shù)量分布如圖5所示。本文收集的數(shù)據(jù)集的特點是每段程序與其作者一一對應,以便于在模型訓練時進行交叉驗證。
Figure 5 2008~2019 competition source codes圖5 2008~2019年競賽源碼圖
本文使用F-measure評估CPNN模型的分類性能,用模型精度度量作者歸屬的準確率,評估指標如式(13)~式(16)所示:
(13)
(14)
(15)
(16)
其中,真陽性(TP)表示事實為真判定為真的樣本數(shù)量,假陽性(FP)表示事實為假判定為真的樣本數(shù)量。相反,真陰性(TN)表示事實為假判定也為假的樣本數(shù)量,假陰性(FN)表示事實為真判定為假的樣本數(shù)。
為了驗證本文提出的耦合度對于作者歸屬的影響,對比CPNN模型在PDG特征、PDG與耦合度特征結合2種不同情況下的預測作者歸屬的準確率。在200名程序員的C++源代碼數(shù)據(jù)集上的對比結果如圖6所示。
Figure 6 Feature selection analysis圖6 特征選擇分析
圖6顯示了在200名程序員的C++源代碼上,PDG特征的準確率在訓練中由最初的98%逐漸下降到了96%,而耦合度和程序依賴圖特征結合比單一的PDG特征擁有較高的準確率。因此,本文提出的耦合度特征能夠對源代碼作者歸屬判別產(chǎn)生較好的影響。
為了驗證CPNN模型的高效性,本文將模型與解決相同問題的其它3個模型進行了比較,結果如圖7所示。
Figure 7 Comparison of F-measure between CPNN model and baseline models圖7 CPNN模型與基線模型F-measure 對比圖
圖7顯示出CPNN模型在較短的時間內(nèi)達到了穩(wěn)定的最佳結果,而基于卷積神經(jīng)網(wǎng)絡(CNN)預測名稱[16]、長短期記憶(LSTM)網(wǎng)絡預測C#源代碼[17]和基于抽象語法樹路徑(Paths)的詞向量網(wǎng)絡[18],前期訓練速度較平緩,并且到達最優(yōu)值的時間較長。因此,本文提出的基于耦合度的PDG網(wǎng)絡模型達到最佳F-measure的速度快于其它3個基線模型。
將本文的工作與已有的相關研究進行準確率對比,結果如表1所示。
Table 1 Classification accuracy comparison CPNN model with other models
在文獻[10]中,作者使用隨機森林RF(Random Forest)技術對C++源代碼中的程序員進行預測。在數(shù)據(jù)集包含35名程序員時,準確率為99%,1 000名程序員時準確率下降到92%。因此,隨著程序員數(shù)量的增多,網(wǎng)絡模型預測的準確率會降低。類似地,文獻[19]中使用卷積神經(jīng)網(wǎng)絡(CNN),在745名程序員的2種語言源代碼上平均準確率達到了95%,另外在1 000名程序員的Python源代碼上準確率達到了93%,Java源代碼上準確率達到了94%。文獻[11]使用循環(huán)神經(jīng)網(wǎng)絡(RNN)模型在3種不同語言源代碼上的平均準確率達到了90%。文獻[13]使用基于路徑的神經(jīng)網(wǎng)絡PbNN(Path-based Neural Network)在少量程序員的單一語言源代碼上達到了97%的準確率。本文采用基于耦合度和PDG特征網(wǎng)絡(CPNN)模型,在200名和1 000名程序員的不同類型源代碼上,平均準確率達到了98%和95%,在跨語言類型和準確率方面都優(yōu)于其它4種模型。
本文提出了一種基于耦合度和PDG混合特征的神經(jīng)網(wǎng)絡模型。每名程序員都有不同的編碼樣式,即控制邏輯流。PDG功能僅可以用于提取有關控制流邏輯的隱藏模式以及不同編程代碼中的數(shù)據(jù)變化,而源代碼模塊間的耦合程度很大程度上決定了一名程序員的編碼水平。將這些PDG特性和耦合度特征進一步用作深度學習的輸入用于捕獲編碼樣式以識別程序員。本文使用一種自然語言的處理方式,即采用CPNN預測不同類型源代碼的作者。與卷積神經(jīng)網(wǎng)絡和基于抽象語法樹路徑的網(wǎng)絡進行比較,結果表明了CPNN模型的高效性,并且在1 000名程序員的Java、C++和Python 3種不同類型的源代碼數(shù)據(jù)上,準確率達到了95%。
實驗結果表明,本文提出的基于耦合度的作者歸屬判別模型在準確率方面優(yōu)于許多已有的代碼身份識別的神經(jīng)網(wǎng)絡模型。然而,它仍然存在一些不足,隨著數(shù)據(jù)規(guī)模的增加,PDG特征預處理消耗的成本會增加,并且會造成程序員特征集的不平衡。其次程序員的編程風格有可能隨時間發(fā)生變化。因此,尋找較低的成本提取更能表示源代碼屬性的特征方法、消除數(shù)據(jù)不平衡和通過加入抑制過擬合的手段比如Dropout和正則化,解決編程風格隨時間變化的問題成為將來的主要研究方向。