PE知識復習之PE的各種頭屬性解析
一丶DOS頭結構體
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;
DOS頭是在16位程序下使用的.所以不用全部關心.只需要關心第一個跟最后一個成員記住即可.
DOS頭大小是64個字節,十六進制是0x40 總結一下就是說. 4行只有第一行的前兩個字節.以及最后一行的4個字節有用.
WORD e_magic 這個成員是操作系統檢查的MZ頭.
LONG e_lfanew 這個成員指向PE頭.也很重要.
如果上面兩個成員更改了.那么文件就不能運行了.
二丶NT頭解析
NT頭也是我們所指的PE頭. 其中NT頭包括了文件頭跟擴展頭.
typedef struct _IMAGE_NT_HEADERS { DWORD Signature; PE標識 IMAGE_FILE_HEADER FileHeader; 文件頭 IMAGE_OPTIONAL_HEADER32 OptionalHeader;擴展頭 } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
NT頭的第一個成員也很重要.是PE標識.占4個字節. DOS頭的最后一個成員的偏移.就是NT頭.(PE)
例如:
操作系統會檢查PE頭.以及MZ頭.檢查是否是正確的值.
而NT頭中有兩個子結構體.一個是文件頭.一個是擴展頭.這兩個頭比較重要.
三丶文件頭解析
文件頭挺重要的.里面存儲了我們的節表個數.等等一些列信息.跟擴展頭息息相關.
文件頭大小是20個字節. 十六進制 是0x14大小.如果按照16一行分組.那么就是一行零4個字節.是文件頭總大小.
typedef struct _IMAGE_FILE_HEADER { WORD Machine; 機器型號.表名了我們CPU執行的這個PE文件是x86的還是x64的.有一系列宏標識. WORD NumberOfSections; 節表個數. 此成員很重要.標識着我們的節表有多少個.如果節個數小於節的總數那么程序就不能運行. DWORD TimeDateStamp; 文件時間.不重要.與文件屬性里面的創建事件修改時間無關.編譯器填寫的
DWORD PointerToSymbolTable; 調試器相關 DWORD NumberOfSymbols; 調試器相關. WORD SizeOfOptionalHeader; 擴展PE頭大小,此成員很重要.表明了我們的擴展頭總體大小. WORD Characteristics; 文件屬性 } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
machine 標識 #define IMAGE_FILE_MACHINE_IA64 0x0200 // Intel 64 代表是x64運行的程序. 其中 0x14c則帶便是86運行的程序.具體可以查看宏
文件頭中標紅的都很重要. 一個是操作系統判斷是什么系統運行的文件.一個是當前PE的節個數.一個表明了擴展頭的大小.一個表明了文件屬性
總結: 一行零4個字節.其中前4個字節很重要.分別表示機器型號.以及節個數. 一行零2個字節表明了擴展頭大小.也很重要.
關於最后一個成員 是按位來做的.具體成員如下.
四丶擴展頭解析
擴展頭的大小.在我們的文件頭中標識着. 一般是E0大小.擴展頭是可以更改的. E0十進制大小是224個字節.
我們看一下擴展頭結構.
x86跟x64的擴展頭是不一樣的.我們直說一下x86
typedef struct _IMAGE_OPTIONAL_HEADER { // // Standard fields. // WORD Magic; //標志.表名了我們的PE是x86還是x64 BYTE MajorLinkerVersion; //連接器主要版本號 BYTE MinorLinkerVersion; //連接器次要版本號 例如 3.54 主要版本就是3.次要就是54 DWORD SizeOfCode; //代碼段大小,以字節為單位. DWORD SizeOfInitializedData; //初始化數據部分的大小. DWORD SizeOfUninitializedData; //未知初始化數據的大小 DWORD AddressOfEntryPoint; //OEP 程序入口點,驅動程序也是入口點.對於DLL而言.是可選的.沒有入口則為0 DWORD BaseOfCode; //指向代碼部分的指針 DWORD BaseOfData; //指向數據部分開頭的指針 // // NT additional fields. // DWORD ImageBase; //基址.PE文件加載到內存中的基址.這個值是64k的倍數.DLL默認值是0x100000000,應用程序默認是0x00400000
windows CE除外.他是0x00010000 DWORD SectionAlignment; //PE文件加載到內存中.的內存對齊.按照這個成員進行對齊 DWORD FileAlignment; //文件對齊,PE存數據存放在文件中.按照文件對其值對其 WORD MajorOperatingSystemVersion;//所需要操作系統的主要版本號. WORD MinorOperatingSystemVersion;//所需要操作系統的次要版本號. WORD MajorImageVersion; //PE主版本號 WORD MinorImageVersion; //PE次版本號 WORD MajorSubsystemVersion; //子系統主要版本號. WORD MinorSubsystemVersion; //子系統次要版本號. DWORD Win32VersionValue; //保留成員,必須為0 DWORD SizeOfImage; //PE鏡像大小.必須是內存對齊的倍數. sizeofImage/SectionAllignment == 0 才可以 DWORD SizeOfHeaders; // DOS頭+NT頭+節表的總大小.按照文件對齊存放 sizeofHeaders / FileAlignment == 0
DWORD SubSystem //表名PE文件是什么程序. 1驅動程序2窗口程序3控制台程序(DLL) DWORD CheckSum; WORD DllCharacteristics; //P的文件屬性 DWORD SizeOfStackReserve; //堆棧保留字節數.我們的程序使用的棧空間多大靠這個成員.不過操作系統只作為參考 DWORD SizeOfStackCommit; //要為堆棧提交的字節數.不做參考 DWORD SizeOfHeapReserve; //堆保留字節數. DWORD SizeOfHeapCommit; //本地堆提交的字節數. PS: 棧堆保留數值.斗魚自己的sizeof(Head/stack)Commit成員有關. DWORD LoaderFlags; //成員已經過時 DWORD NumberOfRvaAndSizes; //數據目錄數組的大小 IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];//數據目錄 } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
總結來說.上面的成員挺多的.也很重要.
OEP AddressOfEntryPoint 指明了PE入口點.在擴展頭的0x10字節位置.也就是擴展頭往下數一行就是OEP. OEP所在位置是一行零4個字節.
ImageBase 基址. 指明了PE加載時候的基址.基址+ OEP就能確定代碼在哪里開始運行. 0x1c位置處.也就是擴展頭下數一行零12個字節,下面4個字節就是Image. Image所在位置是2行位置.
SectionAlignment 內存對齊,PE加載到內存中所需要的對齊值. 在擴展頭兩行位置處.往下數4個字節就是. 所在位置兩行零4個字節
FileAlignment 文件對齊,PE存放在文件中的數據的對齊值.擴展頭兩行零4個字節位置. 所在位置是兩行零八個字節.也就是兩行半.
SizeOfImage PE的鏡像大小. 擴展頭 三行半位置往下數4個字節 所在位置.三行零12字節位置處.
SizeOfHeaders DOS頭+NT頭+節表的大小.按照文件對齊放在文件中的成員. 三行零12字節位置處往下數4個字節. 所在位置是4行位置.
NumberOfRvaAndSizes 數據目錄大小.所在位置六行位置處 下面都是數據目錄了.數據目錄指明了導入表導出表等等一些列的表格位置
關於擴展頭.重要成員就這么多. 主要就是熟悉各成員之間的關系.
sizeofImage 跟 內存對齊成員有關 sizeofImage / sectionAlignment == 0
sizeofHeaders 頭大小.跟文件對齊有關 SizeofHeaders / FileAlignment == 0
五丶數據目錄
數據目錄在我們的擴展頭中.作為一個子結構體存放
typedef struct _IMAGE_DATA_DIRECTORY { DWORD VirtualAddress; DWORD Size; } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
數據目錄的作用就是指明了PE文件的導入表.導出表等等一些列表格在哪里存放. 有兩個成員.一個是虛擬地址.一個是大小.