PE檔案格式詳解(一)――基礎知識

來源:互聯網
上載者:User

什麼是PE檔案格式:
    我們知道所有檔案都是一些連續(當然實際儲存在磁碟上的時候不一定是連續的)的資料群組織起來的,不同類型的檔案肯定組織形式也各不相同;PE檔案格式便是一種檔案組織形式,它是32位Window系統中的可執行檔EXE以及動態串連庫檔案DLL的組織形式。為什麼我們雙擊一個EXE檔案之後它就會被Window運行,而我們雙擊一個DOC檔案就會被Word開啟並顯示其中的內容;這說明檔案中肯定除了存在那些檔案的主體內容(比如EXE檔案中的代碼,資料等,DOC檔案中的檔案內容等)之外還存在其他一些重要的資訊。這些資訊是給檔案的使用者看的,比如說EXE檔案的使用者就是Window,而DOC檔案的使用者就是Word。Window可以根據這些資訊知道把檔案載入到地址空間的那個位置,知道從哪個地址開始執行;載入到記憶體後如何修正一些指令中的地址等等。那麼PE檔案中的這些重要訊息都是由誰加入的呢?是由編譯器和連接器完成的,針對不同的編譯器和連接器通常會提供不同的選項讓我們在編譯和連接產生PE檔案的時候對其中的那些Window需要的資訊進行設定;當然也可以按照預設的方式編譯串連產生Window中預設的資訊。例如:WindowNT預設的程式載入基址是0x40000;你可以在用VC串連產生EXE檔案的時候使用選項更改這個地址值。在不同的作業系統中可執行檔的格式是不同的,比如在Linux上就有一種流行的ELF格式;當然它是由在Linux上的編譯器和連接器產生的,所以編譯器、連接器是針對不同的CPU架構和不同的作業系統而涉及出來的。在嵌入式領域中我們經常提到交叉編譯器一詞,它的作用就是在一種平台下編譯出能在另一個平台下啟動並執行程式;例如,我們可以使用交叉編譯器在跑Linux的X86機器上編譯出能在Arm上啟動並執行程式。

程式是如何運行起來的:
    一個程式從編寫出來到運行一共需要那些工具,他們都對程式作了些什麼呢?裡面都涉及哪些知識需要學習呢?先說工具:編輯器-》編譯器-》連接器-》載入器;首先我們使用編輯器編輯源檔案;然後使用編譯器編譯程目標檔案OBJ,這裡面涉及到編譯原理的知識;連接器把OBJ檔案和其他一些庫檔案和資源檔串連起來產生EXE檔案,這裡面涉及到不同的連接器的知識,連接器根據OS的需要產生EXE檔案儲存著磁碟上;當我們運行EXE檔案的時候有Window的載入器負責把EXE檔案載入到線性地址空間,載入的時候便是根據上一節中說到的PE檔案格式中的哪些重要訊息。然後產生一個進程,如果進程中涉及到多個線程還要產生一個主線程;此後進程便開始運行;這裡面涉及的東西很多,包括:PE檔案格式的內容;記憶體管理(CPU記憶體管理的硬體環境以及在此基礎上的OS記憶體管理方式);模組,進程,線程的知識;只有把這些都弄清楚之後才能比較清楚的瞭解這整個過程。下面就讓我們先來學習PE檔案格式吧。

PE檔案的總體結構:
    便是PE檔案的一個總體結構:注意,圖2是在圖1的基礎上進一步細化了,不過圖2的順序是從下向上代表檔案的從頭到尾的順序。

DOS MZ Header

DOS stub

PE header

Section table

Section 1

Section 2

Section ...

Section n

圖一圖2

我們的EXE檔案在磁碟上就是按照上面的格式順序儲存的,當啟動並執行時候它就很容易被載入器載入到線性地址空間;但是線上性空間中和在磁碟上不同,線上性空間中各個部分不一定是佔據連續的線性地址空間。下面對PE檔案格式的介紹就按照中對從頭到尾對每個部分進行介紹。好的,今天剛去醫院回來有些累了,就先寫到這兒吧。

嗯,不行,還有幾個重要而又基礎的概念需要在這兒先澄清一下,否則後面就會出亂子了。

幾個重要的基本概念:

1)節:PE檔案的真正內容劃分成塊,稱之為sections(節)。每節是一塊擁有共同屬性的資料,比如代碼/資料、讀/寫等。我們可以把PE檔案想象成一邏輯磁碟,PE header 是磁碟的boot扇區,而sections就是各種檔案,每種檔案自然就有不同屬性如唯讀、系統、隱藏、文檔等等。 值得我們注意的是 ---- 節的劃分是基於各組資料的共同屬性: 而不是邏輯概念。重要的不是資料/代碼是如何使用的,如果PE檔案中的資料/代碼擁有相同屬性,它們就能被歸入同一節中。不必關心節中類似於"data", "code"或其他的邏輯概念: 如果資料和代碼擁有相同屬性,它們就可以被歸入同一個節中。(節名稱僅僅是個區別不同節的符號而已,類似"data", "code"的命名只為了便於識別,惟有節的屬性設定決定了節的特性和功能)如果某塊資料想付為唯讀屬性,就可以將該塊資料放入置為唯讀節中,當PE裝載器映射節內容時,它會檢查相關節屬性共置對應記憶體塊為指定屬性。下面是常見的節名及作用:

節名

作用

.arch

最初的構建資訊(Alpha Architecture Information)

.bss

未經初始化的資料

.CRT

C運行期唯讀資料

.data

已經初始化的資料

.debug

調試資訊

.didata

延遲輸入檔案名稱表

.edata

匯出檔案名稱表

.idata

匯入檔案名稱表

.pdata

異常資訊(Exception Information)

.rdata

唯讀初始化資料

.reloc

重定位表資訊

.rsrc

資源

.text

.exe或.dll檔案的可執行代碼

.tls

線程的本機存放區器

.xdata

異常處理表

注意:上面已經說過了“節的劃分是基於各組資料的共同屬性: 而不是邏輯概念。重要的不是資料/代碼是如何使用的,如果PE檔案中的資料/代碼擁有相同屬性,它們就能被歸入同一節中” 所以上面表中列出的節並不一定單獨成節,也就是說即使存在上面表中的某一節,在節表(section table)(後面會講到)中也不一定就有於之對應的項,因為它可能和別的具有共同屬性的節共同組成了一節。比如 .idata 可以和 .text 合成一節而命名為 .text,而在節表中只有和 .text 對應的項。這也就是後面的optional header中資料目錄(DataDirectory)存在的作用,因為很多有用的節被合并了,因此載入器無法通過節表來定位它們,所以這就是資料目錄(DataDirectory)發揮作用的時候了(具體作用後面會講到)。

2)虛擬位址:虛擬位址即程式中使用的地址,也就是從程式員的角度看到的地址,有時也叫邏輯地址;通常使用段地址:位移量的形式表示,不過在32位系統中使用的是平坦(Flat)記憶體模式,所以我們可以不用管段地址,只考慮32位的位移量即可,認為32位的位移量就是虛擬位址,這樣一來程式員就可以認為他是在一個段中寫程式,這個段的大小是232 = 4G的容量,當然這部分地址空間是程式和OS共用的,程式員可以利用的大約有2G(具體可以參考Win98和WinNT的記憶體布局);所以我們平時在寫程式申請記憶體的時候實際上申請的就是這2G的線性地基空間,由於所有的4G線性地址空間都被OS作為資源來管理(這4G的線性地址空間是通過頁表來表現出來的,OS分配線性地址空間給進程也就是分配相應的頁表給進程),所以我們無論用什麼方式使用記憶體最終都是轉換為OS為我們分配線性地址空間,至於分配的線性地址空間又如何被映射為真正的實體記憶體完全是有OS負責的(更詳細資料參見“Windows 記憶體管理”),程式員不必操心。

3)相對虛擬位址:「相對虛擬位址(Relative VirtualAddress,RVA)」即相對於上面的基地址的位移量。可攜式執行檔中的許多欄位內容都是以RVA 表示,一個RVA 是某一資料項的offset(位移)值-- 從檔案被映像進來的起點(即基地址)算起。舉個例子,我們說Windows載入器把一個可攜式執行檔映像到虛擬位址空間的0x400000 處,如果此image 有一個表格開始於0x401464,那麼這個表格的RVA 就是0x1464:虛擬位址0x401464 - 基地址0x400000 = RVA 0x1464隻要把RVA 加上基地址,RVA 就可以被轉換為一個有用的指標。在PE檔案中大多數地址多是RVA 而 RVA只有當PE檔案被PE裝載器裝入記憶體後才有意義。 如果我們直接將檔案對應到記憶體而不是通過PE裝載器載入,那麼我們就不能直接使用那些RVA。必須先將那些RVA轉換成檔案位移量,RVAToOffset函數就起到這個作用。

4)基地址:「基地址(base address)」是一個重要概念,用來描述被映像到記憶體中的EXE 或DLL 的起始地址。為了方便,Windows NT 和Windows 95 都以模組的基地址做為模組的instance handle(HINSTANCE,執行個體控制代碼)。Windows95載入器把一個可攜式執行檔映像到虛擬位址空間的0x400000 處;而WindowNT載入器把一個可攜式執行檔映像到虛擬位址空間的0x10000 處 。

5)檔案位移量:檔案中的地址與記憶體中表示不同,它是用位移量(File offset)來表示的,檔案中的第一個位元組的位移量是0,後面的位元組依次遞增。在SoftICE和W32Dasm下顯示的地址值是記憶體線性地址,或稱之為虛擬位址(Virual Address,VA)。而十六進位工具裡,如:Hiew、Hex Workshop等顯示的地址就是檔案地址,稱之為位移量(File offset) 或物理地址(RAW offset,注意這個物理地址不是記憶體定址中說到的物理地址 )。

6)模組:「模組(module)」一詞表示一個EXE 或DLL 被載入記憶體後的程式碼、資料和資源(就是被載入到記憶體後的EXE或DLL整體,包括代碼、資料和資源,而不是說代碼、資料、資源分別都是模組)。除了程式碼和資料是你的程式直接使用的之外,模組還內含一些支援性資料,Windows 用它來決定程式碼和資料放在記憶體的什麼地方,在Win32,這些資訊保留在PE頭部(即圖1中的PE header,實際上它是一個IMAGE_NT_HEADERS 結構)中。

7)邏輯地址:見“虛擬位址”

8)線性地址:線性地址是由虛擬位址(邏輯地址)轉換來的,轉換需要CPU和OS共同合作來完成;裡面涉及到通用描述元表GDT和局部描述符表LDT;不過由於32位的Window系統採用flat記憶體模式,所以我們可以認為虛擬位址就是線性地址,即我們可以認為邏輯地址中的32位位移量就是線性地址。

9)物理地址:即最終發往地址匯流排上的地址,它對應著實際的實體記憶體,在32位的Window儲存管理中它是通過頁表由線性地址轉換出來的。

10)實際地址:即“物理地址”。

其中前面的6個概念是學習PE檔案格式需要知道的,後面的幾個主要在記憶體管理裡面提到,在這裡為了便於區別一起列了出來。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.