文章來源:https://0xrick.github.io/win-internals/pe4/
- 簡介
- NT Headers(IMAGE_NT_HEADERS)
- 簽名 Signature
- File Header(IMAGE_FILE_HEADER)
- Optional Header (IMAGE_OPTIONAL_HEADER)
- 總結
簡介
在前面的文章中,我們看過了DOS Header的結構以及逆向了DOS stub。
這篇文章我們準備討論一下PE檔案結構中NT Header的部分。
在我們進入正題之前,我們需要講一講等下我們會用到很多次的一個重要的概念,相對虛擬地址(Relative Virtual Address)或者RVA。 RVA(相對虛擬地址)就是相對於EXE在記憶體中起始地址的一個偏移(相對於Image Base)。也就是說,將相對虛擬地址Relative Virtual Address轉化為絕對虛擬地址,我們需要將RVA的值加上ImageBase的值。接下來我們會看到,PE很多地方都會用到RVA。
NT Headers(IMAGE_NT_HEADERS)
NT Headers是一個定義在winnt.h
的結構體IMAGE_NT_HEADERS
,觀察它的定義,我們可以看到它有三個成員(DWORD型別的簽名,IMAGE_FILE_HEADER
型別的FileHeader,以及IMAGE_OPTIONAL_HEADER
型別的OptionalHeader)。
值得一提的是,這個結構體有兩個版本的定義。一個用於32bit,IMAGE_NT_HEADERS
。 一個用於64bit,IMAGE_NT_HEADERS64
。
typedef struct _IMAGE_NT_HEADERS64 {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER64 OptionalHeader;
} 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;
簽名 Signature
NT Headers結構體的第一個成員是PE簽名,它是DWORD
型別,意味著它需要4位元組的空間。
這個欄位的值總是固定為0x50450000
,用ASCII碼錶示為PE\0\0
。
下面是來自PE-Bear的截圖。
File Header(IMAGE_FILE_HEADER)
也被稱作"COFF File Header", File Header這個結構體持有PE檔案的一些資訊。
它在winnt.h
中定義為IAMGE_FILE_HEADER
, 下面是它的定義:
typedef struct _IMAGE_FILE_HEADER {
WORD Machine;
WORD NumberOfSections;
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;
WORD Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
這是一個有著7個成員的結構體:
Machine
:用於表示可執行檔案的目標機器,型別是WORD。這個欄位可以有很多值,但是我們只對其中兩個感興趣,0x8864 for AMD64 and 0x14c for i386. 想要了解全部可能的值,可以訪問微軟官方文件。NumberOfSections
:這個欄位儲存了sections的個數。(也可以說是section header的個數)TimeDateStamp
: 表示檔案被建立時的unix
時間戳。PointerToSymbolTable
和NumberOfSymbols
: 這兩個欄位儲存了 到COFF符號表的偏移以及符號表中有幾個物件。它們也可能被設定為0,意味著沒有符號表,這是因為COFF除錯資訊被丟棄了。SizeOfOptionalHeader
:Optional Header的大小。Characteristics
:用於表示檔案屬性的flag。這些屬性可以是檔案能否被執行,是否是系統檔案,以及很多其他資訊。詳情可以訪問微軟官方文件。
下面是一個真實的PE檔案的PE Header的截圖。
Optional Header (IMAGE_OPTIONAL_HEADER)
Optional Header是NT headers中最重要的,PE載入器會查詢這個header中提供的特定的資訊來載入以及執行EXE檔案。
它被稱為可選頭部資訊是因為有些檔案型別 像是obj檔案不需要,但是這個header對於映象檔案image file很重要。
Optional Header沒有固定的大小,所以會存在 IMAGE_FILE_HEADER.SizeOfOptionalHeader
。
Optional Header前8個成員對於COFF檔案格式來說是必須實現的標準,header剩餘的部分是微軟對標準定義的一個擴充套件,結構體中擴充套件部分的成員會被用於Windows的PE載入器以及連結器。
正如之前提到的,Optional Header 有兩個版本,一個用於32bit的Exe,一個用於64bit。這兩個版本有以下兩方面的區別:
- 結構體本身的大小(或者說結構體中定義的成員的數量):
IMAGE_OPTIONAL_HEADER32
有31個成員,而IMAGE_OPTIONAL_HEADER64
只有30個成員, 32bit版本多出的成員為DWORD
型別的BaseOfData
,儲存了data section起始位置的Relative Virtual Address. - 一些成員的資料型別:下面5個成員在32bit版本中是
DWORD
,在64bit版本中是ULONGLONG
:- ImageBase
- SizeOfStackReserve
- SizeOfStackCommit
- SizeOfHeapReserve
- SizeOfHeapCommit
我們來看一下兩個結構體的定義:
typedef struct _IMAGE_OPTIONAL_HEADER {
//
// Standard fields.
//
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
DWORD BaseOfData;
//
// NT additional fields.
//
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_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
每個欄位的詳細解釋可以看對應的文件。
讓我們看一下實際PE檔案中Optional Header的內容。
我們可以討論一下其中的一些欄位,首先是Magic
欄位,它的值是0x20B
意味著這是一個64位的可執行程式。
我們可以看到程式入口點entry point的相對虛擬地址RVA是0x12C4
並且程式碼段Code Section開始於相對虛擬地址0x1000
,記憶體中的段對齊SectionAlignment
的大小也是0x1000
.
檔案中的段對齊File Alignment被設定為0x200
,並且我們可以觀察任何一個section來驗證。
你可以看到,data section的實際內容是從0x2200
到0x2229
,然而section剩餘部分被0填充直到0x23ff
來滿足FileAlignment
對齊的要求。
SizeOfImage
映象被載入到記憶體中的大小被設定為 7000 並且 SizeOfHeaders
被設定為400,兩個各自都是SectionAlignment
和FileAlignment
的倍數。
Subsystem
欄位被設定為3,表示這是一個Windows控制檯程式。
DataDirectory
下面會講。
總結
本篇文章到此結束,至此我們看了NT Headers結構,詳細討論了File Header和Optional Header。
下一篇文章我們會看一下Data Directories, Section Headers, 以及sections。