當前位置:
首頁 > 新聞 > Karta:一款新的二進位文件分析和對比插件

Karta:一款新的二進位文件分析和對比插件

前言

「Karta」(在俄語中的意思是「map」)是IDA(一個靜態反編譯程序)的源代碼輔助二進位對比插件,該插件的開發是為了對比一個非常大的二進位文件(通常是固件文件)中的開源庫的符號。對於那些每天都要處理固件文件的人來說,重複逆轉net-snmp(一個免費的、開放源代碼的SNMP實現,以前稱為UCD-SNMP)非常浪費時間的。所以,人們急需一種對比插件工具,來識別使用過的開源碼,並在IDA中自動對比它們對應的符號。

人們最初的開發重點是放在了快速對比的過程上,但在實踐中,就發現即使他們試圖逆向的二進位文件包含超過10萬個函數,也僅僅會對比一個包含300個函數的庫,而光這個過程就需要等待幾個小時。

事實證明,出於性能原因而部署的啟發式演算法對對比結果也有很大影響。Karta的假陽性率非常低,而真陽性率很高。這使得它甚至可以用於對比中小型二進位文件。

因此,有人認為Karta可以成為開發人員工具箱中的一個重要工具,並應用於以下場景:

1.偵察階段:確定二進位文件中使用的開放源代碼(包括它們的版本);

2.優化Clutter:對比使用的開源的符號,從而為逆向工程節省時間;

3.查找用例:使用已用過的開放源代碼列表,其中的符號已經在二進位文件中對比,以便在可執行或固件文件中輕鬆查找用例。

KARTA

如前所述,Karta是IDA*的源代碼輔助二進位對比插件,該插件有兩個重要的用途:

1.標識:對靜態編譯的開源代碼的確切版本進行標識;

2.對比:對比已標識的開源代碼的符號;

Karta現在是開源的,我可以在Github中找到。

由於在不同系統結構上編譯開源庫是非常複雜的事情,因此人們通常會通過讓插件獨立於系統運行來消除此複雜的過程。例如,如果我想要對比libpng開源的1.2.29版本(這是我在HP OfficeJet固件中使用的版本),我所要做的就是從Github複製它並在(本文使用的x86)設備上編譯它。編譯完成後,Karta可以生成描述庫的規範.json配置文件。使用此配置,即使固件已編譯為Big-Endian ARM Thumb模式,Karta插件現在也可以在固件中成功找到庫。

Karta由模塊組成,IDA反彙編模塊可以被任何其他反彙編模塊替換。

尋找用例

雖然目前,我已經為介紹了幾個插件用例,但在熱門程序中查找用例可能是最有趣的用例。以下是我在研究過程中發現的兩個真實用例。

HP OfficeJet固件

由於在對HP傳真固件研究期間,我需要將用例用作調試漏洞( CVE-2017-9765 )。該漏洞允許攻擊者遠程破壞 SOAP Web 服務後台進程,並在受害者設備上執行任意代碼。在完成Karta的開發之後,我返回固件並檢查了Karta是如何幫助我進行漏洞研究的。

標識符插件會告訴我固件中使用的開源庫是:

·libpng: 1.2.29

·zlib: 1.2.3

·OpenSSL: 1.0.1.j

·mDNSResponder: unknown

·gSOAP: 2.7

我可以看到確實使用了gSOAP(編譯工具提供了一個SOAP/XML 關於C/C 語言的實現,從而讓C/C 語言開發web服務或客戶端程序的工作變得輕鬆了很多),快速的CVE搜索顯示它包含一個關鍵的漏洞:CVE-2017-9765 。

在快速編譯了此版本的gSOAP的配置之後,我運行了對比器並導入了對比的符號。如下圖所示,我可以看到易受攻擊的代碼函數soap_get_pi與Karta對比。

反編譯的soap_get_pi函數,與Karta對比

這對Karta插件來說是一個非常好的消息,這意味著它可以在真實的場景中正常工作(可惜我也只是在調試漏洞完成後才知道這一點)。

普通的閉源程序:TeamViewer

雖然在固件中輕鬆找到用例很方便,但在Windows PC上使用的日常程序中,實現這一點就很難了,為什麼?在閱讀Project Zero在WebRTC上的博客文章時,我發現他們在一個名為libvpx的庫中發現了一個漏洞:CVE-2018-6155,它可以攻擊VP8視頻編碼技術源代碼,從而影響了VP8庫libvpx而不是WebRTC中的代碼。因此,該漏洞有可能影響使用除WebRTC之外的所有其他庫的程序。

這看起來很有趣,因為Project Zero特別指出這個漏洞「有可能影響使用除WebRTC之外的所有其他庫的程序」。由於我在計算機上安裝了TeamViewer,看起來它應該使用相同的開源庫,讓我來看看。

為此,我在IDA中打開了TeamViewer.exe,並在分析過程中開始運行它。為此,我還專門下載了最新版本的libvpx(1.7.0),為它編寫了一個簡單的標識符,並將其添加到Karta中。由於IDA完成這個分析的時間太長了,我直接將其停止,並運行Karta的標識符插件。從中確定的開放源代碼包括:

·zlib: 1.2.5

·mDNSResponder: unknown

·libjpeg: 8b

·libvpx: 1.6.1

TeamViewer不僅使用libvpx(編解碼器開發包),而且都是2017年1月的舊版本。

以下是Google發布的補丁,其中我感興趣的函數是vp8_deblock,如下所示。

vp8_deblock函數的代碼片段,易受CVE-2018-6155攻擊

現在讓IDA恢複分析進程,然後繼續編譯libvpx版本1.6.1的Karta配置。配置完成後,在IDA完成對二進位文件的分析之後,我運行了Karta的對比器插件。如下所示,對比器發現了易受攻擊的函數:

Karta的對比結果顯示它與易受攻擊的函數相對比(用藍色突出顯示)

在我將結果導回IDA之後,我可以通過數字常量清楚地驗證這是正確的對比。

如IDA Pro所示,易受攻擊的函數與我的插件相對比

此時,我已經成功在TeamViewer程序中發現了一個漏洞,甚至還知道了在調試時確切地放置斷點的位置。

整個漏洞發現過程大約需要2個小時,因為IDA的分析非常耗時,原因在於TeamViewer是一個非常大的程序,包含超過143000個函數。

Karta是如何運行的?

101個函數的二進位對比

簡而言之,二進位對比可以歸結為以下這個過程:如果我們想檢查兩個函數(一個來自已編譯的開源,另一個來自二進位)是否表示同一個函數,為了能夠比較這兩個函數,我們需要將它們轉換成用統一的方法表示的內容,通常稱為「規範表示」。這種表示通常包括我們從函數中提取的一組特性:數字常量列表、字元串列表、彙編指令的數量等等。

當我們嘗試對比一組相關函數時,例如,在一個已編譯的開源項目中,我們將多餘的信息存儲在規範表示中,以便對函數之間的關係進行編碼:被調用函數列表(被調用者),列表調用我的函數(調用者)等。使用此信息,我可以嘗試根據控制流圖(CFG)中的規則或位置對比兩個函數。

此時,我們可以使用一些傳統的二進位對比工具,如BinDiff或Diaphora。雖然每個對比工具都有自己獨特的巧妙對比啟發式演算法,但它們都基於相同的規則,以比較兩個規範表示並對結果進行評分,這意味著二進位對比工具首先將所有函數轉換為它們的規範表示形式。

避免內存崩潰

當分析一個包含大約65000個函數的二進位文件時,比如我使用的OfficeJet印表機的固件,為所有函數構建規範表示的過程根本無法擴展。它需要很長時間(通常超過一個小時),並且可以在磁碟空間中消耗超過3GB的空間。這意味著,稍後將此數據集載入到內存中常常會導致對比程序崩潰。

如果我們想要對比巨大的二進位文件中的任何內容,就需要改變對比策略。由於開源庫相對較小,我可以將問題描述為:

·M——我的開源函數數量;

·N——我的二進位文件中的函數數量;

由於我希望在大小為N的二進位中對比數量為M的函數,其中M

二進位地址空間的圖解,我試圖在其中對比我的庫

鏈接器位置的尋找

如果我將稍後討論的特殊情況排除在外的話,我可以跳過編譯過程直接使用以下步驟進行鏈接:

1.編譯器獨立編譯每個源文件,並創建一個對比的二進位文件(.o用於gcc,.obj用於visual studio);

2.鏈接器將所有二進位文件組合成一個二進位大對象(一個可以存儲二進位文件的「容器」);

3.在鏈接階段,此大對象將按原樣插入最終程序或固件;

這導致兩個重要的結果:

1.編譯後的源代碼包含在固件或可執行文件中的一個連續的大對象中;

2.一旦我們找到了這個大對象的一個標誌(被稱為錨點),我們就可以根據這個大對象中應該包含的函數的數量來推測二進位中這個大對象位置的下限和上限。

其實,這也是Karta所依據的基本規則。

構建函數關係地圖

Karta是一個源代碼輔助工具,通過利用源代碼中的信息,我可以構建一個映射:哪些函數包含在哪個文件中,以及該庫包含哪些文件。

以下是對比二進位文件庫的過程:

1. 從一個基本標識符腳本開始,該腳本檢查二進位文件是否使用了該庫,以及使用的版本——O(N)時間和O(1)內存消耗;

2.識別後,掃描二進位文件以搜索錨函數——O(N)時間和O(1)內存消耗;

3. 使用定位錨函數來放大二進位函數的推測範圍,這些函數可能是庫的一部分—O(1)時間和O(1)內存消耗;

4.此時,所有邏輯都將出現在聚焦範圍內,其大小為O(M)。

以下是具體的示例:

1.我有一個含有322個函數的庫,利用此方法,我找到了5個錨函數;

2.最低的錨點是二進位文件中的函數#2622;

3.最高的錨點是二進位文件中的函數#2842;

4.錨點之間包含的範圍包括221(2842 - 2622 1 = 221)個函數;

5.我們還需要找到101(322 - 221 = 101)個函數;

6.為了安全起見,在我的方法中,我會在第一個錨點之前包含101個函數,在最後一個錨點之後也包含101個函數。

總的來說,函數的總數量: 423(101 221 101 = 423)個函數

我現在要做的只是為聚焦功能構建規範表示,從而大大提高了我從這一點開始的性能。

注意:這個映射可以為構建函數關係地圖提供進一步的幫助,因為來自文件a.c的foo()函數應該只與a.c中的函數對比,這樣就不需要將它與我們已經標識為駐留在不同文件中的函數進行比較。

選擇錨點

從本質上講,這些錨點函數在規範表示之前都是對比的,這就限制了我在搜索時可以使用的特性。此外,我希望錨點成為庫的唯一地標識,這樣,我正在處理的二進位文件中的其他庫就不會有任何誤報。

在不知道所有開放源代碼的情況下,為複雜的特性確定標準非常困難。儘管如此,我還是編寫了一些在實踐中似乎很有效的啟發式演算法。

IDA Pro中的OpenSSL函數SHA224_Init

我選擇這個函數是因為它有惟一的數值常量,且可配置確切的規則,你可以在其中找到src/config/anchor_config.py文件。

對比步驟過程

現在我們已經知道了Karta對比引擎背後的主要邏輯是什麼,下來就讓我完整介紹對比的詳細步驟。

標識符

每個受支持的庫都有一個標識符,我試圖在二進位文件中找到它。由於大多數開源代碼都包括唯一字元串,且它們通常都具有完整版本詳細信息,因此大多數標識符都是基於字元串的,並且是為他們試圖識別的庫配置的。一旦找到庫,標識符就嘗試提取版本信息,並對可執行或固件使用的確切版本進行指紋識別。

開源項目是不可能在編譯後的二進位文件中試圖隱藏的,這些庫不僅通常包含一個簡短的Google搜索,以識別字元串所在原始庫的唯一線索。不過,它們通常包含不必要的信息。以下是一個來自libpng庫的版權聲明的示例,它是一個使用二進位文件編譯的字元串。

編譯後的二進位文件中包含libpng的版權字元串

如你所見,在大多數用例中,識別可執行程序中是否存在開源庫相對比較容易。

錨點搜索

使用標識符中的信息,就可以載入特定庫版本的配置(基於.json *)。第一步是掃描二進位文件,查找與庫的錨函數對比的惟一數值常量和惟一字元串。如果沒有錨點,我無法擴展庫並繼續對比過程。

對比開始後,整個配置都會載入到內存中。由於我們不需要對配置發出查詢,這樣就不需要使用更流行的sqlite資料庫了,這種從sqlite到json的轉換,會導致配置文件的大小大幅減小。

根據繪製的文件映射關係圖,我可以精確定位包含對比錨函數的每個文件的位置,並估計其下限和上限(使用前面描述的相同邏輯)。

對比結果的示例圖

文件提示

由於許多開源項目往往包含源文件名稱的調試或跟蹤字元串,通常,這些字元串位於上述源文件的函數中,這意味著我們可以使用它們作為文件提示。繪製的文件映射關係圖後,我可以使用這些提示來確定其他文件的位置。

定位代理

由於代理是本地唯一的函數,它也可以稱為本地錨。在它的文件中,它是一個錨,但它包含的常量弱於全局錨所需的常量,每個定位的文件都會嘗試對比自己的代理。

優化鏈接器

在上面的講解中,我假設了一種前提,就是開源將被編譯為單個連續的大對象,並且內部文件不會彼此模糊處理。不幸的是,實際情況並非如此。

實際上,當我最初嘗試在Adobe PDF的二進位文件(2d.x3d)中對比libtiff時,得到的結果並不理想:只有176/500個函數對比。經過調查,該鏈接器似乎與具有相同二進位結構的函數混淆了。例如,用不同的名稱或不同的名稱範圍(來自不同文件的靜態函數)實現了兩次的函數。

來自libtiff的兩個相同的函數,其實它們位於不同的文件中

來自IDA Pro的分析,顯示使用了左邊的函數而不是右邊的函數

雖然這種優化減少了可執行文件的大小,但它由於會進行模糊處理,且改變了控制流程圖。且稍後進行幾次快速檢查,我發現這種優化也會破壞其他二進位對比工具的對比結果。

我決定像處理鏈接器那樣解決這個問題,在編譯開源庫的規範表示時,我對每個函數的鏈接器視圖進行哈希,並將其存儲為唯一的函數ID。起初我自己對位元組進行了哈希處理,但是當兩個函數引用相同的全局變數並且該變數駐留在每個文件中的不同偏移量時,會引入一些錯誤。參見下圖,我通過對大多數操作碼的位元組進行哈希,並在引用導出的全局變數時對指令的文本進行哈希,解決了這個問題。

文件tif_dirwrite.c中的函數TIFFClampDoubleToFloat()

文件tif_dir.c中的函數TIFFClampDoubleToFloat()

在對比過程中,對比器會查找任何可能合併的線索。當對比器在控制流圖中發現兩個函數是相同函數時,合併就會發生。此時,二進位函數就知道它所代表的源函數的數量,並且將保存它對比的合併源函數的列表。

使用此優化,我現在可以修復控制流圖中的異常,因為每個檢測到的合併都有效地將控制流圖擴展到鏈接器優化之前的原始狀態。在相同的二進位文件(2d.x3d)上再次測試時,結果明顯優化:對比了248/500個函數,對比度提高了41%。

可以看到,Karta可以讓鏈接器對函數_TIFFNoFixupTags的優化:

來自Karta的對比結果,成功地標識了鏈接合併函數

對比結果

現在是測試Karta如何處理原始的OfficeJet固件的時候了,我在計算機上的虛擬機(VM)中測試了這個插件,結果如下:

測試的OfficeJet固件上的對比結果

可以看到,不到30秒就可以對比一個包含300個函數(如libpng)的開源程序。此外,我還能夠對比所有引用的庫函數。

注意事項

1.當Karta處理函數的規範表示時,它與架構無關;

2.因為我的對比是從對比的開源庫的角度進行的,所以我還可以推導出關於「外部」函數的信息,這些函數不是庫的一部分,但是可以從庫中調用。例如,libpng使用zlib。

在libpng對比過程中對比的外部zlib函數

此外,在大多數情況下,我能夠對比標準庫中的函數,例如:memcpy,memcmp,malloc等。

與已知的BinDiff工具的比較

由於我無法比較所有現有的二進位對比工具,所以我選擇關注以下常用的工具:

BinDiff:BinDiff是二進位文件的比較二進位對比工具,可以幫助漏洞研究人員和工程師快速找到反彙編代碼中的差異和相似之處。使用BinDiff,你可以識別並隔離供應商提供的修補程序中的漏洞修復程序。你還可以在同一二進位文件的多個版本的反彙編之間借用符號和注釋,或使用BinDiff收集代碼被盜或專利侵權的證據。

以下,我會對它們是否開源 、是否支持大型二進位文件、是否有源代碼輔助、是否標識版本等方面進行比較。

對比結果表


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 嘶吼RoarTalk 的精彩文章:

CVE-2019-0841:Windows許可權提升漏洞PoC

TAG:嘶吼RoarTalk |