一、PE文件結構
PE即Portable Executable,是win32環境自身所帶的執行體文件格式,其部分特性繼承自Unix的COFF(Common Object File Format)文件格式。PE表示該文件格式是跨win32平台的,即使Windows運行在非Intel的CPU上,任何Win32平台的PE裝載器也能識別和使用該文件格式的文件。
所有Win32執行體(除了VxD和16位的DLL)都使用PE文件格式,如EXE文件、DLL文件等,包括NT的內核模式驅動程序(Kernel Mode Driver)。
PE文件至少包含兩個段,即數據段和代碼段。Windows NT 的應用程序有9個預定義的段,分別為 .text 、.bss 、.rdata 、.data 、.pdata 和.debug 段,這些段並不是都是必須的,當然,也可以根據需要定義更多的段(比如一些加殼程序)。
在應用程序中最常出現的段有以下6種:
.執行代碼段,通常 .text (Microsoft)或 CODE(Borland)命名;
.數據段,通常以 .data 、.rdata 或 .bss(Microsoft)、DATA(Borland)命名;
.資源段,通常以 .rsrc命名;
.導出表,通常以 .edata命名;
.導入表,通常以 .idata命名;
.調試信息段,通常以 .debug命名;
PE文件的結構在磁盤和內存中是基本一樣的,但在裝入內存中時又不是完全復制。Windows裝載器在裝載的時候僅僅建立好虛擬地址和PE文件之間的映射關系,只有真正執行到某個內存頁中的指令或訪問某一頁中的數據時,這個頁才會被從磁盤提交到物理內存。但因為裝載可執行文件時,有些數據在裝入前會被預先處理(如需要重定位的代碼),裝入以后,數據之間的相對位置也可能發生改變。因此,一個節的偏移和大小在裝入內存前后可能是完全不同的。
..
PE的基本結構就是這樣了。
下面開始各個部分學習。
==================================================
(1)IMAGE_DOS_HEADER和Dos Stub
其實IMAGE_DOS_HEADER和Dos Stub沒有什么重要的,只是IMAGE_DOS_HEADER中的第十九個成員指向IMAGE_NT_HEADERS的位置。
typedef struct IMAGE_DOS_HEADER { WORD e_magic; WORD e_cblp; WORD e_cp; WORD e_crlc; WORD e_cparhdr; WORD e_minalloc; WORD e_maxalloc; WORD e_ss; WORD e_sp; WORD e_csum; WORD e_ip; WORD e_cs; WORD e_lfarlc; WORD e_ovno; WORD e_res[4]; WORD e_oemid; WORD e_oeminfo; WORD e_res2[10]; DWORD e_lfanew; //指向IMAGE_NT_HEADERS的所在 }IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
下面我們用一個具體的PE文件來看看。
這是64個字節(64Byte),下面是一些分析。
首先,只要是PE文件,那么開始兩個字節就一定是4D 5A。
然后看最后四個字節,是E8000000,代表了地址是000000E8h,我們往下找找。
通過搜索,可以直接到達這個位置,一會我們再來看這個位置。
也就是說,00000040h到000000E8h之間的數據都是Dos Stub(稱為dos殘余程序)。
-----------------------------------------------------------------------------
(2)PE文件頭
現在我們來看看IMAGE_NT_HEADERS的情況,看看e_lfanew指向的這里是什么含義,看看000000E8h(這個數值是不固定的,不同的PE程序的值可能不同,我們只要找到這個位置,讀取它的值即可找到PE文件的頭所在)指向的這里又是什么。
typedef struct IMAGE_NT_HEADERS { DWORD Signature; IMAGE_FILE_HEADER FileHeader; IMAGE_OPTIONAL_HEADER32 OptionalHeader; }IMAGE_NT_HEADERS,*PIMAGE_NT_HEADERS;
首先我們看到的,這是一個結構體,而且結構體中還有結構體。
我們先看看它的總大小和在c32中的情況
【1】其中紅色的部分就是DWORD Signature,表示字符“PE\0\0”,十六進制為00004550h,這個值也是不會變化的。
【2】綠色線部分表示IMAGE_FILE_HEADER FileHeader,我們具體來看看這個結構體代表了什么含義。
typedef struct _IMAGE_FILE_HEADER { WORD Machine; //運行平台 WORD NumberOfSections; //塊(section)數目 DWORD TimeDateStamp; //時間日期標記 DWORD PointerToSymbolTable; //COFF符號指針,這是程序調試信息 DWORD NumberOfSymbols; //符號數 WORD SizeOfOptionalHeader; //可選部首長度,是IMAGE_OPTIONAL_HEADER的長度 WORD Characteristics; //文件屬性 }
第一個表示這個程序運行需要的平台,來看看我剛才這個程序的值
這里表示運行的平台是Intel的CPU,下面是一個列表代表各種對應的平台的值:
第二個表示塊的數目
0004當然就是4個節了,一會我們可以看看是哪四個節
第三個表示時間,我們先看看
也就是4F91318Fh,用計算器算一下就是1334915471,這個值應該是秒,我們換算一下,結果大約是42年
這個是程序的創建日期,減去42年,大約就是1970年,這個日期就是從1970到文件最后修改時間之間的秒數?
這個我可不知道,有待研究。
第六個SizeOfOptionalHeader表示之后OptionalHeader的大小,我們先來看看
00E0就是十進制的224,也就是說OptionalHeader的大小是224字節。
第七個值Characteristics表示文件屬性,它的每一個bit都代表了某種含義。
Bit 0 :置1表示文件中沒有重定向信息。每個段都有它們自己的重定向信息。 這個標志在可執行文件中沒有使用,在可執行文件中是用一個叫做基址重定向目錄表來表示重定向信息的,這將在下面介紹。 Bit 1 :置1表示該文件是可執行文件(也就是說不是一個目標文件或庫文件)。 Bit 2 :置1表示沒有行數信息;在可執行文件中沒有使用。 Bit 3 :置1表示沒有局部符號信息;在可執行文件中沒有使用。 Bit 4 : Bit 7 Bit 8 :表示希望機器為32位機。這個值永遠為1。 Bit 9 :表示沒有調試信息,在可執行文件中沒有使用。 Bit 10:置1表示該程序不能運行於可移動介質中(如軟驅或CD-ROM)。在這 種情況下,OS必須把文件拷貝到交換文件中執行。 Bit 11:置1表示程序不能在網上運行。在這種情況下,OS必須把文件拷貝到交換文件中執行。 Bit 12:置1表示文件是一個系統文件例如驅動程序。在可執行文件中沒有使用。 Bit 13:置1表示文件是一個動態鏈接庫(DLL)。 Bit 14:表示文件被設計成不能運行於多處理器系統中。 Bit 15:表示文件的字節順序如果不是機器所期望的,那么在讀出之前要進行 交換。在可執行文件中它們是不可信的(操作系統期望按正確的字節順序執行程序)。
010Fh就是0000000100001111
具體的我們來看一張圖:
【3】下面是OptionalHeader,占224個字節
我們看看 它的結構是怎樣的
typedef struct _IMAGE_OPTIONAL_HEADER { WORD Magic; BYTE MajorLinkerVersion; BYTE MinorLinkerVersion; DWORD SizeOfCode; DWORD SizeOfInitializedData; DWORD SizeOfUninitializedData; DWORD AddressOfEntryPoint; DWORD BaseOfCode; DWORD BaseOfData; DWORD ImageBase; DWORD SectionAlignment; DWORD FileAlignment; WORD MajorOperatingSystemVersion; WORD MinorOperatingSystemVersion; WORD MajorImageVersion; WORD MinorImageVersion; WORD MajorSubsystemVersion; WORD MinorSubsystemVersion; DWORD Win32VersionValue; DWORD SizeOfImage; DWORD SizeOfHeaders; DWORD CheckSum; WORD Subsystem; WORD DllCharacteristics; DWORD SizeOfStackReserve; DWORD SizeOfStackCommit; DWORD SizeOfHeapReserve; DWORD SizeOfHeapCommit; DWORD LoaderFlags; DWORD NumberOfRvaAndSizes; IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; } IMAGE_OPTIONAL_HEADER, *PIMAGE_OPTIONAL_HEADER;
好了,下面的內容將在下一篇文章中學習。