可執行文件的相似度比較

 

二進制可執行文件的相似度分析一直是一個難題。大家都知道,即使是同一份源代碼,使用同一個編譯器,可用不同的編譯參數進行編譯後,代碼也會產生極大的差異。
當發生有人因爲盜用別人的源代碼而產生的侵權後,如果不能夠將二者的源代碼拿出進行比較的話,判斷是否抄襲非常困難。因此,一直以來或多或少,總會有人無 所顧忌的將開放源代碼的軟件拿來加入到自己的軟件中,或者乾脆就是在那些源代碼的基礎上稍加修改和更換了版權信息就宣稱是自己研發的。因爲他們知道,只要 不把自己的源代碼公諸於衆,那麼抄襲就很難判定。
下面我就詳細說一下我採用的分析方法。
2.1 ELF可執行文件相似度分析方法
這次分析起始,我就碰到了一些難題。如果對二進制可執行文件進行基於字節的相似性分析,即使匹配上某些字節,也很難說明兩段代碼的相似性,另外匹配也很容 易受到各種噪音的干擾而產生很低的相似度,可是噪音卻無法被去除。
因此,使最小比較單元具有明確的語義和合理的過濾噪音是我首先要解決的問題。
2.1.1 反彙編
二進制文件的比較難以確定最小單元語義的根本問題在於二進制文件是以字節爲單位,然而每個字節卻沒有特定的含義。你很難說89 e5和83 EC 89中的89相同說明什麼,在這個例子中,前者的89 e5是i386的一條指令,而後者的89則是一個立即數,所以他們相同實際上什麼都不說明。
針對這次分析,由於都是可執行代碼,而且都採用了ELF的文件格式。由於這個特點,我首先將所有操作系統的內核通過objdump反彙編成彙編代碼。這樣 做有一個直接的好處,就是每一行都是一條彙編語句,而每一條彙編語句又是一個程序不可分的最小邏輯單元。這樣,接下來的分析就可以基於行來進行相似性的分 析,因爲每出現一行相同就說明有一個最小的邏輯單元相同,如果出現連續的行相似,那麼就說明有連續的代碼段相似。相同的行越多兩個內核就越相似。
並且經過反彙編後,就避免了因文件內包含的其他無關信息,如字符串、資源文件、數據文件等,對分析結果產生的影響。
這個方法依舊無法避免因編譯參數差異所造成的相似度下降的影響。雖然如此,但是我很幸運,從這次分析的結果看,依舊得到了不低的相似度。
2.1.2 過濾噪音
噪音的出現有很多原因,可能是內存分佈不同、代碼的增刪導致的偏移地址的變化,對相同含義的常量而數值卻不同等等。這些值的差異,可能會造成不同的執行結 果,但是卻對兩段代碼的相似性比較影響不大。請看下列兩個代碼段:

左邊的代碼是來自FreeBSD 5.3內核的,而右邊的代碼來自麒麟2.0.21/18的內核。通過人的分析,我們可以得出這兩段代碼實際上是相同的。可是對於計算機程序比較的時候,就 不盡然。
請注意上述的有顏色的數字。用藍色表示的代碼地址[4]、綠色表示的偏移地址、紅色表示的立即數、深藍色表示的函數偏移地址和粉色表示的函數地址,這些數字的不同,就造成了代碼比較時候的失敗。上述13行代碼,如果就這樣比較的話,只有函數名一行可以匹配。因此雖然是相同的代碼,卻只有7.7%的相似度。下面我們就來去除這些干擾。
首先,我們將代碼行地址、函數跳轉地址和函數偏移地址去除。代碼行所在的地址,實際上是說明了代碼所在內存的位置,內存的位置會隨着代碼的刪改而很容易產 生變動,這些對我們比較代碼邏輯沒有意義。其中有些絕對地址,我們將其替換爲“{Address}”,這樣既不受地址變化的影響,又不至影響了代碼的含 義。
然後我們將綠色的偏移地址替換成特定字符串“{Offset}”。產生偏移地址的原因一般有兩種,一種是結構體,另一種是數組。即使不對結構體刪改,而僅 僅是對結構體的聲明順序的變動都可以造成偏移地址的不同,我們在這裏只關心程序在這裏用到了一個偏移地址,而不關心用的到底是偏移了多少。數組的用法雖然 不常出現,但是即使出現其中的位置也是很容易發生變動的。因此在這裏,我們也將偏移地址的數值替換成統一的字符串。
最後,我們來處理紅色的立即數。當然立即數並不是只有上述的幾種情況下出現,雖然在上述的例子中,兩邊的立即數都完全一樣,單是在某些情況下還是會出現不同。
立即數在程序中一般是常量,而常量有可能是與系統相關的數值,或者僅僅是一個符號,而不在乎具體數值。無論是什麼含義,常量雖然在執行過程中不會改變,在 設計過程中卻很容易發生變動。不過對我們分析代碼邏輯沒有太大的影響,因此,在分析的時候我們對數值進行模糊化,將其替換爲“{Number}”這個特定 字符串。
至此,上述代碼將會變爲:

<freebsd4_sigcode>:    | <freebsd4_sigcode>:
freebsd4_sigcode():                       | freebsd4_sigcode():
call   *{Offset}(%esp)                    |   call   *{Offset}(%esp)
lea    {Offset}(%esp),%eax               |   lea    {Offset}(%esp),%eax
push   %eax                              |   push   %eax
testl {Number},{Offset}(%eax)           |   testl {Number},{Offset}(%eax)
jne    <freebsd4_sigcode+{Offset}>       |   jne    <freebsd4_sigcode+{Offset}>
movl   {Offset}(%eax),%gs                |   movw   {Offset}(%eax),%gs
mov    {Number},%eax                     |   mov    {Number},%eax
push   %eax                              |   push   %eax
int    {Number}                          |   int    {Number}
jmp    <freebsd4_sigcode+{Offset}>       |   jmp    <freebsd4_sigcode+{Offset}>
nop                                      |   nop
現在這兩段代碼的相似度將變成真實的100%。

 

發佈了41 篇原創文章 · 獲贊 7 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章