1. 程式人生 > >PE檔案結構(一) 基本結構

PE檔案結構(一) 基本結構

PE檔案結構(一)

參考

書:《加密與解密》

視訊:小甲魚 解密系列 視訊

        exe,dll都是PE(Portable Execute)檔案結構。PE檔案使用的是一個平面地址空間,所有程式碼和資料都被合併在一起,組成一個很大的結構。先看2張圖,來大概瞭解一下PE檔案結構。


PE檔案的框架結構


通過這張圖(開始在下面),我們可以知道PE檔案的大概結構,PE檔案是由 DOS頭,PE檔案頭,塊表,塊,除錯資訊 這些部分組成的。這些結構的定義在 winnt.h 中的  “Image Format”  這一節中。

PE檔案磁碟與記憶體映像結構圖


通過這張圖我們可以知道PE檔案對映到記憶體中的結構,PE檔案在磁碟中的結構與對映到記憶體中的結構的區別。PE檔案在磁碟中的結構與對映到記憶體中的結構基本相同,基本佈局也相同。DOS頭,PE頭,塊表相對開頭的地址在磁碟跟記憶體中相同。但是因為磁碟對齊跟記憶體對齊不同,還有window可能不會一次性載入完程式,會引起 後面塊項與開頭的偏移地址不同。

PE檔案被window載入到記憶體中後,記憶體中的版本就模組(Module)。對映檔案的起始地址叫做模組控制代碼(hModule),就是圖中基地址(ImageBase)。通過它,可以訪問到模組中的其他結構。

下面就來具體分析PE檔案結構:

MS-DOS頭部

PE檔案第一個位元組起始於一個傳統的MS-DOS頭部,被叫做 IMAGE_DOS_HEADER。

(下面的程式碼,可以在 winnt.h 中找到)

typedef struct _IMAGE_DOS_HEADER {
	WORD e_magic;   //DOS 可執行檔案標記 "MZ"  +0h
	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];
	LONG e_lfanew;     //指向PE檔案頭,"PE",0,0       +3ch
} IMAGE_DOS_HEADER,*PIMAGE_DOS_HEADER;

其中e_magic 和 e_lfanew 比較重要, e_magic  為 "MZ" ,e_lfanew 欄位是真正PE檔案的相對偏移(RVA)。

圖片3


PE檔案頭

接著DOS stub 後是PE檔案頭(PE Header),被叫做 IMAGE_NT_HEADERS

typedef struct _IMAGE_NT_HEADERS {
    DWORD Signature;                         //+0h  PE檔案頭  "PE"
    IMAGE_FILE_HEADER FileHeader;            //+4h
    IMAGE_OPTIONAL_HEADER32 OptionalHeader;  //+18h
} IMAGE_NT_HEADERS32,*PIMAGE_NT_HEADERS32;   

IMAGE_FILE_HEADER

IMAGE_FILE_HEADER  映像檔案頭, 包含了PE檔案的一些基本資訊。其中SizeOfOptionalHeader指出了IMAGE_OPTIONAL_HEADE 大小。

typedef struct _IMAGE_FILE_HEADER {
	WORD Machine;                     //+04h 執行平臺
	WORD NumberOfSections;            //+06h 檔案區塊(Section)的數目
 	DWORD TimeDateStamp;              //+08h 檔案的建立時間。這個值是從1970年1月1號以來格林威治時間計算的秒數
	DWORD PointerToSymbolTable;       //+0Ch 指向COFF符號表(用於除錯)
	DWORD NumberOfSymbols;            //+10h 符號表中符號個數(用於除錯)
 	WORD SizeOfOptionalHeader;        //+14h IMAGE_OPTINAL_HEADER結構的大小 32位檔案一般是00E0h,64位檔案一般是00F0h
	WORD Characteristics;             //+16h 檔案屬性,通過幾個值運算得到,這些些標誌定義在winnt.h 中的IMAGE_FILE_xx,exe檔案一般是010fh,dll一般是210Eh
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

圖片4

IMAGE_OPTIONAL_HEADER

IMAGE_OPTIONAL_HEADER 可選映像頭,雖然是一個可選結構,但是事實上IMAGE_FILE_HEADER 不夠用,需要IMAGE_OPTIONAL_HEADER定義更多的資料。

typedef struct _IMAGE_OPTIONAL_HEADER {
	WORD Magic;
	BYTE MajorLinkerVersion;
	BYTE MinorLinkerVersion;
	DWORD SizeOfCode;
	DWORD SizeOfInitializedData;
	DWORD SizeOfUninitializedData;
	DWORD AddressOfEntryPoint;         // +28h 程式執行入口RVA,dll檔案一般為0
	DWORD BaseOfCode;
	DWORD BaseOfData;
	DWORD ImageBase;                   // +34h 程式預設裝入基地址
	DWORD SectionAlignment;            // +38h 記憶體中區塊的對齊值,32位是 1000h(4K)
	DWORD FileAlignment;               // +3Ch 檔案中區塊的對齊值,一般是200h 或者 1000h
	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;                                              // +74h 資料目錄表的項數 。一直都是16
	IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];   // +78h 資料目錄表。其中有匯入表,到出表,資源表等
} IMAGE_OPTIONAL_HEADER32,*PIMAGE_OPTIONAL_HEADER32;


IMAGE_OPTIONAL_HEADER中的IMAGE_DATA_DIRECTORY是資料目錄表。定義了匯入表,到出表,資源表等的起始RVA與大小。

typedef struct _IMAGE_DATA_DIRECTORY {
    DWORD   VirtualAddress;     // 資料快的起始RVA
    DWORD   Size;               // 資料塊的長度
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

圖片5 

通過這張圖片我們可以知道,這個程式沒有輸出表,輸入表單其實RVA為543Ch,大小為3Ch。

區塊表

在PE檔案頭與原始資料之間存在一個區塊表。一般PE檔案至少需要 .text 跟 .data區塊。

typedef struct _IMAGE_SECTION_HEADER {
    BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];  // 塊名,IMAGE_SIZEOF_SHORT_NAME  為8
    union {
            DWORD   PhysicalAddress;
            DWORD   VirtualSize;            // 實際的區塊大小(即沒有對齊前的區塊大小)
    } Misc;
    DWORD   VirtualAddress;                 // 該塊裝載到記憶體中的RVA。第一個塊預設為1000h
    DWORD   SizeOfRawData;                  // 檔案在磁碟中對齊後的尺寸
    DWORD   PointerToRawData;               // 該區塊在磁碟中的偏移 
    DWORD   PointerToRelocations;
    DWORD   PointerToLinenumbers;
    WORD    NumberOfRelocations;
    WORD    NumberOfLinenumbers;
    DWORD   Characteristics;               // 區塊的屬性
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;