付修鋒 賈張濤 楊鐵湃 安恒 金玉川 耿宏偉



關鍵詞 開源軟件 程序對比 代碼溯源 代碼復用
1引言
在行業規模不斷擴大的過程中,軟件產品功能必須隨著用戶需求的增加而不斷地做出調整[1] 。而功能的迭代使得軟件內部業務邏輯變得越來越復雜。
這就要求在資源有限的情況下,開發人員團隊必須快速對這些復雜的需求做出應對,以滿足如今充滿競爭的軟件市場。
起初,由一些IT 行業人員自發地用博客的形式將開發過程中的心得、經驗分享和代碼工程發布到互聯網中,構成了早期的開源社群。如今,這些小型社群吸收了越來越多開源的支持者,他們是多方位、多元化、豐富的開源項目代碼的來源,從而集成了不斷壯大的開源生態系統。市面上熱度最高的開源社區有GitHub,CSDN,StackOverflow,據2021 年GitHub 年度報告顯示,2021 年GitHub 工程數目已經達到1 億7000 萬,用戶年總量增長30%,其中中國用戶755 萬,位居全球第二。豐富的、易獲取的代碼來源和龐大的用戶群體,方便了程序員快速、準確地找到已有的代碼。通過對目標代碼的復用和調試,我們可以在短時內解決項目模塊開發“冷啟動問題”,也減少了時間上的開銷。代碼復用即開發者通過復制、粘貼或引用調包等方式使用了已有的開源代碼[2~4] 。
開發人員在享受代碼復用帶來高效率開發快樂的同時,也同樣意識到了背后所存在的安全隱患和侵權問題。最著名的就屬Oracle 與Google 由于代碼復用長達數年的訴訟官司。從此可以看出,開發者必須了解項目工程中的復用代碼,哪怕僅僅是幾行代碼,都可能給公司帶來不可估量的損失,也會給后期項目維護帶來額外管理支出。
代碼溯源就是在復用代碼與已知代碼庫中,從代碼的相似性高的角度出發,完成代碼追溯工作。對此,本文利用NLP 自然語言技術提出了一種精準快速模塊溯源方法。通過統一代碼中的特定標識,將完整檢測樣本切分。在每一塊函數代碼塊映射成唯一的特征向量的基礎上,利用新穎的工程相似計算方法得出代碼溯源。實驗結果表明,該方法能滿足開源代碼復用中相似度檢測的需求,且有著較高的準確率。
2算法描述
由于一般商業軟件普遍采用“閉源”方式并不會提供軟件源代碼,所以我們主要研究二進制代碼溯源。在本文中,我們將已知項目工程拆分成文件級和函數級兩個維度進行溯源。通過歸納,總結二進制文件的反匯編文件中函數跳轉標識,將工程文件中方法函數切分成多個代碼塊,并儲存在多個分詞序列中。利用NLP 技術將每一條代碼序列映射成統一潛在空間的二進制特征向量。我們也可以獲取待測工程代碼二級制特征向量。兩者之間自然語言相似時,其特征向量之間也應該具有相似性,由此判斷兩個工程的溯源關系。快速溯源模型共包括3 個階段,即溯源數據預處理階段(圖1)、溯源函數特征提取階段以及溯源函數復用比對階段。
溯源數據預處理階段是將二進制代碼文件用函數跳轉標識進行切分,再對每一種函數方法利用統一的變量格式進行標準化,最終獲得多個統一格式的函數代碼塊。
函數溯源函數特征提取階段是以函數為基本單位,對函數代碼進行語法、語義等數據分析,獲取函數文本中隱含的函數特征向量。函數特征向量必須滿足以下特點:(1)相同的函數指令序列產生的函數特征編碼應該是相同的,不同的函數指令序列應產生不同的函數特征編碼;(2)匯編代碼通過特征編碼模型構造出來的與函數代碼對應的特征向量是唯一的,即每一個函數特征向量都能很好地表達對應函數塊的特征;(3)相似的函數指令序列應產生相似的函數特征編碼,以滿足相似函數的判定需求。
工程相似度計算階段是計算兩個對比函數特征向量的相似性,以判定溯源等級。
2.1溯源數據預處理
溯源數據預處理的對象是匯編代碼,所以要用反編譯工具將二進制樣本逆向獲取匯編代碼;在項目工程中,多個相互調用方法的組成特定的功能函數,而多個特定函數代碼塊集成一個匯編文件[5] 。
用P 表示獲取的匯編代碼,F 表示功能函數,L 則表示代碼塊。由此可得,P = {F1,F2,Fn }, F = {L1,L2,L,Lm }。隨后,我們對每一個代碼塊進行了標準化,具體如圖1 所示。
在匯編代碼中有許多代碼函數的起始和終止的標識符,如“near”和“end”,由此可以將匯編文件切分成多個代碼塊。匯編文件切分的代碼如下:
在函數loc_4023E7 中,根據跳轉指令“jnz”與跳轉地址“loc_4023E7”將其劃分為2 個代碼塊,對第一個代碼塊,依據函數位置命名,如表1 所列。
2.2使用NLP 技術進行代碼特征的提取
通過跳轉符拆分代碼塊序列,利用NLP 算法為開源工程中的每一個標準化工程文件序列、函數塊序列、代碼塊序列分別進行編碼,獲取對應的特征向量Embding。在函數特征構造算法中,每一步的輸出均為下一步的輸入,具體實現步驟可分為5 個部分。
(1)將匯編文本中的代碼塊序列映射到潛在特征空間中:使用傳統哈希算法將每個特征分詞映射成一組長度為64 位的二進制數列。
(2)統計分詞權重:在一段代碼分詞序列中,某一個分詞出現的次數越多,可能其對整個代碼特征提取的影響越大。通過統計出分詞例如“sub,mov,js”出現的次數作為該分詞的權重值。
(3)獲取每一個分詞的加權向量:本文使用的是將“統計分詞權重”中獲取的分詞權重值與分詞哈希值相乘獲取分詞的加權向量。例如,通過哈希函數得到“mov”的6 位二進制哈希向量(1,1,0,0,1,0),序列中“mov”出現的次數是6,其中比特值為0 的位替換成-1,則“mov”的加權向量是(6,6,-6,-6,6,-6)。
(4)合并:將上述加權向量的對應位相加合并,壓縮數據,此部分的輸出是一個含權值向量。
(5)降維:將合并后的向量降維,若該位的數值大于0,則出1,否則該位出0,最終計算出特征向量值。
2.3工程相似度計算
常見用來衡量代碼相似度的指標主要有以下幾個:歐氏距離、余弦相似度等。由于本文提出了一種基于函數代碼塊的特征提取方法。因此,將兩個函數塊分別得到對應的函數特征向量,其計算原始文本相似程度則可以通過計算它們特征向量之間的距離來獲取。該距離是指函數指紋對應比特取值不同的比特數(函數指紋長度需相同),具體計算公式如下:
公式中X 和Y 表示兩個長度為n 位的函數指紋,M 表示函數指紋X 與Y 的異或結果,m 表示異或結果M 的第i 位的值。首先對兩個函數指紋X,Y 做按位異或操作,得到異或碼M。此步驟中,如果兩個函數指紋對應位相同,則出“0”,若不同則出“1”。然后對M 的每一位求和,即可獲取兩函數指紋間的距離。我們通過大量的參考文獻的統計,同時考慮方法的精確度和運行效率的情況下,將評估兩個特征向量之間的距離閥值設定為3,即當兩個代碼特征向量通過距離運算得到的結果小于等于3 時,我們認為這兩個函數相似。
輸入:兩個工程的源代碼
輸出:兩個工程的代碼相似度S
3實驗結果與分析
本文從工程相似度角度出發進行程序對比分析。為了證明方法的函數溯源能力,我們采用評估函數復用的準確率作為模型的評估指標。
二進制代碼溯源驗證主要通過針對同一軟件不同編譯版本和已知存在復用事實的不同軟件開展二進制代碼溯源。具體驗證過程如下:(1)將Chrome 瀏覽器(版本號49.0.2623.112) 入庫;(2) 上傳Chrome瀏覽器(版本號44.0.2403.155)開展二進制代碼溯源;(3)上傳Redcore 瀏覽器(版本號49.1.2623.213)開展二進制代碼溯源。
通過前期數據處理分析,Chrome 瀏覽器44.0、Redcore 瀏覽器49.1 文件級和函數級個數如表2 所列。我們將Chrome 49.0 中的函數作為基準代碼,Chrome 44.0,Redcore 49.1 中的每個函數作為溯源的對比函數。利用特定的對比方法判定兩個特征向量的相似性,對比結果如表3 所列。
利用代碼克隆檢測溯源方法對兩個工程中每一個函數進行編碼得到函數特征向量,發現Chrome44.0,Redcore 49.1 與Chrome 49.0 文件級別高相似個數都是7 個,兩者函數級別高相似函數個數分別為14個和13 個。同時,由手動查看三者之間相似文件和函數統計得出,該方法溯源準確率為95.77%,因此本文的溯源方法具有很高的準確率。
在待檢測的兩個工程中,存在大量的相似函數調用關系,例如chrome.dll,chrome_elf.dll 就被調用。導致它們雖然從界面上和操作流程上不大相同,檢測結果卻非常相似。因此,通過函數塊相似度計算判定溯源,比手工判定更加準確和高效。
4結論
在工業軟件項目的開發過程中,由于復用代碼的同源判定通常依賴于人工經驗識別,導致同源判定工作效率較低。針對該問題,本文提出了一個能夠快速準確提取二進制工程文件特征的模型,并在此基礎上設計了一種代碼相似度對比方法,完成待檢測代碼的溯源。在已知代碼數據集的實驗表明,該方法通過提取代碼特征,有助于減少出現復用代碼溯源誤報、漏報的情況。