反編譯與反彙編

反編譯做的就是編譯的相反動作。舉例來說,例如一個程序用C語言來寫,寫完之後要編譯成CPU可以識別和運行的機器代碼,也就是生成EXE程序。而反編譯是反過來,把EXE反編譯成編程源代碼,也就是C程序,不過一般反編譯大多數都是從機器碼反編譯爲彙編。其他語言的情況基本也是這樣的。至於具體要怎麼用,這個就看你的目的了。但一般情況下是通過反編譯主要是用於沒有源碼的程序的分析,例如破解別人的軟件等。

一,比如說,你拿到人家的一個程序,是一個EXE文件,經過用工具分析,知道人家是用delphi寫的,於是你用一個反編譯工具把這個程序從EXE反編譯爲delphi源代碼,這樣你就可以對源代碼進行修改和完善,然後再重新編譯成EXE。
二,再比如你拿到一個程序,這程序需要註冊,不註冊不能用或者只能試用幾次,註冊後就能一直使用,因此你想對它進行破解。但EXE是CPU能識別的代碼,在我們看起來是所謂的“亂碼”,於是要找反編譯工具,把它反編譯成我們能看得懂的彙編語言,這樣就能看到程序是怎麼判斷是否註冊的,然後再根據找到的判斷位置對其進行修改,就能做出破解版了。

對於上面這兩種反編譯的情況,不懂編程的人,第一種情況反編譯出來的源代碼,肯定看不懂,更別說對其進行完善了,根本就沒意義,至於第二種情況,不但要有很深的編程水平還要非常有經驗的人才能做到,反編譯出來的彙編源碼動輒上萬行甚至幾十萬行,要從這麼多行裏面找出判斷點,不是說學過一點編程的人就能做到的,更別說沒學過編程的了。

 要更深入瞭解C++, 必須要知道一個程序從開始到結束都幹了些什麼, 怎麼幹的。 所以我從C++編譯到運行過程,解析下程序是怎麼跑的。

        首先,初略的說一下之前C++的編譯過程,C++編譯過程包括預編譯-》彙編-》編譯-》鏈接。稱爲一個可執行文件。(Windows平臺下爲.exe文件)。

預編譯主要展開包含的頭文件,宏定義等操作。例如一個簡單的main程序,編譯預編譯後,的文件對比。

  可以看到裏面的宏已經被去掉了。如果定了那個宏,那麼宏裏面的內容也會顯示出來。頭文件也是,如果你包含了你一個.h 文件,那麼整個.h文件會包含進來。

     彙編過程,就是把已經預編譯的文件編譯成彙編代碼的過程,整個過程會包含語法,詞法的分析,和一些優化操作。

     編譯過程其實是跟彙編可以合成一個階段,變成目標代碼。也就是二進制文件。

     鏈接過程是將單個編譯後的文件鏈接成一個可執行程序。前面的預編譯、彙編、編譯都是正對單個文件,以一個文件爲一個編譯單元,而鏈接則是將所有關聯到的編譯後單元文件和應用的到庫文件,進行一次鏈接處理,之前編譯過的文件 如果有用到其他文件裏面定義到的函數,全局變量,在這個過程中都會進行解析。

首先看看編譯後的文件樣子(已VS2012編譯後的OBJ文件爲例子,不同編譯器 樣式可能會不同。)

編譯前的文件

#include "Car.h" int main(int argc, char* argv[]) {  Car* p = new Car();

 delete p;  return 1; }

編譯後的樣子(由於編譯後的文件 信息太多 只貼出裏面未解析符號部分。)

 UNDEF:00002DC4 ; int __thiscall Car::Car(Car *__hidden this) UNDEF:00002DC4                 extrn ??0Car@@QAE@XZ:near ; CODE XREF: _main+63p UNDEF:00002DC8 ; int __thiscall Car::~Car(Car *__hidden this) UNDEF:00002DC8                 extrn ??1Car@@QAE@XZ:near UNDEF:00002DC8                                         ; CODE XREF: Car::`scalar deleting destructor'(uint)+26p UNDEF:00002DCC ; __fastcall _RTC_CheckStackVars(x, x) UNDEF:00002DCC                 extrn @_RTC_CheckStackVars@8:near UNDEF:00002DCC                                         ; CODE XREF: std::_String_alloc<0,std::_String_base_types<char,std::allocator<char>>>::_Alloc_proxy(void)+68p UNDEF:00002DCC                                         ; $LN19+72p ... UNDEF:00002DD0 ; __fastcall __security_check_cookie(x) UNDEF:00002DD0                 extrn @__security_check_cookie@4:near UNDEF:00002DD0                                         ; CODE XREF: __ehhandler$??$construct@PADAAPAD@?$allocator@D@std@@QAEXPAPADAAPAD@Z+Fp UNDEF:00002DD0                                         ; __ehhandler$??$construct@U_Container_proxy@std@@U12@@?$allocator@U_Container_proxy@std@@@std@@QAEXPAU_Container_proxy@1@$$QAU21@@Z+Fp ... UNDEF:00002DD4 ; __stdcall _CxxThrowException(x, x)

編譯後的文件用(用反彙編成彙編代碼查看) 其中實現函數會變成一堆彙編指令。而那些引用到的在其他文件裏面實現的函數將會變成一個特點的符號(如上面中的調用Car類的構造函數 extrn ??0Car@@QAE@XZ:near)這些符號稱做爲解析的符號,表示在鏈接的時候需要被解析。符號的生成名稱具體跟編譯器有關,但是會保證一個類的某個函數名稱在同一個編譯裏面必須是唯一的,因爲我們在預編譯階段已經把Car.h包含進來所以編譯器能正確生成這個函數的名字,然後在鏈接的時候 會找到改名字的函數,把此標識名字替換爲函數的地址。這樣就實現的鏈接。

在符號解析(symbol resolution)階段,鏈接器按照所有目標文件和庫文件出現在命令行中的順序從左至右依次掃描它們,在此期間它要維護若干個集合:(1)集合E是將被合併到一起組成可執行文件的所有目標文件集合;(2)集合U是未解析符號(unresolved symbols,比如已經被引用但是還未被定義的符號)的集合;(3)集合D是所有之前已被加入到E的目標文件定義的符號集合。一開始,E、U、D都是空的。

(1): 對命令行中的每一個輸入文件f,鏈接器確定它是目標文件還是庫文件,如果它是目標文件,就把f加入到E,並把f中未解析的符號和已定義的符號分別加入到U、D集合中,然後處理下一個輸入文件。 (2): 如果f是一個庫文件,鏈接器會嘗試把U中的所有未解析符號與f中各目標模塊定義的符號進行匹配。如果某個目標模塊m定義了一個U中的未解析符號,那麼就把 m加入到E中,並把m中未解析的符號和已定義的符號分別加入到U、D集合中。不斷地對f中的所有目標模塊重複這個過程直至到達一個不動點(fixed point),此時U和D不再變化。而那些未加入到E中的f裏的目標模塊就被簡單地丟棄,鏈接器繼續處理下一輸入文件。 (3): 如果處理過程中往D加入一個已存在的符號,或者當掃描完所有輸入文件時U非空,鏈接器報錯並停止動作。否則,它把E中的所有目標文件合併在一起生成可執行文件。

原文地址http://blog.csdn.net/yinzhuo1/article/details/47069201



C/C++程序編譯流程(預處理->編譯->彙編->鏈接)

程序的基本流程如圖:

1. 預處理

預處理相當於根據預處理指令組裝新的C/C++程序。經過預處理,會產生一個沒有宏定義,沒有條件編譯指令,沒有特殊符號的輸出文件,這個文件的含義同原本的文件無異,只是內容上有所不同。

  • 讀取C/C++源程序,對其中的僞指令(以#開頭的指令)進行處理

    ①將所有的“#define”刪除,並且展開所有的宏定義

    ②處理所有的條件編譯指令,如:“#if”、“#ifdef”、“#elif”、“#else”、“endif”等。這些僞指令的引入使得程序員可以通過定義不同的宏來決定編譯程序對哪些代碼進行處理。預編譯程序將根據有關的文件,將那些不必要的代碼過濾掉。 

    ③處理“#include”預編譯指令,將被包含的文件插入到該預編譯指令的位置。

(注意:這個過程可能是遞歸進行的,也就是說被包含的文件可能還包含其他文件)

  • 刪除所有的註釋

  • 添加行號和文件名標識。

    以便於編譯時編譯器產生調試用的行號信息及用於編譯時產生的編譯錯誤或警告時能夠顯示行號

  • 保留所有的#pragma編譯器指令

2. 編譯

將預處理完的文件進行一系列詞法分析、語法分析、語義分析及優化後,產生相應的彙編代碼文件。

3. 彙編

將編譯完的彙編代碼文件翻譯成機器指令,並生成可重定位目標程序的.o文件,該文件爲二進制文件,字節編碼是機器指令。

彙編器是將彙編代碼轉變成機器可以執行的指令,每一個彙編語句幾乎都對應一條機器指令。所以彙編器的彙編過程相對於編譯器來講比較簡單,它沒有複雜的語法,也沒有語義,也不需要做指令優化,只是根據彙編指令和機器指令的對照表一一翻譯即可。

4. 鏈接

通過鏈接器將一個個目標文件(或許還會有庫文件)鏈接在一起生成一個完整的可執行程序。

    由彙編程序生成的目標文件並不能立即就被執行,其中可能還有許多沒有解決的問題。

例如,某個源文件中的函數可能引用了另一個源文件中定義的某個符號(如變量或者函數調用等);在程序中可能調用了某個庫文件中的函數,等等。所有的這些問題,都需要經鏈接程序的處理方能得以解決。

    鏈接程序的主要工作就是將有關的目標文件彼此相連接,也就是將在一個文件中引用的符號同該符號在另外一個文件中的定義連接起來,使得所有的這些目標文件成爲一個能夠被操作系統裝入執行的統一整體。

原文地址:http://www.cnblogs.com/Lynn-Zhang/p/5377024.html

C++ 之編譯過程

 

1)用戶點擊編譯程序時,編譯程序將C++源代碼轉換成目標代碼,目標代碼通常由 機器指令和記錄如何將程序加載到內存的信息組成。其後綴通常爲.obj或.o;

2)目標文件中存儲的只是用戶所編寫的代碼的轉換結果,並不包括底層的操作指令,不能直接運行。例如程序包 iostream 實現了所有有關輸入和輸出的操作,並且其所有實現操作的機器代碼都放在一個庫中,庫是對已實現的程序經編譯後所產生的代碼集合,用戶可以在程序中直接使用庫。

3)一個被稱爲鏈接程序的特殊程序將用戶程序的目標文件和iostream庫中必要代碼鏈接起來生成一個可執行文件,其後綴通常爲.exe 。這個可執行文件中包含了執行該用戶程序所需要的所有機器代碼,其過程大體如下所示:

 

原文地址:http://www.cnblogs.com/debuging/archive/2011/10/16/2214195.html


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