趙雅霖 雷聚超 唐俊勇
(西安工業(yè)大學計算機科學與工程學院 陜西 西安 710021)
隨著網(wǎng)絡高速發(fā)展,人們對各種軟件的使用也越來越頻繁,軟件給人們生活帶來了極大的便利和巨大的經(jīng)濟效益。在這些利益的背后,很多商家或者個人通過不正當手段來盜取他人的軟件以獲取非法利益,給軟件著作者帶來了很大的損失。商業(yè)軟件聯(lián)盟(BSA)在2018年全球軟件調(diào)查報告中指出,全球PC的軟件盜版率為37%,而中國的軟件盜版率更是高達66%。盜版軟件每年造成的損失高達3 590億美元。可見,盜版軟件嚴重危害了版權所有者的利益。
針對軟件抄襲問題,目前主要有兩種解決方法。一種是對軟件本身的保護,保證軟件不被盜取;另一種是在軟件被盜取之后,通過找到軟件中可以證明軟件版權信息的方法來維權。由于Java具有跨平臺運行和開源的特點,利用逆向分析手段將Java指令恢復為源代碼很容易,因此無法做到對軟件的絕對保護。在軟件被盜取后,目前常見的證明手段是通過軟件水印[1]或軟件胎記[2]的方法。軟件水印是指向軟件代碼中嵌入唯一的標識符的技術手段,盡管在嵌入的時候已經(jīng)對這些信息做了一些隱蔽處理,但目前可以通過多種方法對這些水印進行篡改和移除。
軟件胎記是指程序中一些與生俱來的,固有的特征,它具有很強的抗攻擊能力,無論代碼被進行了怎樣的篡改和迷惑,其胎記依然保持不變。在軟件胎記的研究中,陳林等[3]提出一種將程序中所有k-gram碎片頻率構成向量生成的軟件胎記算法;Xie等[4]則在計算k-gram碎片對單種語義保持變換的頻數(shù)變化率的基礎上,統(tǒng)計出各碎片的抗攻擊值構建胎記向量,以此來判定抄襲行為。以上兩種軟件胎記一定程度上克服了指令集胎記健壯性不強的問題,但并沒有從本質上解決問題,因為造成語義丟失的根本原因在于k-gram算法本身。Schuler等[5]提出了基于Java平臺的API動態(tài)軟件胎記算法。Park等[6]提出了一種由靜態(tài)分析程序后得到的API蹤跡的集合生成的軟件胎記算法,但是目前已有研究者提出了針對系統(tǒng)調(diào)用的攻擊方法使該軟件胎記算法失效。
本文提出了一種基于控制流的靜態(tài)軟件胎記算法。首先,提取中間文件中的控制流結構,并以圖的形式進行存儲。其次,對軟件胎記中的可達方法引用指令進行解引用處理,并對其中的冗余指令進行約減處理,為軟件胎記的比較降低一定的復雜度,以提高胎記的可信性。最后通過一種基于VF2[7]算法的胎記對比模型得出胎記的相似度,進而判定兩軟件是否抄襲。
軟件胎記的概念是由Grover[8]提出的,一個完整的軟件胎記抄襲檢測模型主要由兩個函數(shù)組成:
(1)
式中:BP表示程序P的胎記,extract()表示軟件胎記的特征提取函數(shù),similarity為[0,1]區(qū)間上的實數(shù)。式(1)表明了軟件胎記是由一個軟件胎記提取算法對軟件的一方面或多方面特征提取的產(chǎn)物,軟件胎記的相似度是由軟件胎記比較模型對比兩個軟件胎記計算出的數(shù)值,該數(shù)值在0到1之間,通過該數(shù)值確定軟件是否抄襲。
使用靜態(tài)軟件胎記進行程序檢測分為以下三步:首先,對源程序和可疑程序的源碼或中間文件及可執(zhí)行碼進行靜態(tài)分析,獲取源程序及可疑程序中的一項或多項特征,通過對特征的加工,獲得源程序和可疑程序的軟件胎記。其次,針對提取出的軟件胎記構建一個比較模型,該模型以源程序及可疑程序的軟件胎記為輸入,輸出兩胎記之間的相似值。最后通過相似值判定程序是否抄襲,設α為閾值,P、Q為兩個程序,BP、BQ分別為兩個程序的胎記,則軟件抄襲的判定標準為:
(1) 若comp(BP,BQ)≥1-α,則P、Q為抄襲關系;
(2) 若comp(BP,BQ)≤α,則P、Q為非抄襲關系;
(3) 若comp(BP,BQ)<1-α或comp(BP,BQ)>α,則P、Q關系不確定。
k-gram軟件胎記是由Myles等[9]提出的,主要思想是從程序指令中提取k-gram碎片,將所提取的所有碎片作為一個集合來表示軟件胎記。在提取k-gram碎片時,主要以滑動窗口的方式去提取,即按照其物理相鄰地址去劃分。僅考慮物理關系而不考慮其邏輯關系或內(nèi)部的控制流情況。
軟件胎記的屬性有兩種,分別為可信性和健壯性。
定義1軟件胎記的可信性:設P和Q是非抄襲關系的兩個程序,extract為軟件胎記的提取函數(shù),Bp與BQ分別為兩個軟件的軟件胎記,BP=extract(P),BQ=extract(Q),如果BP≠BQ,則稱extract函數(shù)具有可信性。
定義2軟件胎記的健壯性:設程序P′是程序P的抄襲程序,extract為軟件胎記的提取函數(shù),Bp與BP′分別為兩個程序的軟件胎記,BP=extract(P),BP′=extract(P′),如果BP=BP′,則稱extract函數(shù)具有健壯性。
在系統(tǒng)完成建模之后,通過精確率(Precision)、召回率(Recall)、F度量值(F-Measure)進行評價。
在樣本中,設T為檢測結果正確,F(xiàn)為檢測結果錯誤。TP表示將盜版程序對檢測為盜版程序對,TN表示將非盜版程序對檢測為非盜版程序對,F(xiàn)P表示將非盜版程序對檢測為盜版程序對,F(xiàn)N表示將盜版檢測為非盜版程序對。
精確率:盜版檢測結果的正確率,即檢測結果為盜版的程序對中實際結果確實是盜版的比率,計算公式為:
召回率:盜版檢測結果的正確率,即檢測結果為盜版的程序對中實際結果確實是盜版的比率。計算公式為:
F值計算公式為:
本文提出一種基于程序控制流的軟件胎記,使用該軟件胎記對程序對進行抄襲檢測分為以下三步:
首先,對Java源程序P與Java可疑程序Q通過分析各自對應的Class中間文件獲取其對應的控制流特征,將兩程序的控制流特征加工為各自的靜態(tài)軟件胎記。
其次,構建一種基于VF2算法的控制流特征靜態(tài)胎記對比模型,計算兩胎記之間的相似度作為源程序與可疑程序之間的相似度。
最后,設置檢測閾值α,使用式(2)所示的判定標準得出兩程序是否抄襲的結論。
其流程如圖1所示。
圖1 抄襲檢測流程圖
目前,Java的字節(jié)碼指令共有203個,按照功能可分為10種[10],但從控制流的角度來說,本文將指令分為3種:
單后繼指令:該種指令在讀取完該指令及操作數(shù)后將下一個字節(jié)翻譯為指令。
雙后繼指令:該種指令必定附帶操作數(shù),并且在指令附帶的操作數(shù)中必定帶有一個offset部分。
非正規(guī)金融組織迎合人們追求發(fā)家致富的心理,通過預期高收益誘使大量的農(nóng)村資金進入,當其經(jīng)營狀況不善時,利息只能靠新發(fā)展的存款來支付,當沒有足夠的新存款來付息和還本時,就會發(fā)生嚴重的社會風波,影響社會安定。由于缺乏金融風險意識,農(nóng)村經(jīng)濟主體這種逐利的投機心理甚至被一些不法分子所引導,脫離正常的金融活動,演變成非法集資等各種經(jīng)濟詐騙行為,導致農(nóng)村金融秩序混亂和農(nóng)村社會動蕩。
多后繼指令:該種指令在整個體系中只有兩條,分別是lookupswitch與tableswitch,這兩條指令都是由switch結構編譯而來。當switch結構中的case值比較稀疏時,會生成lookupswitch,反之則會生成tableswitch。這類指令的后繼指令數(shù)目不定,由case分支的數(shù)目決定。
Java語言用這三類指令即可構成程序中最基本的三種控制流結構:分支結構、循環(huán)結構和順序結構。其中雙后繼指令和多后繼指令構成了分支結構中的if和switch結構,順序結構和循環(huán)結構均由單后繼指令構成,稍有不同的是循環(huán)結構中一定會有一個goto指令,其后繼是指向之前的某條指令。所有程序的控制流結構均由以上三種基本控制流結構組合而成。
Class文件作為編譯的中間文件,為JVM提供了所有運行期必要的符號信息。一個Class文件中最主要信息的就是Method數(shù)組中的CodeAttribute,該屬性表記錄了源程序經(jīng)編譯后產(chǎn)生的指令數(shù)組,JVM在運行期通過遍歷該數(shù)組來執(zhí)行一個方法。由于JVM的字節(jié)碼指令體系是基于棧的變長指令體系,所以本文使用map的結構來存儲程序的控制流信息。
本文獲取方法M的程序控制流胎記步驟如下:
(1) 指定jar文件J和迭代深度Rmax,解壓后獲得工程P,設置Rtemp=1。
(2) 根據(jù)給定的方法簽名Msign和class文件C,在工程P中獲取方法M的指令數(shù)組,記為code,code首元素必定為一個指令。
(3) 遍歷code,根據(jù)虛擬機字節(jié)碼指令規(guī)范(下稱指令規(guī)范)將字節(jié)數(shù)組code中的信息翻譯到instruction中,步驟如下:
① 建立一個跟code等長的數(shù)組,記為instruction,建立游標index=0。
② 將codeindex位置元素拷貝至instructionindex中,查找指令規(guī)范翻譯codeindex為i0,查找指令規(guī)范得到i0的附帶操作數(shù)有l(wèi)en個字節(jié),codeindex+1至codeindex+len即為i0指令的操作數(shù),將操作數(shù)拷貝至instructionindex元素。
③ 將index向后移動len+1長度,重復②,直至遍歷完code。
步驟(3)過程示意圖如圖2所示。
圖2 instruction生成過程示意圖
(4) 遍歷instruction,初始化其中所有不為空的元素的前驅和后繼,前驅為0的元素記為起始節(jié)點start,后繼為0的元素記為終止節(jié)點end。記初始化前驅后繼后的instruction為featurestruct。
對如圖3所示的示例程序使用本文提出的胎記提取算法提取出的胎記如圖4所示。
圖3 示例程序
圖4 程序胎記示意圖(迭代深度為2)
為了對源程序和可疑程序的胎記進行對比,需要建立一種以圖同構算法為核心的對比模型,而圖同構問題是NP問題,為此,本文提出一種約簡程序控制流胎記的方法。
(1) 將胎記B中instruction的所有元素的狀態(tài)初始化為MEANINGLESS。
(2) 迭代遍歷胎記B中的instruction,設置所有前驅數(shù)目不為1或后繼數(shù)目不為1的元素狀態(tài)為MEANINGFUL,特別地,若元素i的后繼數(shù)目大于1,則將i的每個后繼元素的狀態(tài)也設置為MEANINGFUL。
(3) 迭代遍歷胎記B中的instruction,記正在遍歷的元素為i,若i的狀態(tài)為MEANINGLESS,則將i的唯一后繼suc加入至其前驅pre的后繼集中,將i從其前驅pre的后繼集中刪除,再將i刪除。
本文提出一種使用程序控制流胎記進行抄襲檢測的方法:
(2) 根據(jù)2.1節(jié)所述方法及步驟(1)中參數(shù),生成源胎記Bsrc與可疑胎記Bd。
(5) 使用式(2)計算Msrc與Md的相似度simsrc,d,按照抄襲判定標準和檢測閾值α給出最終結論[11]。
(2)
本文選擇了4款在業(yè)界廣泛使用的開源測試與分析工具Junit、JMeter、selenium、findbugs,設置迭代深度Rmax=3,檢測閾值α設置為0.3,放大率θ為10,偏置bias為-2。
本次實驗檢測同一軟件不同版本之間的對應方法對與不同軟件之間的隨機抽取方法,對于某軟件,在由版本1演進至版本2時,較多數(shù)類及方法將會保留,故同一軟件對應不同版本間的對應方法默認結果為抄襲,即陽性。對不同軟件間的隨機抽取方法,默認其為獨立開發(fā),即陰性。
為驗證本文提出的胎記可信性,抽取8對陽性程序與4對陰性程序檢測其實驗結果,如表1所示。
表1 程序控制流胎記可信性結果驗證
由表1可以看出:本文提出的胎記對受試的14對程序出現(xiàn)2個誤判,正確率為85.7%,精確率為87.5%,召回率為87.5%,F(xiàn)度量為87.5;而SKB對受試的14對程序出現(xiàn)3個誤判,正確率為78.6%,精確率為85.7%,召回率為75%,F(xiàn)度量為0.8,兩種胎記受試結果對比如圖5所示。
圖5 程序控制流胎記與SKB效果對比圖
為了驗證本文提出的胎記的健壯性,從4個軟件中隨機尋找各50個方法,使用proGuard[12]對共計200個受試對象進行迷惑,使用本文提出的胎記對這200對程序對進行抄襲檢測,結果如表2所示。
表2 軟件胎記健壯性結果驗證
由于是健壯性檢測,故受試程序對的實際結果都是陽性,不存在FP與FN,對共計200對經(jīng)過迷惑的程序對出現(xiàn)23對誤判,整體正確率為88.5%,說明本文提出的胎記具有一定的健壯性。
本文通過靜態(tài)分析程序的指令流獲取程序的控制流軟件胎記,使用VF2算法來計算胎記的相似度,對常見的四種代碼檢測和分析工具進行胎記可信性和健壯性對比實驗。實驗結果表明,本文提出的胎記較傳統(tǒng)SKB胎記更具可信性,由于本文的胎記是以中間指令分析為基礎,所以更具廣泛適用性,但由于本方法未考慮節(jié)點間的標簽,所以仍存在語義丟失的可能。下一步將在兩節(jié)點匹配時考慮節(jié)點內(nèi)部指令序列的相似對比因素。