typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header WORD e_magic; // Magic number WORD e_cblp; // Bytes on last page of file WORD e_cp; // Pages in file WORD e_crlc; // Relocations WORD e_cparhdr; // Size of header in paragraphs WORD e_minalloc; // Minimum extra paragraphs needed WORD e_maxalloc; // Maximum extra paragraphs needed WORD e_ss; // Initial (relative) SS value WORD e_sp; // Initial SP value WORD e_csum; // Checksum WORD e_ip; // Initial IP value WORD e_cs; // Initial (relative) CS value WORD e_lfarlc; // File address of relocation table WORD e_ovno; // Overlay number WORD e_res[4]; // Reserved words WORD e_oemid; // OEM identifier (for e_oeminfo) WORD e_oeminfo; // OEM information; e_oemid specific WORD e_res2[10]; // Reserved words LONG e_lfanew; // File address of new exe header } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
typedef struct _IMAGE_NT_HEADERS64 { DWORD Signature; //Signature PE文件標識,被定義爲00004550 IMAGE_FILE_HEADER FileHeader; //FileHeader 結構。該結構指向IMAGE_FILE_HEADER。 IMAGE_OPTIONAL_HEADER64 OptionalHeader; //OptionalHeader 結構。該結構指向_IMAGE_OPTIONAL_HEADER32。Windows操做系統可執行文件的大部分特性均在這個結構裏面呈現 } IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64; typedef struct _IMAGE_NT_HEADERS { DWORD Signature; IMAGE_FILE_HEADER FileHeader; IMAGE_OPTIONAL_HEADER32 OptionalHeader; } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32; typedef struct _IMAGE_ROM_HEADERS { IMAGE_FILE_HEADER FileHeader; IMAGE_ROM_OPTIONAL_HEADER OptionalHeader; } IMAGE_ROM_HEADERS, *PIMAGE_ROM_HEADERS; #ifdef _WIN64 typedef IMAGE_NT_HEADERS64 IMAGE_NT_HEADERS; typedef PIMAGE_NT_HEADERS64 PIMAGE_NT_HEADERS; #else typedef IMAGE_NT_HEADERS32 IMAGE_NT_HEADERS; typedef PIMAGE_NT_HEADERS32 PIMAGE_NT_HEADERS; #endif
文件頭結構:編程
typedef struct _IMAGE_FILE_HEADER { WORD Machine; //用來指定PE文件的運行平臺 /*Machine字段:用來指定PE文件運行的平臺。因爲Windows最初被設計爲能夠運行在Intel、Sun、Dec、 IBM等多種硬件平臺上,或者能模擬這些平臺的軟件環境中,而不一樣 的硬件平臺其指令的機器碼不相同,所以爲不一樣平臺編譯的EXE文件是沒法通用的。假設將運行在Intel 386機器上的PE文件的該字段設置爲01f0h,即指定平臺爲 IBM POWERPC (小尾方式),則系統會有如圖3-12所示的提示。該字段預約義的值如表3-2所示。*/ WORD NumberOfSections; // PE中節的數量 DWORD TimeDateStamp; // 文件建立日期 時間 /*TimeDateStamp字段:編譯器建立此文件時的時間戳。低32位存放的值是自1970年1月1日00:00時開始到建立時間爲止的總秒數。 該數值能夠隨意修改而不會影響程序運行。因此,有的連接器在這裏填人固定的值,有的則隨意寫入任何值,這對用戶建立的文件並無實際的意義。另外, 這個時間值與操做系統文件屬性裏看到的三個時間(建立時間、修改時間、訪問時間)也沒有任何聯繫。*/ DWORD PointerToSymbolTable; // 指向符號表 DWORD NumberOfSymbols; // 符號表 符號數量 WORD SizeOfOptionalHeader; // 擴展頭結構 /*SizeOfOptionalHeader字段:指定_IMAGE_OPTIONAL_HEADER的長度,默認狀況下這個值爲00e0h,若是是64位PE文件,該結構默認大小爲00F0h. 用戶能夠本身定義這個值的大小,不過須要注意: 更改完之後,須要自行將文件中的IMAGE_OPTIONAL_HEADER的大小擴充爲你指定的值。擴充完之後,要維持文件的對齊特性。*/ WORD Characteristics; // 文件屬性 位13 爲0 字段值 010fh 普通可執行PE文件 位13爲1 字段值 爲210eh Dll /*Characteristics字段:文件屬性字段。他的不一樣數據位定義了不一樣的文件屬性,具體內容見表3-3.這是一個很重要的字段,不一樣的定義將影響系統對文件的裝入方式,好比位 13爲1時,表示這是一個DLL文件,那麼系統將使用調用DLL入口函數的方式執行文件入口函數;當位13爲0時,表示這是一個普通的可執行文件,系統直接跳到入口處執行。對於 普通的可執行PE文件來講,這個字段的值通常是010fh,而對於DLL文件來講,這個字段的值通常是210eh。 如表3-3所示,當第0位爲1時,代表此文件不包含基址重定位信息,所以必須將其加載到文件頭中指定的基地址字段位置。 若是進程空間此處的基地址被佔用,加載器會 報錯。在程序運行前若是發現文件中存在可重定位信息, 連接器會執行移除可執行文件中的重定位信息的操做。 當第1位爲1時,代表此映像文件是合法的, 能夠運行。若是未設置此標誌, 代表出現了連接器錯誤。 當第7位爲1時,表示文件爲小尾方式,即內存中,最低有效位LSB位於最高有效位MSB的前面,與第15位的大尾方式(MSB在前,LSB在後)同樣, 都不同意使用該標誌,最好將其設置爲0。 當第10位爲1時,若是此映像文件在可移動存儲介質上,那麼加載器將徹底加載它並把它複製到內存交換文件中。 當第11位爲1時,若是此映像文件在網絡上,那麼加載器也將徹底加載它並把它複製到內存交換文件中。 當第13位爲1時,代表此映像文件是動態連接庫(DLL)。這樣的文件總被認爲是可執行文件,儘管它們並不能直接運行。 可執行文件的標誌位設置爲010fh,即第0、一、二、三、8位分別被設置爲1 (以下所示),表示該文件爲可執行文件,不含重定位信息,不含符號和行號信息,文件只在32位平臺運行。*/ } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
typedef struct _IMAGE_OPTIONAL_HEADER { // // Standard fields. // WORD Magic; // 標誌字, ROM 映像(0107h),普通可執行文件(010Bh) /*Magic字段 :說明文件的類型,若是爲010Bh,表面文件爲PE32;若是爲0107h,代表文件爲ROM映像;若是爲20Bh,表面文件爲PE64.*/ BYTE MajorLinkerVersion; // 連接程序的主版本號 BYTE MinorLinkerVersion; // 連接程序的次版本號 DWORD SizeOfCode; // 全部含代碼的節的總大小 DWORD SizeOfInitializedData; // 全部含已初始化數據的節的總大小 DWORD SizeOfUninitializedData; // 全部含未初始化數據的節的大小 DWORD AddressOfEntryPoint; // 程序執行入口RVA /*AddressOfEntryPoint字段 指出文件被執行時的入口地址,這是一個RVA地址。若是在一個可執行文件上附加了一段代碼並想讓這段代碼首先被執行,那麼只須要將這個 入口地址指向附加的代碼就能夠了。 */ DWORD BaseOfCode; // 代碼的區塊的起始RVA /*BaseOfCode字段 指出代碼段的起始RVA。在內存中,代碼段一般在PE文件頭以後、數據塊以前。在Microsoft連接器生成的執行文件中,RVA一般是1000h。 Borland的Tlink32是將ImageBase加上第一個Code Section的RVA,並將該結果存入該字段。*/ DWORD BaseOfData; // 數據的區塊的起始RVA // // NT additional fields. 如下是屬於NT結構增長的領域。 // DWORD ImageBase; // 程序的首選裝載地址 /*ImageBase字段 指出文件的優先裝入地址。也就是說當文件被執行時,若是可能的話,Windows優先將文件裝入到由ImageBase字段指定的地址中。只有指定的地址已經被**模塊 使用時,文件才被裝入到**地址中。連接器產生可執行文件的時候對應這個地址來生成機器碼,因此當文件被裝入這個地址時不須要進行重定位操做,裝入的速度最快。若是文件 被裝載到**地址的話,將不得不進行重定位操做,這樣就要慢一點。 對於EXE文件來講,因爲每一個文件老是使用獨立的虛擬地址空間,優先裝入地址不可能被**模塊佔據,因此EXE 老是可以按照這個地址裝入。這也意味着EXE文件再也不須要重定位信息。對於DLL文件來講,因爲多個DLL文件所有使用宿主EXE文件的地址空間,不能保證優先裝入地址沒有被**的 DLL使用,因此DLL文件中必須包含重定位信息以防萬一。所以,在前面介紹的 IMAGE_FILE_HEADER 結構的 Characteristics 字段中,DLL 文件對應的 IMAGE_FILE_RELOCS_STRIPPED 位老是爲0,而EXE文件的這個標誌位老是爲1。在連接的時候,能夠經過對link.exe指定/base:address選項來自定義優先裝入地址,若是不指定這個選項的話,通常EXE文件的默認 優先裝入地址被定爲00400000h,而DLL文件的默認優先裝入地址被定爲10000000h。*/ DWORD SectionAlignment; // 內存中的區塊的對齊大小 /*SectionAlignment 字段指定了節被裝入內存後的對齊單位。也就是說,每一個節被裝入的地址一定是本字段指定數值的整數倍。*/ DWORD FileAlignment; // 文件中的區塊的對齊大小 /*FileAlignment字段指定了節存儲在磁盤文件中時的對齊單位。 */ WORD MajorOperatingSystemVersion; // 要求操做系統最低版本號的主版本號 WORD MinorOperatingSystemVersion; // 要求操做系統最低版本號的副版本號 WORD MajorImageVersion; // 本PE文件映像的主版本號 WORD MinorImageVersion; // 本PE文件映像的次版本號 WORD MajorSubsystemVersion; // 運行所須要的子系統的主版本號 WORD MinorSubsystemVersion; // 運行所須要的子系統的次版本號 DWORD Win32VersionValue; // 子系統版本號,暫時保留未用。必須設置爲0 DWORD SizeOfImage; // 映像裝入內存後的總尺寸 +54h /*SizeOfImage字段。內存中整個PE文件的映射尺寸。以加載在內存中的xxx.exe爲例,xxx,exe中文件頭佔用了1000h字節,三個節各佔用了1000h個字節,因此文件在內存中佔用 的空間總大小是04000h。該值可能比實際的大,但不能比它小,並且必須保證該值是SectionAlignment的整數倍。 */ DWORD SizeOfHeaders; // 全部頭 + 區塊表的尺寸大小 /*SizeOfHeaders字段 指定全部頭+節表按照文件對齊粒度對齊後的大小(即含補足的0),在PE文件中,該部分數據是嚴格按照200h對齊的,若是不對齊,系統在加載時會提示出錯。*/ DWORD CheckSum; // 映像的校檢和 +5Ch WORD Subsystem; // 可執行文件指望的子系統 表3-4 /*Subsystem字段 指定使用界面的子系統,它的取值如表所示。這個字段決定了系統如何爲程序創建初始的界面,連接時的/subsystem:**選項指定的就是這個字段的值, 在前面章節的編程中咱們早已知道:若是將子系統指定爲Windows CUI,那麼系統會自動爲程序創建一個控制檯窗口,而指定爲Windows GUI的話,窗口必須由程序本身創建。 */ WORD DllCharacteristics; // Dll文件屬性 +60h 表3-6 /*DllCharacteristics字段 DLL文件屬性。它是一個標誌集,不是針對DLL文件的,而是針對因此PE文件的。這個字段定義了PE文件裝載時的一些特性。*/ DWORD SizeOfStackReserve; // 初始化時保留的棧大小 /*SizeOfStackReserve字段:初始化時保留棧的大小。該字段表示初始線程的棧而保留的虛擬內存數量,然而並非留出的全部虛擬內存均可以作棧 (真正的棧大小由下一個字段SizeOfStackCommit決定)。該字段的默認值是0x100000(1MB),若是調用API函數CreateThread時,把NULL看成傳入的參數 那麼建立出來的棧大小也會是1MB*/ DWORD SizeOfStackCommit; // 初始化時實際提交的棧大小 +68h /*SizeOfStackCommit字段:初始化時實際提交的棧大小。保證初始線程的棧實際佔用內存空間的大小,它是被系統提交的。這些提交的棧不存在於 交換文件裏,而是存在於內存中。對於Microsoft的連接器來講,這個值的初始值爲0x1000字節(1頁),對於TLINK32,則爲2頁。*/ DWORD SizeOfHeapReserve; // 初始化時保留的堆大小 /*SizeOfHeapReserve字段:初始化時保留的堆大小。用來保留給初始進程堆使用的虛擬內存,這個堆的句柄能夠經過調用GetProcessHeap函數得到。 每個進程至少會有一個默認的進程堆,改堆在進程啓動的時候被建立,並且在進程的生命期中永遠不會被刪除。默認值1MB,咱們能夠經過連接器 的「-heap」參數指定起始的保留堆內存大小和實際提交的堆大小。*/ DWORD SizeOfHeapCommit; // 初始化時實際提交的堆大小 +70h /*SizeOfHeapCommit字段:初始化時提交的堆大小,在進程初始化時設定的堆所佔用的內存空間。默認值爲1頁。*/ DWORD LoaderFlags; // 加載標誌 與調試有關,默認爲 0 DWORD NumberOfRvaAndSizes; // 下邊數據目錄的項數,這個字段自Windows NT 發佈以來一直是16 IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; // 數據目錄表 /*DataDirectory字段 這個字段能夠說是最重要的字段之一,它由16個相同的IMAGE_DATA_DIRECTORY結構組成。雖然PE文件中的數據是按照裝入內存後的頁屬性歸類而被放在不一樣 的節中的,可是這些處於各個節中的數據按照用途能夠被分爲導出表、導入表、資源、重定位表等數據塊,這16個IMAGE_DATA_DIRECTORY結構就是用來定義多種不一樣用途的 數據塊的。*/ } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
typedef struct _IMAGE_DATA_DIRECTORY { DWORD VirtualAddress;//數據起始RVA /*VirtualAddress 這個字段記錄了特定類型數據的其實RVA。固然,針對不一樣的數據結構,該字段包含的是數據含義並不同,有的數據甚至還不是RVA(如 屬性證書數據中該字段表示的是FOA)*/ DWORD Size; //數據塊長度 /*Size 這個字段記錄了特定類型的數據塊的長度。*/ } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
導入表結構:數組
typedef struct _IMAGE_IMPORT_DESCRIPTOR { union { ULONG Characteristics; // 最高位1 0 for terminating null import descriptor ULONG OriginalFirstThunk; //最高位0 RVA to original unbound IAT (橋一) } DUMMYUNIONNAME; ULONG TimeDateStamp; // 0 if not bound, // -1 if bound, and real date\time stamp // in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND) // O.W. date/time stamp of DLL bound to (Old BIND) ULONG ForwarderChain; //鏈表的前一個結構 ULONG Name; //該結構對應DLL的文件名 ULONG FirstThunk; // RVA to IAT (橋二) } IMAGE_IMPORT_DESCRIPTOR; typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;
typedef struct _IMAGE_EXPORT_DIRECTORY { DWORD Characteristics; DWORD TimeDateStamp; WORD MajorVersion; WORD MinorVersion; DWORD Name; DWORD Base; //Base:這個字段包含用於這個可執行文件導出表的起始序數值(基數)。正常地,這個值是1.可是並不須要非得這樣。當經過序數來查詢一個導出函數時,這個值加上序數,結果被用做進入導出地址表(EAT)的索引。
DWORD NumberOfFunctions; //NumberOfFunctions EAT中的條目數量。注意,一些條目多是0,代表用這個序數值沒有代碼或數據被導出。 DWORD NumberOfNames; //NumberOfNames 導出函數名稱表(ENT)裏的條目數量。這個值老是小於或等於NumberOfFunctions域值。小於的狀況發生在符號只經過序數來導出,另外當被賦值的序數裏有數字間距時也會是小於的。 DWORD AddressOfFunctions; // RVA from base of image // AddressOfFunctions; EATE的RVA。EAT是一個RVA數組,數組中的每個非零的RVA都對應於一個被導出的符號。 DWORD AddressOfNames; // RVA from base of image //AddressOfNames: ENT的RVA。 ENT是一個指向ASCII字符串的RVA數組。每個ASCII字符串對應於一個經過名字導出的符號。這個表是排序的,因此ASCII字符串也是按順序排列的。這容許加載器在查詢一個被導出的符號時 //用二進制查找方式,名稱的排序是二進制的(像C++ RTL中stremp函數提供的同樣),而不是一個環境特定的字母順序。 DWORD AddressOfNameOrdinals; // RVA from base of image //AddressofNameOrdinals:導出序數表的RVA。這個表是字的數組。這個表將ENT中的數組索引映射到相應的導出地址表條目。 } IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
typedef struct _IMAGE_TLS_DIRECTORY32 { DWORD StartAddressOfRawData;//-定範圍內存的起始地址 DWORD EndAddressOfRawData;//必定範圍內存的各止地址 PDWORD AddressOfIndex;//索引地址 PIMAGE_TLS_CALLBACK *lddressOfCallBacks;//回調函數指針數組的地址PIMAGE_TLS_CALLBACK * DWORD SizeOfZeroF1ll;//初始化數據的大小 union { DWORD Characteristics; struct { DWORD Reserved0 : 20; DWORD Alignment : 4; DWORD Reserved1 : 8; }DUMMYSTRUCTNAME; }DUMMYUNIONNAME; }IMAGE_TLS_DIRECTORY32; typedef IMAGE_TLS_DIRECTORY32 * PIMAGE_TLS_DIRECTORY32;
#define IMAGE_SIZEOF_SHORT_NAME 8 typedef struct _IMAGE_SECTION_HEADER { BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; // \0結尾的節的名稱 union { DWORD PhysicalAddress; DWORD VirtualSize; /*VirtualSize: 指出實際的,被使用的區塊大小,是區塊在沒有對齊處理前的實際大小,若是VirtualSize大於SizeOfRawData,那麼SizeOfRawData是來自 可執行文件初始化數據的大小,與VirtualSize相差的字節用零填充。這個字段在OBJ文件中是被設爲0的。*/ } Misc; //節的數據在沒有對齊前的尺寸 DWORD VirtualAddress; //該塊裝載到內存中的RVA 第一個塊的默認RVA爲1000h /*VirtualAddress:該塊裝載到內存中的RVA.這個地址是按照內存頁對齊的。它的數值老是SectionAlignment的整數倍。在Microsofe工具中,第一個塊的 默認RVA爲1000h。在OBJ中,該字段沒有意義,並被設爲0。*/ DWORD SizeOfRawData; //節在文件中對齊後的尺寸。 /*SizeOfRawData:該塊在磁盤文件中所佔的大小。在可執行文件中,該字段包含通過FileAlignment調整後的塊的長度。例如,指定FileAlignment的大小 爲200h,若是VirtualSize中的塊長度爲19Ah個字節,這一塊應保存的長度爲200h個字節。*/ DWORD PointerToRawData; //節起始數據在文件中的偏移 /*PointerToRawData.該塊在磁盤文件中的偏移。程序經編譯或彙編生成原始數據,這個字段用於給出原始數據在文件中的偏移。若是程序自裝載PE或COFF文件 (不是由操做系統裝入),這一字段比VirtualAddress還重要。在這種狀態下,必須徹底使用線性映象方法裝入文件,全部須要在該偏移處找到塊的數據,而不是 VirtualAddress字段中的RVA地址。*/ DWORD PointerToRelocations; //在「.obj」文件中使用,指向重定位表的指針 DWORD PointerToLinenumbers; //行號表的位置(調試用) WORD NumberOfRelocations; //重定位表的個數(「.obj」文件) WORD NumberOfLinenumbers; //行號表中行號的數量 DWORD Characteristics; //節的屬性 /*Characteristics 節的屬性。這個字段很重要,這是節的屬性標誌字段,其中不一樣的數據爲表明了不一樣的屬性,具體的定義如表3-7所示。這些數據位的組合 描述了內存中的一個節所擁有的訪問屬性。*/ } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
typedef struct _IMAGE_BASE_RELOCATION { ULONG VirtualAddress; //表示上述公式中第一個4 即重定位內存頁的起始RVA ULONG SizeOfBlock; //重定位塊長度 // USHORT TypeOffset[1]; } IMAGE_BASE_RELOCATION;