1. 程式人生 > >PE檔案結構解析

PE檔案結構解析

t.exe共 3072 bytes,下面是t.exe映象 PE 檔案頭的整體結構圖:

t.exe 映象
windows 的 PE 檔案頭結構包括三大部分:DOS 檔案頭、NT 檔案頭以及 Section 表(節表),在 DOS 檔案頭後面有一小段 DOS 程式,被稱為 DOS stub 程式。

DOS stub 程式是執行在 DOS 下面的 16 位程式,目的是指出:當 windows 程式在 dos 下執行時,將顯示資訊:This program cannot be run in DOS mode.... 然後終止執行。

這段 DOS stub 程式是這樣的:

00000040  0E                push cs
00000041  1F                pop ds
00000042  BA0E00            mov dx,0xe


00000045  B409              mov ah,0x9
00000047  CD21              int 0x21
00000049  B8014C            mov ax,0x4c01
0000004C  CD21              int 0x21

資訊字串在位置 0x0e 上,即在:0x00000040 + 0x0e =0x0000004e,這正好是字元資訊“This program cannot be run in DOS mode....$”的地址。

6.1 MS-DOS 檔案頭

t.exe映象檔案頭最開始部分是 MS-DOS 檔案頭部分,這個檔案頭結構定義在WinNT.h檔案裡,在我的 windows 7 系統 visual studio 2010 下,這個 winnt.h 在目錄:C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Include

下,在 WinNT.h 檔案裡定義了IMAGE_DOS_HEADER結構,它的定義如下:

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;

這個IMAGE_DOS_HEADER結構共 64 bytes,從映象的 0x00000000 - 0x0000003F,參見上圖標註的“DOS 檔案頭”部分,將這部分按照IMAGE_DOS_HEADER結構分解為:

00000000 4D 5A    // e_magic
00000002 90 00    // e_cblp
00000004 03 00    // e_cp
00000006 00 00    // e_cric
00000008 04 00    // e_cparhdr
0000000A 00 00    // e_minalloc
0000000C FF FF    // e_maxalloc
0000000E 00 00   // e_ss
00000010 B8 00    // e_sp
00000012 00 00    // e_csum
00000014 00 00    // e_ip
00000016 00 00    // e_cs
00000018 40 00    // e_lfarlc
0000001A 00 00    // e_ovno
0000001C 00 00 00 00   // e_res[4]
00000020 00 00 00 00   
00000024 00 00    // e_oemid
00000026 00 00    // e_oeminfo
00000028 00 00 00 00  // e_res2[10]
0000002C 00 00 00 00
00000030 00 00 00 00
00000034 00 00 00 00
00000038 00 00 00 00
0000003C C0 00 00 00  // e_lfanew

其中最重要的是:e_magice_lfanew域(上面紅色部分標註),e_magic是 MS-DOS 檔案頭的簽名,它的值是:0x5A4D,這個簽名在WinNT.h中定義為:

#define IMAGE_DOS_SIGNATURE                 0x5A4D      // MZ

它的值代表字母 MZ,表示 MS-DOS 檔案頭。e_lfanew是一個 offset 偏移量,指出IMAGE_NT_HEADER在映象中的位置,IMAGE_NT_HEADER是 PE 檔案的核心部分,IMAGE_NT_HEADER在 WinNT.h 中定義為兩個版本,分別是:IMAGE_NT_HEADERS64IMAGE_NT_HEADERS32,它們的定義如下:

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;

當使用在 win64 下時,IMAGE_NT_HEADER使用的是 64 位版本IMAGE_NT_HEADERS64,當使用在 win32 下時,IMAGE_NT_HEADER使用的是IMAGE_NT_HEADERS32

#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

在本例t.exe映象裡e_lfanew0x000000C0,它說明IMAGE_NT_HEADERS結構位於映象的 0x000000C0 處,此時尚不能斷定這個 PE 是 32 位還是 64 位結構。

6.2 IMAGE_NT_HEADER 結構

IMAGE_NT_HEADERS結構的定義裡得出,它包含了一個簽名和兩個結構體,這個簽名是 0x00004550 表示 PE 檔案:

#define IMAGE_NT_SIGNATURE                  0x00004550  // PE00

6.2.1 IMAGE_FILE_HEADER 結構

IMAGE_NT_HEADER的中IMAGE_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;

這個結構共 20 bytes,下面是這些域的描述:

size 描述
Machine WORD IMAGE_FILE_MACHINE_xxx 表示目標平臺 processor 型別,例:IMAGE_FILE_MACHINE_I386
NumberOfSection WORD --- 表示映象中有多少個 section
TimeDataStamp DWORD 從1970年1月1日0:00 以來的總秒數 表示檔案建立的時間
PointerToSymbolTable DWORD COFF 符號表偏移量 在 PE 中很少見,總是為 0
NumberOfSymbols DWORD COFF 符號表的個數 如果存在的話,表示符號表的個數
SizeOfOptionHeader WORD IMAGE_OPTIONAL_HEADER 結構大小 該域表示IMAGE_NT_HEADER中的IMAGE_OPTIONAL_HEADER結構的大小
Characteristics WORD IMAGE_FILE_xxx 表示檔案屬性,例如:IMAGE_FILE_DLL屬性

IMAGE_FILE_HEADER 結構中比較重要的域是:MachineSizeOfOptionalHeader,Machine可以用來判斷目標平臺,比如:值為 0x8664 是代表 AMD64(即:x64 平臺)它也適合 Intel64 平臺。SizeOfOptionalHeader指出IMAGE_OPTIONAL_HEADER結構的大小。

WinNT.h檔案裡定義了一系列的 Machine 值,這裡舉列一些,詳細的參見 WinNT.h 檔案:

#define IMAGE_FILE_MACHINE_UNKNOWN           0
#define IMAGE_FILE_MACHINE_I386              0x014c  // Intel 386.
#define IMAGE_FILE_MACHINE_ALPHA             0x0184  // Alpha_AXP
#define IMAGE_FILE_MACHINE_POWERPC           0x01F0  // IBM PowerPC Little-Endian
#define IMAGE_FILE_MACHINE_AMD64             0x8664  // AMD64 (K8)

也為 Characteristics 定義了一系列的常量,這些定義的常量值,代表映象是什麼型別的檔案:

#define IMAGE_FILE_RELOCS_STRIPPED           0x0001  // Relocation info stripped from file.
#define IMAGE_FILE_EXECUTABLE_IMAGE          0x0002  // File is executable  (i.e. no unresolved externel references).
#define IMAGE_FILE_LINE_NUMS_STRIPPED        0x0004  // Line nunbers stripped from file.
#define IMAGE_FILE_LOCAL_SYMS_STRIPPED       0x0008  // Local symbols stripped from file.
#define IMAGE_FILE_AGGRESIVE_WS_TRIM         0x0010  // Agressively trim working set
#define IMAGE_FILE_LARGE_ADDRESS_AWARE       0x0020  // App can handle >2gb addresses
#define IMAGE_FILE_BYTES_REVERSED_LO         0x0080  // Bytes of machine word are reversed.
#define IMAGE_FILE_32BIT_MACHINE             0x0100  // 32 bit word machine.
#define IMAGE_FILE_DEBUG_STRIPPED            0x0200  // Debugging info stripped from file in .DBG file
#define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP   0x0400  // If Image is on removable media, copy and run from the swap file.
#define IMAGE_FILE_NET_RUN_FROM_SWAP         0x0800  // If Image is on Net, copy and run from the swap file.
#define IMAGE_FILE_SYSTEM                    0x1000  // System File.
#define IMAGE_FILE_DLL                       0x2000  // File is a DLL.
#define IMAGE_FILE_UP_SYSTEM_ONLY            0x4000  // File should only be run on a UP machine
#define IMAGE_FILE_BYTES_REVERSED_HI         0x8000  // Bytes of machine word are reversed.

下面看看t.exe裡的IMAGE_FILE_HEADER具體的值,如下圖所示:

IMAGE_FILE_HEADER 結構

t.exe映象的目標平臺是 x64 平臺,它指出了接下來的 IMAGE_OPTIONAL_HEADER結構大小為:0x00F0 bytes

注意:雖然在 Machine 裡指出目標平臺,但是對於判斷映象是 32 位還是 64 位的 PE 檔案,Microsoft 官方認可以的方法是:通過IMAGE_OPTIONAL_HEADER結構裡的Magic

6.2.2 IMAGE_OPTIONAL_HEADER 結構

IMAGE_NT_HEADER一樣,IMAGE_OPTIONAL_HEADER也有 32 位版本和 64 位版本,因此,相應版本的IMAGE_NT_HEADER對應相應版本的IMAGE_OPTIONAL_HEADER。雖然這個結構被稱為 IMAGE_OPTIONAL_HEADER(可選),但是它卻是必須存在於IMAGE_NT_HEADER結構中。

下面是 64 位版本的IMAGE_OPTIONAL_HEADER結構定義, 32 位版本的參見 WinNT.h 中的定義

typedef struct _IMAGE_OPTIONAL_HEADER64 {
    WORD        Magic;
    BYTE        MajorLinkerVersion;
    BYTE        MinorLinkerVersion;
    DWORD       SizeOfCode;
    DWORD       SizeOfInitializedData;
    DWORD       SizeOfUninitializedData;
    DWORD       AddressOfEntryPoint;
    DWORD       BaseOfCode;
    ULONGLONG   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;
    ULONGLONG   SizeOfStackReserve;
    ULONGLONG   SizeOfStackCommit;
    ULONGLONG   SizeOfHeapReserve;
    ULONGLONG   SizeOfHeapCommit;
    DWORD       LoaderFlags;
    DWORD       NumberOfRvaAndSizes;
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;

IMAGE_OPTIONAL_HEADER結構的定義稍長一些,下面是自來 Matt Pietrek 所寫的文章,名為《An In-Depth Look into the Win32 Portable Executable File Format》中對IMAGE_OPTIONAL_HEADER結構的描述,地址在:http://msdn.microsoft.com/en-us/magazine/bb985997.aspx其中的Figure 5 IMAGE_OPTIONAL_HEADER一節裡對 IMAGE_OPTIONAL_HEADER 結構有詳細的描述。這裡就不再描述了。 :)

關鍵的一點:

  在IMAGE_OPTIONAL_HEADER裡,第 1 個域magic用來識別檔案頭是 32 位還是 64 位

這個 magic 的值在 WinNT.h 的定義如下:

#define IMAGE_NT_OPTIONAL_HDR32_MAGIC      0x10b
#define IMAGE_NT_OPTIONAL_HDR64_MAGIC      0x20b

當 magic = 0x10b 時,映象是 32 位,magic = 0x20b 時,映象是 64 位。

6.3 觀察 t.exe 映象的實際 IMAGE_NT_HEADER 結構

在我們例項 t.exe 映象中,IMAGE_NT_HEADER的偏移量是0x000000C0IMAGE_NT_HEADER結構在 32 位下是 244 bytes,在 64 位下是 264 bytes

下面我們來看一看 t.exe 映象中的 IMAGE_NT_HEADER 結構,從 0x0000000C0 - 0x000001C7(共 264 bytes)

IMAGE_NT_HEADER結構的簽名 Signature 是 0x00004550 表示 PE 檔案頭。

6.3.1 t.exe 的 IMAGE_FILE_HEADER 結構

藍色部分IMAGE_FILE_HEADER結構,IMAGE_FILE_HEADER結構緊接著 PE 簽名之後,共 20 bytes,其中的Machine是 0x8664,由上述定義所得,它代表的機器型別是AMD64處理器

000000C4  64 86    // Machine = AMD64
000000C6  04 00    // NumberOfSection = 4
000000C8  91 86 CC 4B   // TimeDataStamp = 1271694993 秒
000000CC  00 00 00 00   // PointerToSymbolTable = 0
000000D0  00 00 00 00   // NumberOfSymbols = 0
000000D4  F0 00    // SizeOfOptionHeader = 0xf0(240 bytes)
000000D6  22 00    // Characteristics = 0x0022

它的檔案屬性是 0x0022,也就是:Characteristics =IMAGE_FILE_EXECUTABLE_IMAGE|IMAGE_FILE_LARGE_ADDRESS_AWARE,說明它是一個可執行的映象,可以在 >2G 地址上,並且指明瞭接下來的IMAGE_OPTIONAL_HEADER結構是 0xf0 bytes(240 個位元組)。

6.3.2 t.exe 的 IMAGE_OPTIONAL_HEADER 結構

IMAGE_FILE_HEADER結構接下來就是IMAGE_OPTIONAL_HEADER結構,前面的 SizeOfOptionHeader 域已經指出IMAGE_OPTIONAL_HEADER將會是 0xf0 個位元組。

000000D8  0B 02                // Magic = 0x020b
000000DA  0A                   // MajorLinkerVersion = 0x0A (10 版)
000000DB  00                   // MinorLinkerVersion = 0x00
000000DC  00 02 00 00          // SizeOfCode = 0x0200
000000E0  00 06 00 00          // SizeOfInitializedData = 0x0600
000000E4  00 00 00 00          // SizeOfUninitializedData = 0
000000E8  00 10 00 00          // AddressOfEntryPoint = 0x00001000
000000EC  00 10 00 00          // BaseOfCode = 0x00001000
000000F0  00 00 00 40 01 00 00 00     // ImageBase = 0x00000001_40000000
000000F8  00 10 00 00                 // SectionAlignment = 0x00001000
000000FC  00 02 00 00                 // FileAligment = 0x00000200
00000100  05 00                       // MajorOperationSystemVersion = 0x0005
00000102  02 00                       // MinorOperationSystemVersion = 0x0002
00000104  00 00                       // MajorImageVersion = 0
00000106  00 00                       // MinorImageVersion = 0
00000108  05 00                       // MajorSubsystemVersion = 5
0000010A  02 00                       // MinorSubsystemVersion = 2
0000010C  00 00 00 00                 // Win32VersionValue = 0
00000110  00 50 00 00                 // SizeOfImage = 0x00005000
00000114  00 04 00 00                 // SizeOfHeaders = 0x00000400
00000118  00 00 00 00                 // CheckSum = 0
0000011C  02 00                       // subsystem = 2
0000011E  40 81                       // DllCharacteristics = 0x8140
00000120  00 00 10 00 00 00 00 00     // SizeOfStackReserve = 0x00000000_00100000
00000128  00 10 00 00 00 00 00 00     // SizeOfStackCommit = 0x00000000_00001000
00000130  00 00 10 00 00 00 00 00     // SizeOfHeapReserve = 0x00000000_00100000
00000138  00 10 00 00 00 00 00 00     // SizeOfHeapCommit = 0x00000000_00001000
00000140  00 00 00 00                 // LoaderFlags
00000144  10 00 00 00                 // NumberOfRvaAndSizes
00000148  00 00 00 00 00 00 00 00     // IMAGE_DATA_DIRECTORY
00000150  10 20 00 00 28 00 00 00 00 00 00 00 00 00 00 00
00000160  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000170  00 40 00 00 0C 00 00 00 00 00 00 00 00 00 00 00
00000180  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000190  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000001A0  00 00 00 00 00 00 00 00 00 20 00 00 10 00 00 00
000001B0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000001C0  00 00 00 00 00 00 00 00

t.exeIMAGE_OPTIONAL_HEADER結構從 0xd8 ~ 0x1c7 共 0xF0 bytes,這個值在IMAGE_FILE_HEADER結構的 SizeOfOptionalHeader 域裡已經給出。

在上面的可以看出t.exe映象是 64 位是 PE+ 檔案結構,它的ImageBase是 0x00000001_40000000,t.exe映象的入口點在ImageBase + AddressOfEntryPoint= 0x00000001_40001000,這個資訊可以從上面分析的.text節中所描述的資訊中驗證,IMAGE_OPTIONAL_HEADER結構的 AddressOfEntryPoint 域的值是RVA(Relative Virtual Address)值。

上面的 SectionAlinment 域值為 0x1000 是表示映象被載入到 virtual address 以是 0x1000(4K byte)為單位的倍數,也就是載入在 virtual address 的 4K 邊界上,例如:t.exe映象的.text節被載入到以ImageBasevirtual address 為 0x00000001_40000000)為基址的第 1 個 4K 邊界上(即:0x00000001_40001000 處),.rdata節載入到第 2 個 4K 邊界上(即:0x00000001_40002000 處)。

FileAlinment 域的值為 0x200 表示執行映象從 0x200 為邊界開始載入到 virtual address 上。例如,t.exe映象中 code 位於檔案映象的 0x400 處(0x200 邊界上),因此,t.exe檔案映象 code 從 0x400 處開始載入到 virtual address。

因此:.text節在檔案映象中位於 0x400 開始,在 virtual address 空間中是位於 0x00000001_40001000 開始,它說明映象的 0x400 處被載入到 virtual address 000000001_40001000 位置上

6.3.3 t.exe 的 IMAGE_DATA_DIRECTORY 表格

上面所示:從 0x00000148 到 0x000001C7 是被稱之為IMAGE_DATA_DIRECTORY的結構的表格。這個 IMAGE_DATA_DIRECTORY 結構在WinNT.h中定義如下:

typedef struct _IMAGE_DATA_DIRECTORY {
    DWORD   VirtualAddress;
    DWORD   Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

這個結構十分重要,它用來描述 windows 執行映象中所使用的各種表格的位置和大小。VirtualAddress 域是一個RVA(Relative Virtual Address)值,更明白一點就是:它是一個偏移量(基於 PE 檔案頭),Size 域表示這個表格有多大。

這個陣列有 16 個元素,也就是表示,在執行映象中最多可以使用 16 個表格。在WinNT.h裡定義為:

#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES    16

由於這個IMAGE_DATA_DIRECTORY表格用來描述在映象中所使用到的表格(最多 16 個表格)

實際上這 16 個表格是固定的,對於這些表格 Microsoft 都作了統一的規定,在WinNT.h裡都作了定義:

// Directory Entries

#define IMAGE_DIRECTORY_ENTRY_EXPORT          0   // Export Directory
#define IMAGE_DIRECTORY_ENTRY_IMPORT          1   // Import Directory
#define IMAGE_DIRECTORY_ENTRY_RESOURCE        2   // Resource Directory
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION       3   // Exception Directory
#define IMAGE_DIRECTORY_ENTRY_SECURITY        4   // Security Directory
#define IMAGE_DIRECTORY_ENTRY_BASERELOC       5   // Base Relocation Table
#define IMAGE_DIRECTORY_ENTRY_DEBUG           6   // Debug Directory
//      IMAGE_DIRECTORY_ENTRY_COPYRIGHT       7   // (X86 usage)
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE    7   // Architecture Specific Data
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR       8   // RVA of GP
#define IMAGE_DIRECTORY_ENTRY_TLS             9   // TLS Directory
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG    10   // Load Configuration Directory
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT   11   // Bound Import Directory in headers
#define IMAGE_DIRECTORY_ENTRY_IAT            12   // Import Address Table
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT   13   // Delay Load Import Descriptors
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14   // COM Runtime descriptor

下面,我將這些表格歸納如下:

表項 表格
0 export table
1 import table
2 resource table
3 exception table
4 certificate table
5 base relocation table
6 debug
7 architecute
8 global pointer
9 TLS table
10 load configuration table
11 bound import
12 import address table
13 delay import descriptor
14 CLR runtime header
15 reserved, must bo zero

那麼,下面我們來看一看 t.exe 映象中使用了哪些表?

00000148  00 00 00 00                   // IMAGE_DATA_DIRECTORY[0]
0000014A  00 00 00 00
00000150  10 20 00 00                   // IMAGE_DATA_DIRECTORY[1]
00000154  28 00 00 00

00000158  00 00 00 00                  // IMAGE_DATA_DIRECTORY[2]
0000015A  00 00 00 00
00000160  00 00 00 00                   // IMAGE_DATA_DIRECTORY[3]
00000164  00 00 00 00
00000168  00 00 00 00                   // IMAGE_DATA_DIRECTORY[4]
0000016A  00 00 00 00
00000170  00 40 00 00                   // IMAGE_DATA_DIRECTORY[5]
00000174  0C 00 00 00

00000178  00 00 00 00                   // IMAGE_DATA_DIRECTORY[6]
0000017A  00 00 00 00
00000180  00 00 00 00                   // IMAGE_DATA_DIRECTORY[7]
00000184  00 00 00 00
00000188  00 00 00 00                   // IMAGE_DATA_DIRECTORY[8]
0000018A  00 00 00 00
00000190  00 00 00 00                   // IMAGE_DATA_DIRECTORY[9]
00000194  00 00 00 00
00000198  00 00 00 00                   // IMAGE_DATA_DIRECTORY[10]
0000019A  00 00 00 00
000001A0  00 00 00 00                   // IMAGE_DATA_DIRECTORY[11]
000001A4  00 00 00 00
000001A8  00 20 00 00                   // IMAGE_DATA_DIRECTORY[12]
000001AA  10 00 00 00

000001B0  00 00 00 00                   // IMAGE_DATA_DIRECTORY[13]
000001B4  00 00 00 00
000001B8  00 00 00 00                   // IMAGE_DATA_DIRECTORY[14]
000001BA  00 00 00 00
000001C0  00 00 00 00                   // IMAGE_DATA_DIRECTORY[15]
000001C4  00 00 00 00                               

我們的示例程式 t.exe 僅僅使用了 3 個 driectory 表格:import tablerelocation table以及import address table

import table 的 RVA 是 0x00002010,size 是 0x28,relocation table 的 RVA 是 0x4000,size 是 0x0c, import address table 的 RVA 是 0x2000,size 是 0x10

t.exeImageBase是 0x00000001_40000000,那麼import table則在 0x00000001_40002010,relocation table則在 0x00000001_40004000,import address table則在 0x00000001_40002000。

6.3.3.1 t.exe 所使用的 import table

上面所述,t.exeimport talbe在 0x00000001_40002010(virtual address),這個import table位於.rdata節中,從上面的.rdata節介紹中看出,.rdata節位於檔案映象中的 0x600 ~ 0x7ff 中共 512 bytes。

import table 在 WinNT.h 中定義為一個 IMAGE_IMPORT_DESCRIPTOR 結構,如下:

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics;            // 0 for terminating null import descriptor
        DWORD   OriginalFirstThunk;         // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
    } DUMMYUNIONNAME;
    DWORD   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)

    DWORD   ForwarderChain;                 // -1 if no forwarders
    DWORD   Name;
    DWORD   FirstThunk;                     // RVA to IAT (if bound this IAT has actual addresses)
} IMAGE_IMPORT_DESCRIPTOR;

IMAGE_IMPORT_DESCRIPTOR結構 5 個域,共 20 bytes,OriginalFirstThunk域指向一個IMAGE_THUNK_DATA結構,IMAGE_THUNK_DATA實際上只有一個域AddressOfDataAddressOfData指向一個IMAGE_IMPORT_BY_NANE結構,它們在 WinNT.h 中的定義為:

//
// Import Format
//

typedef struct _IMAGE_IMPORT_BY_NAME {
    WORD    Hint;
    BYTE    Name[1];
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

#include "pshpack8.h"                       // Use align 8 for the 64-bit IAT.

typedef struct _IMAGE_THUNK_DATA64 {
    union {
        ULONGLONG ForwarderString;  // PBYTE
        ULONGLONG Function;         // PDWORD
        ULONGLONG Ordinal;
        ULONGLONG AddressOfData;    // PIMAGE_IMPORT_BY_NAME
    } u1;
} IMAGE_THUNK_DATA64;
typedef IMAGE_THUNK_DATA64 * PIMAGE_THUNK_DATA64;

IMAGE_IMPORT_BY_NAME是最終的 import table 結構,是原始的 Thunk 表格,這個 Thunk 表格是一個包含所有匯入 function 的列表,這個Thunk table包括了 Hint, function name 和 DLL name。 Hint 代表每個函式的標識,是一個 16 位的數值,Hint 下面接著是 import 的函式名,最後是所匯入的 DLL name。

IMAGE_IMPORT_DESCRIPTOR裡的FirstThunk指向出 Import Address Table (IAT)表格,這個 IAT 大小為 16 bytes ,前 4 bytes 是一個 RVA 指向上面所說的 Thunk table。

          import table

     +----------------------+                       IMAGE_THUNK_DATA
 (0) | OriginalFirstThunk   | ----------------> +--------------------+
     +----------------------+                   |   AddressOfData    | ------\        
     | TimeDataStamp        |                   +--------------------+       |
     +----------------------+            +----> |   AddressOfData    | ---+  | 
     | ForwarderChain       |            |      +--------------------+    |  |
     +----------------------+            |                                |  |
     | Name                 |            |                                |  |
     +----------------------+            |                                |  |
     | FirstThunk           | ----\      |                                |  |
     +----------------------+     |      |                                |  |
 (1) | OriginalFirstThunk   | ----+------+                                |  |       Thunk Table (IMAGE_IMPORT_BY_NAME)
     +----------------------+     |                                       |  |
     | TimeDataStramp       |     |                                       |  +--->  +-------+-----------------+--------------+
     +----------------------+     |                                     +-|------>  | Hint  | function name   | DLL name     |
     | ForwarderChain       |     |                                     | +------>  +-------+-----------------+--------------+
     +--------------------- +     |                                     |  +----->  | Hint  | function name   | DLL name     |
     | Name                 |     |      IAT (Import Address Table)     |  |        +-------+-----------------+--------------+
     +----------------------+     |    +----------------------------+   |  |        | Hint  | function name   | DLL name     |
     | FirstThunk           |--+  +--> |     Thunk table            | --+  |        +-------+-----------------+--------------+
     +----------------------+  |       +----------------------------+      |        | Hint  | function name   | DLL name     |
                               +-----> |     Thunk table            | -----+        +-------+-----------------+--------------+
                                       +----------------------------+

上面是一張關係圖表,上面所示,在映象和記憶體中,存在 4 張表格:

● import table(IMAGE_IMPORT_DESCRIPTOR)
● Thunk table pointer-table(IMAGE_THUNK_DATA)
● IAT(Import Address Table)
● 匯入函式的 Thunk Table(IMAGE_IMPORT_BY_NAME)。

下面看看實際中t.exe映象中的這幾個表格是什麼:

RAW DATA #2
  0000000140002000: 48 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00  H ..............
 0000000140002010:38 20 00 00 00 00 00 00 00 00 00 00 56 20 00 00  8 ..........V ..
  0000000140002020: 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00  . ..............
  0000000140002030: 00 00 00 00 00 00 00 00 48 20 00 00 00 00 00 00  ........H ......
  0000000140002040: 00 00 00 00 00 00 00 00 12 02 4D 65 73 73 61 67  ..........Messag
  0000000140002050: 65 42 6F 78 41 00 55 53 45 52 33 32 2E 64 6C 6C  eBoxA.USER32.dll
  0000000140002060: 00 00                                            ..

  Section contains the following imports:

    USER32.dll
             140002000 Import Address Table
             140002038 Import Name Table
                     0 time date stamp
                     0 Index of first forwarder reference

                         212 MessageBoxA

上面已經顯示出了全部資訊,在IMAGE_DATA_DIRECTORY結構裡已給出了 Import table 的 RVA 是 0x00002010,即:t.exe的 import table 在 virtual address 0x00000001_40002010 裡,那麼現在我們來看一看 import table 裡的資料:

0000000140002010: 38 20 00 00           // OriginalFirstThunk(Thunk table pointer
0000000140002014: 00 00 00 00           // TimeDataStamp
0000000140002018: 00 00 00 00           // ForwarderChain
000000014000201A: 56 20 00 00           // Name(import DLL name
0000000140002020: 00 20 00 00           // FirstThunk(Import address table

因此 Thunk table pointer 放在 00000000140002038 裡,匯入的 DLL 名字放在 0000000140002056 裡,Import address table 放在 0x0000000140002000 裡。

6.4 t.exe 的節表

IMAGE_NT_HEADER結構後面緊接著就是section table(節表)結構, 從 0x1c8 ~ 0x267 共 160 bytes。

這個節表結構在WinNT.h中定義為

//
// Section header format.
//

#define IMAGE_SIZEOF_SHORT_NAME              8

typedef struct _IMAGE_SECTION_HEADER {
    BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];
    union {
            DWORD   PhysicalAddress;
            DWORD   VirtualSize;
    } Misc;
    DWORD   VirtualAddress;
    DWORD   SizeOfRawData;
    DWORD   PointerToRawData;
    DWORD   PointerToRelocations;
    DWORD   PointerToLinenumbers;
    WORD    NumberOfRelocations;
    WORD    NumberOfLinenumbers;
    DWORD   Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

#define IMAGE_SIZEOF_SECTION_HEADER          40

映象中包括有多少個節表結構,由IMAGE_NT_HEADER結構中的IMAGE_FILE_HEADER結構中的 NumberOfSections 域指出。在前面所述的t.exe映象裡 NumberOfSections 值是 4 那麼表示將有 4 個 sections 存在於映象中。從前的面 dumpbin 工具輸出可以得出。

IMAGE_SECTION_HEADER結構的第 1 個域 Name,用來標識section table的名字。它的長度固定為 8 bytes(前面定義的巨集),這將意味著,不存在超過 8 bytes 的節表名。接下來使用 VirtualSize 來用表示 section talbe 大小。VirtualAddress 表示 section table 的 RVA

size 描述
Name 8 bytes Section 表名字
VirtualSize DWORD Section 表的大小
VirtualAddress DWORD Section 表的 RVA,即:section 表的位置
SizeOfRawData DWORD section 表佔用映像的大小,這個 size 是以 0x200 為單位的
PointerToRawData DWORD section 表在映像中的物理位置,即是:file 位置,而非 virtual 位置
PointerToRelocation DOWRD
PointerToLinenumber DWORD
NumberOfRelocation WORD
NumberOfLineumbers WORD
Characteristics DWORD section 的屬性 flags,可用於 '|' 多個屬性值

所有的 Characteristics 都在 WinNT.h 中有定義,下面是一些常用的 flags:

#define IMAGE_SCN_CNT_CODE                   0x00000020  // Section contains code.
#define IMAGE_SCN_CNT_INITIALIZED_DATA       0x00000040  // Section contains initialized data.
#define IMAGE_SCN_CNT_UNINITIALIZED_DATA     0x00000080  // Section contains uninitialized data.
... ...
#define IMAGE_SCN_LNK_NRELOC_OVFL            0x01000000  // Section contains extended relocations.
#define IMAGE_SCN_MEM_DISCARDABLE            0x02000000  // Section can be discarded.
#define IMAGE_SCN_MEM_NOT_CACHED             0x04000000  // Section is not cachable.
#define IMAGE_SCN_MEM_NOT_PAGED              0x08000000  // Section is not pageable.
#define IMAGE_SCN_MEM_SHARED                 0x10000000  // Section is shareable.
#define IMAGE_SCN_MEM_EXECUTE                0x20000000  // Section is executable.
#define IMAGE_SCN_MEM_READ                   0x40000000  // Section is readable.
#define IMAGE_SCN_MEM_WRITE                  0x80000000  // Section is writeable.

下面以.text節為例,看看t.exe.text是什麼。

.text節的 Name 是 ".text",VirtualSize 是 0x42 bytes,.text節在 RVA 為 0x00001000 的位置上,section 的分配單位為 0x1000(4K bytes),第 1 個 section 一般都分配在 0x1000 位置上,SizeOfRawData 為 0x200,這是映像檔案分配單位。PointerToRawData 為 0x400,說明.text節在映像檔案的 0x400 處。Characteristics 是 0x60000020,說明.text是 executable/readable/code 屬性。

相關推薦

PE檔案結構解析 C、C++程式 vc2008編譯

//MyPeFile.h------------------------------------------------------------------------------------------------------ typedef unsigned short USHORT;typ

PE檔案結構解析

t.exe共 3072 bytes,下面是t.exe映象 PE 檔案頭的整體結構圖: windows 的 PE 檔案頭結構包括三大部分:DOS 檔案頭、NT 檔案頭以及 Section 表(節表),在 DOS 檔案頭後面有一小段 DOS 程式,被稱為 DOS stub 程

MP3檔案結構解析(超詳細)

1. MP3檔案結構解析 1.1. 概述 1.1.1. 音訊相關術語 ü  ID3: 一般位於一個mp3檔案的開頭或末尾的若干位元組內,記錄該mp3檔案的歌手、標題、專輯名稱、年代、風格等資訊,ID3分位兩個版本,V1版ID3在檔案末尾的固定128位元組,以TAG字元開頭,若沒有則認為

tomcat9目錄檔案結構解析

一、tomcat目錄檔案如下圖: bin: 啟動和關閉tomcat的指令碼檔案 conf: 存放tomcat配置檔案 server.xml : 該檔案用於配置和 server 相關的資訊, 比如 tomcat啟動埠後,配置Host, 配置Conte

MP3解碼流程(一)-----音訊檔案結構解析

本文多處摘自網際網路,僅供本人學習使用,出處標示於文章尾端。 #一、概述     Layer-3 音訊檔案,MPEG(Moving Picture Experts Group) 在漢語中譯為活動影象專家組,特指活動影音壓縮標準,MPEG音訊檔案是MPEG1

windows PE檔案結構及其載入機制

1. 概述 PE檔案的全稱是Portable Executable,意為可移植的可執行的檔案,常見的EXE、DLL、OCX、SYS、COM都是PE檔案,PE檔案是微軟Windows作業系統上的程式檔案(可能是間接被執行,如DLL)。它是1993年Windows

PE檔案結構及其載入機制(一)

一、PE檔案結構 PE即Portable Executable,是win32環境自身所帶的執行體檔案格式,其部分特性繼承自Unix的COFF(Common Object File Format)檔案格式。PE表示該檔案格式是跨win32平臺的,即使Windows執行在非Intel的CPU上,任何Win32平

(2) springframework主配置檔案結構解析

轉自: http://book.51cto.com/art/201004/193743.htm        Spring配置檔案是用於指導Spring工廠進行Bean生產、依賴關係注入(裝配)及Bean例項分發的"圖紙"。Java EE程式設計

PE檔案結構(0x02)

 最     最近包括暑假一直在學習PE檔案的格式結構,對一些病毒感染檔案的方式想要做一些瞭解,學的東西可能很淺顯,在這裡記錄一下想。 PE檔案的第一個位元組起始於 MS-DOS 頭部,被稱作 IMAGE_DOS_HEADER,定義在WINNT.H,其結構如下: t

00 PE檔案結構

一、什麼是PE檔案結構? 可執行檔案指的是由作業系統進行載入執行的檔案,而PE檔案結構就是Windows下可執行檔案的格式,同樣ELF(Executable and Linking Format)檔案結構就是linux下可執行檔案的格式;PE是 Potable Executable的首字母縮

PE檔案詳解------PE檔案結構剖析

一.PE檔案結構縱覽 PE檔案的結構如上圖所示,由低地址到高地址分別為:Dos頭,PE頭,塊表,塊,除錯資訊。其中真正的PE檔案頭是位於Dos頭的後面的部分。 上圖為利用PE工具開啟的一個可執行檔案在磁碟中的對映,這就是PE檔案的內部資訊。 可以看到這個檔案的錢兩個

vue2目錄及檔案結構解析

一、首先建立一個vue專案,如:my-vue,其程式碼結構如下: 由上圖分析每個檔案的作用: 二、我們主要工作src資料夾的分析結構如圖: 這是src資料夾下面的初始情況,裡面有一些示

深入boot.img格式檔案結構解析

以msm8226為例,講解android原始碼編譯生成boot.img的結構。boot.img包括boot.img header、kernel以及ramdisk檔案系統。下面是對boot.img的結構進行解讀。   boot.img 檔案頭資訊的具體結構可以在system/

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

PE檔案結構(一) 參考 書:《加密與解密》 視訊:小甲魚 解密系列 視訊         exe,dll都是PE(Portable Execute)檔案結構。PE檔案使用的是一個平面地址空間,所有程式碼和資料都被合併在一起,組成一個很大的結構。先看2張圖,來大

PE檔案結構詳解(四)PE匯入表

PE檔案結構詳解(二)可執行檔案頭的最後展示了一個數組,PE檔案結構詳解(三)PE匯出表中解釋了其中第一項的格式,本篇文章來揭示這個陣列中的第二項:IMAGE_DIRECTORY_ENTRY_IMPORT,即匯入表。 也許大家注意到過,在IMAGE_DATA_DIRECTO

PE總結7---PE檔案結構NT頭之資料目錄表 IMAGE_DATA_DIRECTORY

     IMAGE_DATA_DIRCTORY結構如下: typedef struct _IMAGE_DATA_DIRECTORY { DWORD VirtualAddress; //相對虛擬地址 DWORD Size;      //大小 } IMAGE_DATA_

PE檔案結構(五)基址重定位

PE檔案結構(五) 參考 書:《加密與解密》 視訊:小甲魚 解密系列 視訊 基址重定位         連結器生成一個PE檔案時,它會假設程式被裝入時使用的預設ImageBase基地址(VC預設exe基地址00400000h,dll基地址10000000h)

PE總結3---PE檔案結構DOS檔案頭

PE檔案結構DOS檔案頭,會使用到IMAGE_DOS_HEADER結構體,如下 typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header WORD e_magic;

C++PE檔案格式解析類(輕鬆製作自己的PE檔案解析器)

PE是Portable Executable File Format(可移植的執行體)簡寫,它是目前Windows平臺上的主流可執行檔案格式。 PE檔案中包含的內容很多,具體我就不在這解釋了,有興趣的可以參看之後列出的參考資料及其他相關內容。 最近我也在學習PE檔案格式,參

opencv2.x檔案結構解析

1 OpenCV簡介         OpenCV(Open Source Computer Vision)是一個用於實時處理的計算機視覺函式庫,它基於BSD許可證授權並且可免費用於學術研究和商業應用。它擁有C/C++、Python、和Java(僅用於Android)介