初步瞭解PE檔案格式(上)

cuglzf發表於2017-07-03

      今天這篇部落格只對PE檔案進行大致的分析,使用一個例子並結合一個簡單的PE檔案格式圖來說明PE檔案格式,因為初學,裡面有不正確的地方還望大家指正。

1.PE檔案結構

      首先說一下什麼是PE格式吧,雖然從網上搜一下會有很多定義,就我的理解來說,PE是Microsoft為了讓程式在Windows上可移植而做的一種檔案格式規定。就拿我們每個人都不陌生的exe程式來說吧,它就是一個PE檔案,在我們的印象中,只要是Windows作業系統,都能執行exe程式,這就是Microsoft做的讓程式在Windows平臺上實現移植的功能,這個移植能力的實現是因為規定了exe程式的格式,Windows在執行exe程式的時候,PE檔案載入器會按照約定載入exe程式,所以程式就正常地執行起來了。我這麼來說是不是對PE檔案不那麼抽象了,比如像EXE,DLL,SYS這種格式的檔案就是PE格式檔案,這些檔案都是我們平時見到過或者使用過的。如果大家瞭解圖片格式,比如BMP圖片,那這個PE檔案格式就更好理解了,都是按照一定的規範生成的,在BMP圖片中檔案頭部資訊記錄了這個檔案的大小、建立日期、寬度和高度等等資訊,PE檔案也是這樣,只不過比BMP檔案格式更復雜。
      說了這麼多廢話下面進入正題,紙上得來終覺淺,覺知此事要躬行,如果不親手用一個工具去看PE檔案,那麼理解PE檔案格式還是有點困難的,因此在看PE檔案的時候最好使用WinHex這個工具,另外還需要一個輔助工具就是LoadPE,LoadPE這個工具是來驗證我們分析的PE檔案是否是正確的。下面我們就來分析一下Windows 7中的記事本程式notepad,按照網上給的PE格式圖我們一邊分析一邊通過LoadPE來驗證。
      首先來看一下PE檔案的基本結構

PE檔案結構

這個圖是我根據我的理解畫出來的,PE檔案基本由以下四個部分組成,DOS頭部、PE檔案頭、節表和節,如果有了一個整體的PE檔案結構,我覺得對於理解PE檔案格式就輕鬆一些,同時記憶也深刻些,下面就一一講解每個部分。
      首先我們用WinHex開啟notepad程式如下圖,開啟後看到16進位制格式編碼,後面會按照上面的PE結構圖分部分來講解。
notepad程式

2.DOS檔案頭

DOS頭部

      上圖就是notepad程式DOS檔案頭的16進位制編碼部分,DOS檔案頭包含兩部分,一部分是DOS檔案頭的標誌也就是以ASCII碼為“MZ”字母開頭的部分,簡稱MZ檔案頭,另外一部分就是DOS程式,這部分是當程式在DOS環境下執行時呼叫的,具體就不詳細講解了,因為我也不懂。在這個部分我們比較關注的有兩個地方,一個就是前面講的MZ標誌,另外一個就是從檔案頭偏移3CH(16進位制)的地方有4個位元組,這4個位元組是一個地址,指向PE檔案頭。
      上圖中最開始的兩個位元組4D5A就是我們說的MZ頭,看到對應右邊的ASCII碼是字母MZ,從頭開始偏移3CH的位置的4個位元組圖中顯示的是E0000000,這裡是一個地址,對了這裡要說明一下,從WinHex看到的資料是E0000000,實際是要倒過來看000000E0,這裡涉及到資料儲存的大小端問題,這裡就不展開說明了,記住後面的資料都是從後往前讀就可以了。我們找到000000E0位置,看到50450000這4個位元組,對應ASCII碼是字母PE,這裡就進入了PE頭部了。

3.PE檔案頭

這裡寫圖片描述

      上圖就是PE部分(除了最後8個標紅的位元組),緊接著DOS部分,從50450000這個4個位元組開始,PE檔案頭佔用F8H(248)個位元組,總共分為三個部分,PE標誌、PE檔案頭和PE可選頭部,其中PE標誌佔4個位元組,PE檔案頭佔20個位元組,PE可選頭部佔224個位元組。
      首先我們看到PE標誌,DOS部分我們也講到了這個標誌,佔用4個位元組。接著是PE檔案頭了,PE檔案頭包含塊的數目、檔案建立日期和檔案資訊標誌等資訊,這裡就不細講了。再接著就是PE可選頭部,這裡麵包含資訊比較多,我只挑一些重要的講一下,首先是兩個位元組的標誌位,一般常值為10BH,然後就是4個位元組找到圖中的89360000(第三行標紅的塊),這是程式入口RVA地址,RVA(Relative Virtual Address)就是相對虛擬地址,這個地址與我們前面說的3CH偏移值不一樣,前面的那個3CH偏移值指的是從PE檔案開始的偏移值,而這裡的地址是程式載入到記憶體以後相對於載入的基址的偏移值(這裡很繞,如果沒有搞明白也沒關係,後面我會著重將明白什麼是RVA)。這裡又涉及到檔案對齊和記憶體對齊的問題,說簡單點就是我們程式碼儲存在硬碟裡,硬碟裡是512位元組一個扇區,那麼每次硬碟讀寫資料都是512個位元組的讀,如果讀寫的資料是300個位元組,那麼仍然佔用512個位元組的空間,這就是檔案對齊。記憶體對齊同理,假設CPU訪問記憶體每次讀寫的都是4096個位元組,那這樣的話從硬碟上讀取程式碼載入到記憶體裡就要進行轉換,我們用WinHex開啟的notepad程式顯示的僅僅是在硬碟裡存的程式碼,如果載入到記憶體,記憶體空間就不是我們看到的這樣了。
      我們繼續講下一個比較關注的地方,就是記憶體塊對齊值,上圖中的00100000,表示記憶體塊對齊值,而00020000表示檔案對齊值(我都用紅色標出來了)。這個就是前面我提到記憶體對齊和檔案對齊的單位,將記憶體塊對齊值換算成十進位制就是4096,檔案對齊值換算成十進位制就是512。再下面一個重要的是映像大小,上圖中的00000300就是,映像大小就是PE檔案載入到記憶體後佔用記憶體的大小,我們換算一下,是192KB,我們再看一下notepad.exe的檔案大小是176KB,所以一般檔案載入到記憶體後都會佔用更多的空間。上面說到的一些資訊我們用LoadPE開啟看一下,如下圖,
LoadPE

在圖中,我把重要的幾個標出來了,與我們前面使用WinHex分析的是一致的。
      剩下兩個重要的,就是輸出表RVA值和輸入表RVA值,在圖中就是緊挨著的兩個各自佔用8位元組的紅色塊。每個都是前4個位元組表示RVA值,後4個位元組表示大小值。所謂的輸入表和輸出表,簡單點來說就是程式是否有匯出函式、類等,是否需要匯入函式,就像我們使用DLL需要匯入函式,那這個DLL資訊就可以在匯入表中找到。這裡匯出表全為0,因為我們的notepad程式不需要匯出函式,所以為空。那既然有了匯入表RVA值,我們想知道notepad程式到底呼叫了哪些DLL,怎麼來在WinHex中找到匯入表資訊呢,在後面的部落格我們再講,這篇寫的有點長了,中間斷斷續續,寫部落格真的很考驗一個人,文章中有問題的地方請大家指正!

相關文章