PE檔案結構解析 Part3 NT Headers

dewxin發表於2024-11-29

文章來源: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時間戳。
  • PointerToSymbolTableNumberOfSymbols: 這兩個欄位儲存了 到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的實際內容是從0x22000x2229,然而section剩餘部分被0填充直到0x23ff來滿足FileAlignment對齊的要求。
SizeOfImage映象被載入到記憶體中的大小被設定為 7000 並且 SizeOfHeaders被設定為400,兩個各自都是SectionAlignmentFileAlignment的倍數。

Subsystem欄位被設定為3,表示這是一個Windows控制檯程式。

DataDirectory下面會講。

總結

本篇文章到此結束,至此我們看了NT Headers結構,詳細討論了File Header和Optional Header。
下一篇文章我們會看一下Data Directories, Section Headers, 以及sections。

相關文章