<span id="7ztzv"></span>
<sub id="7ztzv"></sub>

<span id="7ztzv"></span><form id="7ztzv"></form>

<span id="7ztzv"></span>

        <address id="7ztzv"></address>

            原文地址:http://drops.wooyun.org/papers/13514

            0x00 反編譯的局限、先決條件和評價指標


            反編譯技術具有一些局限性,各類反編譯工具或者系統,都面臨著一些共同的困難,多數困難是由于需要恢復出一些輸入的二進制文件中并非顯式提供的信息,畢竟編程語言在設計時考慮的不是逆向工程,而是正向編譯。這部分是有志反編譯技術人士面臨的主要技術困難。同時,也是惡意代碼編寫者常用于對抗反編譯的技術手段。

            1.反編譯技術面臨的宏觀問題

            1)問題

            同一機器上,同一語言的不同編譯版本存在目標代碼結構上的差異,即多編譯版本問題;同一機器上,不同語言編譯存在目標代碼結構上的差異,即多語言問題;不同機器上,同一語言編譯存在目標代碼上的差異,即多機種(CPU,也就是多體系結構)問題。

            2)相應的解決方案

            上述這些問題都影響到以程序分析為目的反編譯系統的實用性和通用性。對這些問題,某些可能的解決途徑是:

            最笨但一定可行的方法:針對每一種情況,分別開發各自的反編譯系統,這種窮舉方法勢必使反編譯投資龐大,且研制的反編譯器時效性差,顯然不適合實際需要。

            基于同一機型,設計良好的中間語言(針對于特定的體系結構,比如X86固定寄存器表示等,與機器相關!!),開發標準的反編譯系統,對各編譯版本的影響或者不同語言的差別(目標代碼結構上)采用預處理方法分別轉換為標準的中間語言形式,這種思路可行(不同前端-相同的中端,相同或者不同的后端),從中間語言轉換到高級語言的技術是成熟的,但要覆蓋各種情況,因而開發量也很大(僅比第一種方法看上去美些……)。

            對于不同的機型和同一種語言,可以設計抽象的中間代碼,也就是與體系結構不相關的,寄存器數量不限,采用目標代碼預處理方法分別轉換到抽象的中間代碼,接下來根據不同的后端,進行中間代碼的提升,這種方法效果類似2)中所述(是目前常用的方法!)。

            2.反編譯技術面臨的技術性問題

            反向編譯需要解決如下一些傳統的難點問題,在此我們僅僅列出了最難解決的若干問題,而在一款反編譯器開發的過程中,開發者往往會遇到更多具體而又瑣碎的問題。

            1)區分代碼和數據

            受馮?諾伊曼結構的制約,絕大多數計算機使用的數據和代碼是存儲在同一段內存空間中的,因此,區分數據和代碼的一般解決辦法已經被證明等價于停機問題。雖然,多種格式的可執行文件都定義了代碼段.text和數據段.data,但這樣并不能阻止編譯器或程序員把常量數據(如:字符串、switch跳轉表)放入代碼段中,也無法阻止將可執行代碼放入數據段中。因此,代碼和數據的區分仍然是一個亟需解決的重要問題。

            針對代碼和數據的區分問題,對靜態反編譯而言效果最好的一個方法是數據流制導的遞歸遍歷。此技術根據機器代碼從程序的入口點搜索所有可能的程序路徑,它依賴于程序的所有路徑都是有效的,且入口點是可發現的。最終,它同樣依賴于分析間接轉移指令以獲得其目標地址的能力,間接轉移指令包括間接跳轉和間接調用指令。

            2)處理間接跳轉和間接調用指令

            對于指令中的每個立即數操作數,都需要選擇將這個數值作為常量的值來表示還是作為指向內存中地址的指針來表示。對間接跳轉和間接調用的分析面臨著一個共同的問題——目標地址的確定。程序切片、表達式復制傳播和值域分析等是最有希望解決這一問題的技術,但是這些技術嚴重依賴于數據流分析,而數據流分析又依賴于完整的控制流圖。間接跳轉和間接調用問題未得到解決之前,是不可能擁有一個完整的控制流圖的。因此,初看起來它就如“雞跟蛋”問題一樣是無法解決的。

            當反匯編器或者反編譯器面對一個指針尺寸(如:8字節)的立即數時,它們需要判斷此立即數到底是常數(屬于整形、字符型或者其它數據類型)還是指向某類型數據的指針。

            3)自修改代碼

            自修改代碼指的是指令或者預先設定的數據在程序的執行中被修改。用于儲存指令的內存空間可能會在程序執行過程中被修改成為了另外的指令或者數據。在上個世紀六七十年代,計算機的內存空間很小,難以運行大的程序。計算機的最大內存為32Kb或64Kb,由于空間的制約,必須以最好的方式對空間進行利用,其中一種方法就是在可執行程序中節省字節,同一內存單元在程序執行中能保存指令,也能在另一時刻保存數據或其他指令。

            自修改代碼是程序運行時改變自身執行指令的程序代碼。自修改代碼的編寫是非常困難的,因為它要考慮可能對指令緩存造成的不良影響,它的主要用途是:反靜態分析、反盜版、病毒利用此方法逃避殺毒軟件的查殺等。

            4)編譯器和鏈接器包含的子過程

            二進制翻譯的另外一個問題就是編譯器引入的大量子過程以及鏈接器鏈接進來的很多過程造成翻譯難度和工作量大的問題。編譯器總是需要通過start-up子過程來設置環境,而且在需要的時候引入一些運行時的支持過程,這些過程通常是用匯編語言編寫的,無法翻譯到高級表示。同時,由于多數操作系統不提供共享庫機制,因此二進制程序是自包含的,庫函數綁定到二進制映像中,而且很多庫函數是用匯編語言編寫的。這就意味著二進制程序包含的不僅僅是程序員編寫的過程,而且還有很多是鏈接器鏈接進來的其他過程。二進制翻譯本身只對用戶編寫的過程感興趣,因此需要能夠區分用戶自定義過程和庫函數。

            5) 對難點問題的總結

            傳統的反編譯想要具備一定的實用性,下列問題是不能夠回避的:

            3.反編譯的先決條件

            反編譯的難度要遠遠大于編譯,因為編譯后得到的機器代碼己將源程序中所有顯式的高級語言信息完全丟失了。針對某種語言進行反編譯,不但需要較深的關于編譯和操作系統以及硬件等方面理論知識的支持,還需要通過大量的實踐和摸索獲得源——目標對之間的某些對應模式,在此基礎上進行研究和實踐。

            反編譯的實踐,要以如下的背景知識作為先決條件:

            1. 反編譯所要達到的高級語言的語法描述;反編譯過程是由它所翻譯到的目標高級語言的語法來制導的。
            2. 反編譯源文件所包含的目標代碼集,即編譯所對應的機器指令集;只有掌握作為反編譯器的輸入的機器指令集的規格說明,才能有效地恢復低級代碼程序的控制結構和數據流。
            3. 編譯所得到的可執行代碼的內存映象:有效地區分數據區和代碼區,能減小反編譯的工作量。

            4.反編譯器的評價指標

            反編譯器的性能評價方面并沒有確定的標準,通常人們采用如下幾點作為評價依據:

            1. 反編譯自動化程度:反編譯器運行過程中人工干預的次數是評定反編譯自動化程度的量度;
            2. 反編譯時間:即針對某個應用,反編譯器獲得結果所需要的運行時間;
            3. 反編譯器開發效率:重新編制程序和通過研制反編譯目標程序得到高級語言程序所需工作量的比值;
            4. 反編譯壓縮比:反編譯生成的高級語言程序與輸入的低級語言程序的長度比,比值越小,則壓縮比越大,反編譯器越優秀。

            0x01 常見反編譯框架


            比起如何使用一款反編譯工具,我們這里更關心如何設計和實現一款實用的反編譯器以及需要用到哪些技術。在這里,我們還是站在較為宏觀的角度列舉和認識一下幾種常見的反編譯器框架。在討論框架的同時,分析它們各自的優點,進行必要的對比。從整體上學習傳統反編譯器是如何設計的?能夠解決實際問題的新型反編譯又是如何設計的?以反編譯為核心技術實現的具有二進制翻譯功能、同時又具備反編譯能力的“翻譯器”是怎樣設計的?

            通過閱讀,讀者朋友們能夠從整體上了解實現不同用途的反編譯器的整體設計思路,以及一些具備現代反編譯特征的(即多源反編譯、多源二進制翻譯)新型反編譯器的框架設計理念。

            1.“I型”反編譯器的框架

            反編譯器在實際使用中,需要一些輔助程序來配合其創建目標高級語言的工作。無論這個反編譯器多么簡單,至少也要包括能夠實現文件裝載和可以處理有關庫函數在反編譯(或者具備二進制翻譯特性的反編譯工作)過程中的庫函數處理程序。接下來我們將介紹經典反編譯器在囊括必要輔助程序以后的程序框架和基本功能組成。

            1)上下文環境的銜接

            一般來說,源二進制程序都有一個重定位的地址表,當程序被裝入內存的時候,將在某些地址上進行重定位,通常反編譯器會通過裝載程序實現這個操作。接著,已經被重定位的(或絕對的)機器碼就會被反編譯器中的反匯編引擎進行機器碼到匯編碼的轉換,產生該程序的匯編表示。

            反匯編引擎在工作中并非將可執行程序中所有01代碼翻譯成匯編代碼,而是需要借助上文提到的“輔助程序”——即借助“編譯器簽名”和“庫簽名”兩類輔助程序去掉編譯器在編譯時加入的啟動代碼(start-up code)和庫例程代碼,然后再對剩余的由用戶編寫的代碼進行反匯編。

            接著,匯編語言程序作為反編譯器的輸入,輸出并產生一個高級語言的目標程序。該目標程序并不是最終反編譯的結果,還需要進行進一步的處理,例如:對while()循環做轉換以便后期處理器處理等。

            當然,“輔助程序”等自動工具不能保證在任何情況下都能進行正確的處理,反編譯過程有時需要人為的干預,即使用者也可能作為一個信息提供者,尤其是在確定庫例程以及區分數據和指令的時候。經驗豐富的反編譯程序員比使用自動工具更可靠。

            2)dcc反編譯器的框架

            dcc是用C語言編寫的一個適用于DOS操作系統的原型反編譯器(昆士蘭大學Cristina Cifuentes博士期間進行的反編譯研究成果)。dcc最初在一臺運行Ultrix的DecStation 3000上開發,后來被移植到運行DOS的PC機體系結構上。dcc把Intel i80286體系結構的.exe文件和.com文件作為輸入,并且產生目標C語言和匯編語言程序。這個反編譯器嚴格按照上一節介紹的反編譯上下文環境設計實現,它的框架由圖所示的幾個部分組成。

            4-1

            圖1 dcc反編譯器框架

            (1)裝載器

            裝載器是一段程序,它負責將待反編譯的目標程序載入內存,并完成目標程序機器碼的重定位(如果是可重定位的)。程序裝載是反編譯的準備階段的第一步。

            (2)簽名生成器

            簽名生成器是編譯器的重要“輔助程序”,它存在的目的是簡化反編譯的目標程序,簡化方法就是確定待反編譯程序的所使用的編譯器版本以及庫函數版本(dcc的編制者稱其為編譯器和庫的簽名,可以理解為編譯器和庫的特征信息)。簽名生成器可以自動、且唯一地標識每個編譯器和庫子程序的二進制標本。這些簽名的使用試圖反向進行鏈接器的工作——鏈接器把庫和編譯器啟動代碼鏈接到程序。通過上述處理,被分析的程序就剝離掉非用戶程序部分的所有編碼,只包含用戶當初用高級語言編寫的那部分程序。

            從Cristina所給出的示例可以看出:顯示“hello world”的C程序編譯以后,在二進制程序中有26個不同子程序,其中16個子程序是被編譯器增加來設置它的環境,9個例程是被鏈接器加入來實現printf(),1個子程序來自最初的C程序。簽名生成器的使用不僅減少了需要分析的子程序個數,也由于使用庫函數名稱代替任意的子程序名稱從而增加了目標程序的可讀性。

            (3)原型生成器

            原型生成器是一個自動確定庫子程序參數類型以及函數返回值類型的程序。這些原型來自于函數庫的頭文件,被反編譯器用來確定庫子程序的參數以及參數個數。原型生成器所做的工作是所有反編譯器必須著重處理的重要目標之一:函數恢復,包括庫函數的名稱、參數個數、參數類型、返回值類型等等。

            (4)反匯編器

            反匯編器是一個把機器語言轉換成匯編語言的程序。有些反編譯器把匯編語言程序轉換成一個更高級的表示法(一般是為統一多源目標反編譯的高級中間表示)。

            (5)庫綁定

            這一步是用來處理源可執行程序的編制語言與反編譯輸出的高級語言不一致的問題,例如:用Pascal編寫的程序所生成的可執行代碼被反編譯成C程序。假如產生的目標代碼中使用庫函數名稱 (也就是說能夠檢測到庫簽名),由于兩種語言使用不同的庫例程,所以即使這個程序是正確的也不能再用目標語言編譯它了,需要將原來用到的庫函數替換成反編譯目標語言的庫函數。dcc解決這個問題的辦法是使用庫綁定——在兩種語言的庫例程之間建立關聯。

            當然這種類似的方法在反編譯或者二進制翻譯中被普遍的使用,例如二進制翻譯會涉及到夸操作系統使用庫函數,這樣即便是同一種高級語言也可能因為版本不同而存在庫函數的差異,因此這種“庫綁定”的處理方式適用面很廣。

            (6)后期處理器

            dcc后期處理器也是一個程序,它把一個高級語言程序轉換成同種語言的一個語義等價的高級程序,例如while循環轉換成for循環,此處假設目標語言是C語言,以下while循環的代碼:

            #!c
            a = 1;
            while (a < 50)
            {
            /* 其它 C代碼 */
            a =a + 1;
            }
            

            可能被后期處理器轉換成等價的for循環代碼:

            #!c
            for (a = 1; a < 50; a++) { /*其它 C代碼 */}
            

            這是一個語義等價的程序,我們知道C語言中使用for作為循環結構具有更好的性能,因此此處后期處理器處理的結果是更適合于C語言的for循環,而不是反編譯器直接生成的反編譯結果的一般化結構while循環。

            2.經典多源反編譯框架簡介

            目前支持多源的反編譯框架主要有三種,分別是:基于語義描述和過程抽象描述的可變源、可變目標框架;以商用反匯編軟件IDA Pro為前端的、支持可擴展的反編譯框架;以及基于第三方代碼轉換庫的多源反編譯框架。下面分別以三種框架的典型系統為例,簡單介紹和分析一下各種框架的特點。

            1)UQBT

            在實驗型反編譯器dcc的研發基礎上,畢業后Cristina Cifuentes和Mike van Emmerik等人于1997年提出了UQBT可重定向的二進制翻譯系統,該二進制翻譯系統是以反編譯為主要技術手段實現的。2001年研發者又對其后端作了擴展,在1999年版本的基礎上增加了對JVML的支持等功能。

            (1)框架結構

            UQBT的1999版框架如圖所示。

            p4-2

            圖2 UQBT原始框架

            2001年擴展后的框架結構如所示。

            4-3

            圖3 UQBT的擴展框架

            UQBT框架可以大致被分為三部分:前端、分析和翻譯部分、后端。Ms表示給定的源機器,Md表示目標機器,前端負責對源機器Ms上的二進制文件解碼,并將其轉換為與機器無關的中間語言形式,即RTLs的形式;分析部分負責將源機器上的地址映射為目標機上的地址并完成相關的優化;后端負責將優化過的中間語言形式轉換為對應目標機上的可執行文件。由此可見UQBT框架中的前端和分析部分相當于反編譯的部分,后端則是代碼生成部分,即將反編譯后的代碼再編譯或轉換為目標機上的可執行代碼。

            UQBT是可變源、可變目標的,對于反編譯來說只是為了實現從低級代碼到高級代碼的轉換,不需要再轉變為目標機器上的可執行代碼,因此可以不考慮多目標的問題。UQBT的可變源和目標的特性通過描述語言、API和可插入模塊支持。其中幾個形式化的描述語言成為UQBT的亮點和精髓,分別是:編解碼描述語言SLED、語義描述語言SSL、過程描述語言PAL等。

            (2)中間表示

            UQBT應用兩種中間表示,低級RTL(Register TransferLists)直接與機器指令映射,高級HRTL(High LevelRegister Transfer Lists)形式與編譯器中間代碼類似,它應用了控制流的高一級抽象。

            RTL是一種基于寄存器的中間語言,它針對機器的匯編指令進行描述,代表了指令間的信息傳遞。該語言提供了無限個寄存器和內存單元可供使用,不會受限于某種特殊的機器結構。近年來,RTL已經被廣泛的應用到各種系統中作為中間表示,例如GNU編譯器、編譯連接優化器OM、編輯庫EEL等等。由于其操作簡單并且具有良好的平臺無關性,因此我們選用RTL作為IA64到Alpha二進制翻譯器的中間表示語言。

            在UQBT中,源機器體系結構每一條指令對應一個寄存器傳送列表或RTL語句。這種語言能夠通過對某一位置的一系列執行效果來捕獲機器指令的語義信息。

            HRTL是從過程調用、過程內控制流等與機器特性相關的細節中抽象出來的一種高級中間表示語言,由指令和操作符組成。它提供了所有基本控制流指令,例如無條件跳轉(JUMP)和條件跳轉(JCOND)、應用CALL和RETURN指令的過程調用、N—way分支指令等,為了給內存單元賦值,HRTL定義了ASGN指令。

            所有的內存單元和值都由語義串描述,分支指令將它們的跳轉目標作為語義串參數。復雜的語義串應用算術、邏輯或字節運算函數遞歸定義。在ITA翻譯器的實現過程中,我們應用該語言表示IA-64指令語義時,需要對它的函數進行擴展,目前HRTL定義了一百多個不同的操作符和函數。

            (3)前端模塊

            前端模塊的工作由一系列階段完成,每一個階段將源輸入文本流變換成高一級的表示形式。

            (4)后端模塊

            UQBT框架應用多種后端產生代碼,其中比較成功的方法是依賴C編譯器作為目標機的優化器和代碼產生器。在這種方法中,HRTL代碼被翻譯到低級C,應用C編譯器作為宏匯編器。后來的UQBT版本應用公共域或特性優化器后端,并集成在RTL級。

            (5)開發耗時——一個有趣的事實

            這里我們討論一個有趣的事實,但卻能夠充分說明進行一款實用的反編譯器開發需要龐大的工作量,以及使用描述語言對開發效率的提升。在表中我們列出了基于UQBT翻譯框架開發其他的二進制翻譯器所用的時間和人力,可以看出描述語言的應用使得在UQBT框架上容易實現其他機器的二進制翻譯器。寫描述文件與寫和機器相關的部分源代碼相比,無論是時間還是代碼數量都少了很多,而且開發者還可以重用本系統提供的機器無關的分析。開發人員需要完成源機器指令的SLED和SSL描述文件,以及應用PAL語言對源操作系統環境進行描述,再針對源機器的機器特性進行處理,結合目標機的特性完成相應的后端,這樣就可以有效地縮短二進制翻譯系統的開發周期。

            UQBT開發時間和人力情況:

            里程碑 人力耗費 人力耗費細節
            (SPARC,Solaris)和(x86,Solaris) 前端;C后端 5.7(人-年) 18(研究員-月),24(工程師-月)3.6(研究員-月),4.8(學生-月)6(學生-月),12(學生-月)
            (68328,PalmOs) 前端 6(人-月) 3(工程師-月),3(工程師-月)
            JVML后端(C ver.) 3(人-月) 3(學生-月)
            JVML后端(Java ver.) 5(人-月) 3(學生-月),2(工程師-月)
            目標代碼后端 3(人-月) 3(學生-月)
            RTL后端 7(人-月) 3(研究員-月)(SPARC)4(工程師-月)(ARM)
            (PA-RISC,HP-UX)前端 10(人-月) 6(學生-月,)3(工程師-月)1(研究員-月)

            盡管UQBT提供的框架和二次開發方法大大提高了反編譯器的編寫效率,縮短了開發周期和時間,但是以筆者常年參與以反編譯為基礎的逆向分析方法的研究經歷和研發經驗來看——一款可用的反編譯器開發仍然會耗費一個10人團隊數年的開發時間。

            2)Hex-Rays

            Hex-Rays是一款商用反編譯工具,前端是IDA Pro。Hex-Rays實際上是IDA Pro中的反編譯插件,因為IDA Pro支持擴展,且已經支持對大量不同指令集架構下可執行程序的反匯編,因此,Hex-Rays理論上可以實現對所有IDA Pro支持的指令集架構下的所有可執行程序進行反編譯,但目前為止,Hex-Rays僅實現了對x86和ARM平臺下可執行程序的反編譯,擴展緩慢。

            與同類工具相比,Hex-Rays能很好的識別復合條件表達式及循環結構,對庫函數名及參數的識別率較高。另外Hex-Rays提供有Decompiler SDK,允許開發者以IDA Pro為前端,實現自己的分析方法。目前很多逆向分析人員都是利用IDA提供的接口來編寫插件,從而完成漏洞挖掘、軟件確認、覆蓋分析等。

            3)BAP

            BAP是由David Brumley在BitBlaze靜態分析組建Vine的基礎上改進得到的,與BitBlaze相比,BAP對中間語言做了一些擴展,清除了Vine中存在的幾個漏洞。其框架如圖所示。

            4-4

            圖4 BAP框架

            由圖中可以看到,BAP分為前端、中間語言、后端三部分。其中前端主要完成二進制文件格式的解析及語義的提升,后端主要完成相關的優化、程序驗證、其他的程序分析工作、生成相關的圖、代碼生成等工作,中間語言代碼則是基于libVEX第三方庫生成的VEX IR中間語言代碼轉換得到的。BAP基于第三方的工具集,主要包括:反匯編器、代碼轉換庫、GNU中的二進制文件解析工具libbfd,決策過程等。其中反匯編器BAP主要支持IDA Pro和GNU objdump,代碼轉換庫是libVEX。值得一提的是,BAP支持動態分析,TEMU就是基于QEMU專門針對x86平臺的動態分析引擎,TEMU為用戶提供了各種語義提取接口以及動態污點分析接口,為用戶進行動靜態結合的漏洞挖掘及惡意代碼分析等工作提供了很好的平臺。

            由于BAP前端主要依賴于第三方庫,只要第三方庫支持的平臺,BAP就會支持,第三方庫不支持的平臺BAP也無法進行分析,要實現對新平臺的支持,只能依賴第三方庫的擴展,因此存在被動擴展的問題。

            <span id="7ztzv"></span>
            <sub id="7ztzv"></sub>

            <span id="7ztzv"></span><form id="7ztzv"></form>

            <span id="7ztzv"></span>

                  <address id="7ztzv"></address>

                      亚洲欧美在线