PE文件初步

PEPortable Executable简写,PE文件是32/64位Windows上的主流可执行文件,主要有EXE和DLL文件。 C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Include\WinNT.h定义了PE文件的数据结构, PE文件的总体框架如下:

DOS头

每个PE文件以一个DOS程序开始,如果在DOS下运行,DOS就能识别出这是一个有效的程序,然后运行DOS stub,在不支持PE文件的操作系统下一般会显示“This program cannot be run in DOS mode”。 PE文件以一个IMAGE_DOS_HEADERMZ头)开始,IMAGE_DOS_HEADER数据结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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 MZ头后是DOS stub,DOS下会根据识别出来的MZ头运行stub中的代码。 以一个简单的“Hellor, world”为例,

1
2
3
4
5
6
7
#include <iostream>

int main()
{
std::cout << "Hello, world.\n";
return 0;
}

用g++编译后,以十六进制打开:

1
2
3
4
5
6
7
8
9
00000000h: 4D 5A 90 00 03 00 00 00 04 00 00 00 FF FF 00 00 ; MZ?..........
00000010h: B8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 ; ?......@.......
00000020h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
00000030h: 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 00 ; ............€...
00000040h: 0E 1F BA 0E 00 B4 09 CD 21 B8 01 4C CD 21 54 68 ; ..?.???L?Th
00000050h: 69 73 20 70 72 6F 67 72 61 6D 20 63 61 6E 6E 6F ; is program canno
00000060h: 74 20 62 65 20 72 75 6E 20 69 6E 20 44 4F 53 20 ; t be run in DOS
00000070h: 6D 6F 64 65 2E 0D 0D 0A 24 00 00 00 00 00 00 00 ; mode....$.......
00000080h: 50 45 00 00 4C 01 08 00 F4 D1 D7 57 00 B4 00 00 ; PE..L...粞譝.?.

e_magic4D 5A,对应字符MZ#define如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#ifndef _MAC

#include "pshpack4.h" // 4 byte packing is the default

#define IMAGE_DOS_SIGNATURE 0x5A4D // MZ
#define IMAGE_OS2_SIGNATURE 0x454E // NE
#define IMAGE_OS2_SIGNATURE_LE 0x454C // LE
#define IMAGE_VXD_SIGNATURE 0x454C // LE
#define IMAGE_NT_SIGNATURE 0x00004550 // PE00

#include "pshpack2.h" // 16 bit headers are 2 byte packed

#else

#include "pshpack1.h"

#define IMAGE_DOS_SIGNATURE 0x4D5A // MZ
#define IMAGE_OS2_SIGNATURE 0x4E45 // NE
#define IMAGE_OS2_SIGNATURE_LE 0x4C45 // LE
#define IMAGE_NT_SIGNATURE 0x50450000 // PE00
#endif

在文件头偏移3C处,e_lfanew指出了PE文件头的位置,值为00 00 00 80,可以看到在80偏移处,对应字符PE

用IDA分析文件可以看到其代码如下:

程序调用了int 21hfunction为9,作用是将地址为DS:DX处的字符串写到标准输出,在0E偏移处就是以$结尾的字符串“This program cannot be run in DOS mode”。然后执行

1
2
mov     ax, 4C01h
int 21h

终止程序。

用LordPE可以看到DOS头所有内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
->DOS Header
e_magic: 0x5A4D
e_cblp: 0x0090
e_cp: 0x0003
e_crlc: 0x0000
e_cparhdr: 0x0004
e_minalloc: 0x0000
e_maxalloc: 0xFFFF
e_ss: 0x0000
e_sp: 0x00B8
e_csum: 0x0000
e_ip: 0x0000
e_cs: 0x0000
e_lfarlc: 0x0040
e_ovno: 0x0000
e_res: 0x0000000000000000
e_oemid: 0x0000
e_oeminfo: 0x0000
e_res2: 0x0000000000000000000000000000000000000000
e_lfanew: 0x00000080

PE头

PE头的数据结构如下:

1
2
3
4
5
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
00000080h: 50 45 00 00 4C 01 08 00 F4 D1 D7 57 00 B4 00 00 ; PE..L...粞譝.?.
00000090h: 2D 02 00 00 E0 00 07 03 0B 01 02 19 00 7E 00 00 ; -...?.......~..
000000a0h: 00 B0 00 00 00 0C 00 00 C0 12 00 00 00 10 00 00 ; .?.....?......
000000b0h: 00 90 00 00 00 00 40 00 00 10 00 00 00 02 00 00 ; .?...@.........
000000c0h: 04 00 00 00 01 00 00 00 04 00 00 00 00 00 00 00 ; ................
000000d0h: 00 10 01 00 00 04 00 00 30 64 01 00 03 00 00 00 ; ........0d......
000000e0h: 00 00 20 00 00 10 00 00 00 00 10 00 00 10 00 00 ; .. .............
000000f0h: 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 ; ................
00000100h: 00 E0 00 00 34 08 00 00 00 00 00 00 00 00 00 00 ; .?.4...........
00000110h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
00000120h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
00000130h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
00000140h: 04 00 01 00 18 00 00 00 00 00 00 00 00 00 00 00 ; ................
00000150h: 00 00 00 00 00 00 00 00 9C E1 00 00 24 01 00 00 ; ........溼..$...
00000160h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
00000170h: 00 00 00 00 00 00 00 00 2E 74 65 78 74 00 00 00 ; .........text...

Signature

A 4-byte signature identifying the file as a PE image,#define IMAGE_NT_SIGNATURE 0x00004550 // PE00给出了定义,与IMAGE_DOS_SIGNATURE的定义在同一处。

FileHeader

1
2
3
4
5
6
7
8
9
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;
  • Machine The architecture type of the computer. An image file can only be run on the specified computer or a system that emulates the specified computer。常见标志如下:
Value Meaning
0x014c Intel 386
0x0200 Intel 64
0x8664 AMD64 (K8)

这里为01 4C。更多的在WinNT.h中有定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#define IMAGE_FILE_MACHINE_UNKNOWN           0
#define IMAGE_FILE_MACHINE_I386 0x014c // Intel 386.
#define IMAGE_FILE_MACHINE_R3000 0x0162 // MIPS little-endian, 0x160 big-endian
#define IMAGE_FILE_MACHINE_R4000 0x0166 // MIPS little-endian
#define IMAGE_FILE_MACHINE_R10000 0x0168 // MIPS little-endian
#define IMAGE_FILE_MACHINE_WCEMIPSV2 0x0169 // MIPS little-endian WCE v2
#define IMAGE_FILE_MACHINE_ALPHA 0x0184 // Alpha_AXP
#define IMAGE_FILE_MACHINE_SH3 0x01a2 // SH3 little-endian
#define IMAGE_FILE_MACHINE_SH3DSP 0x01a3
#define IMAGE_FILE_MACHINE_SH3E 0x01a4 // SH3E little-endian
#define IMAGE_FILE_MACHINE_SH4 0x01a6 // SH4 little-endian
#define IMAGE_FILE_MACHINE_SH5 0x01a8 // SH5
#define IMAGE_FILE_MACHINE_ARM 0x01c0 // ARM Little-Endian
#define IMAGE_FILE_MACHINE_THUMB 0x01c2
#define IMAGE_FILE_MACHINE_AM33 0x01d3
#define IMAGE_FILE_MACHINE_POWERPC 0x01F0 // IBM PowerPC Little-Endian
#define IMAGE_FILE_MACHINE_POWERPCFP 0x01f1
#define IMAGE_FILE_MACHINE_IA64 0x0200 // Intel 64
#define IMAGE_FILE_MACHINE_MIPS16 0x0266 // MIPS
#define IMAGE_FILE_MACHINE_ALPHA64 0x0284 // ALPHA64
#define IMAGE_FILE_MACHINE_MIPSFPU 0x0366 // MIPS
#define IMAGE_FILE_MACHINE_MIPSFPU16 0x0466 // MIPS
#define IMAGE_FILE_MACHINE_AXP64 IMAGE_FILE_MACHINE_ALPHA64
#define IMAGE_FILE_MACHINE_TRICORE 0x0520 // Infineon
#define IMAGE_FILE_MACHINE_CEF 0x0CEF
#define IMAGE_FILE_MACHINE_EBC 0x0EBC // EFI Byte Code
#define IMAGE_FILE_MACHINE_AMD64 0x8664 // AMD64 (K8)
#define IMAGE_FILE_MACHINE_M32R 0x9041 // M32R little-endian
#define IMAGE_FILE_MACHINE_CEE 0xC0EE
  • NumberOfSections The number of sections. This indicates the size of the section table, which immediately follows the headers. Note that the Windows loader limits the number of sections to 96.

  • TimeDateStamp The low 32 bits of the time stamp of the image. This represents the date and time the image was created by the linker. The value is represented in the number of seconds elapsed since midnight (00:00:00), January 1, 1970, Universal Coordinated Time, according to the system clock.

  • PointerToSymbolTable The offset of the symbol table, in bytes, or zero if no COFF symbol table exists.

  • NumberOfSymbols The number of symbols in the symbol table.

  • SizeOfOptionalHeader The size of the optional header, in bytes. This value should be 0 for object files.

  • Characteristics 文件的属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#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.

FileHeader所有内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
->File Header
Machine: 0x014C (I386)
NumberOfSections: 0x0008
TimeDateStamp: 0x57D7D1F4 (GMT: Tue Sep 13 10:16:20 2016)
PointerToSymbolTable: 0x0000B400
NumberOfSymbols: 0x0000022D
SizeOfOptionalHeader: 0x00E0
Characteristics: 0x0307
(RELOCS_STRIPPED)
(EXECUTABLE_IMAGE)
(LINE_NUMS_STRIPPED)
(32BIT_MACHINE)
(DEBUG_STRIPPED)

OptionalHeader

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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;
  • Magic The state of the image file.
1
2
3
#define IMAGE_NT_OPTIONAL_HDR32_MAGIC      0x10b
#define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20b
#define IMAGE_ROM_OPTIONAL_HDR_MAGIC 0x107
  • MajorLinkerVersion The major version number of the linker.

  • MinorLinkerVersion The minor version number of the linker.

  • SizeOfCode The size of the code section, in bytes, or the sum of all such sections if there are multiple code sections.

  • SizeOfInitializedData The size of the initialized data section, in bytes, or the sum of all such sections if there are multiple initialized data sections.

  • SizeOfUninitializedData The size of the uninitialized data section, in bytes, or the sum of all such sections if there are multiple uninitialized data sections.

  • AddressOfEntryPoint A pointer to the entry point function, relative to the image base address. For executable files, this is the starting address. For device drivers, this is the address of the initialization function. The entry point function is optional for DLLs. When no entry point is present, this member is zero.

  • BaseOfCode A pointer to the beginning of the code section, relative to the image base.

  • BaseOfData A pointer to the beginning of the data section, relative to the image base.

  • ImageBase The preferred address of the first byte of the image when it is loaded in memory. This value is a multiple of 64K bytes. The default value for DLLs is 0x10000000. The default value for applications is 0x00400000, except on Windows CE where it is 0x00010000.

  • SectionAlignment The alignment of sections loaded in memory, in bytes. This value must be greater than or equal to the FileAlignment member. The default value is the page size for the system.

  • FileAlignment The alignment of the raw data of sections in the image file, in bytes. The value should be a power of 2 between 512 and 64K (inclusive). The default is 512. If the SectionAlignment member is less than the system page size, this member must be the same as SectionAlignment.

  • MajorOperatingSystemVersion The major version number of the required operating system.

  • MinorOperatingSystemVersion The minor version number of the required operating system.

  • MajorImageVersion The major version number of the image.

  • MinorImageVersion The minor version number of the image.

  • MajorSubsystemVersion The major version number of the subsystem.

  • MinorSubsystemVersion The minor version number of the subsystem.

  • Win32VersionValue This member is reserved and must be 0.

  • SizeOfImage The size of the image, in bytes, including all headers. Must be a multiple of SectionAlignment.

  • SizeOfHeaders The combined size of the following items, rounded to a multiple of the value specified in the FileAlignment member.
    • e_lfanew member of IMAGE_DOS_HEADER
    • 4 byte signature
    • size of IMAGE_FILE_HEADER
    • size of optional header
    • size of all section headers
  • CheckSum The image file checksum. The following files are validated at load time: all drivers, any DLL loaded at boot time, and any DLL loaded into a critical system process.

  • Subsystem The subsystem required to run this image.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Subsystem Values

#define IMAGE_SUBSYSTEM_UNKNOWN 0 // Unknown subsystem.
#define IMAGE_SUBSYSTEM_NATIVE 1 // Image doesn't require a subsystem.
#define IMAGE_SUBSYSTEM_WINDOWS_GUI 2 // Image runs in the Windows GUI subsystem.
#define IMAGE_SUBSYSTEM_WINDOWS_CUI 3 // Image runs in the Windows character subsystem.
#define IMAGE_SUBSYSTEM_OS2_CUI 5 // image runs in the OS/2 character subsystem.
#define IMAGE_SUBSYSTEM_POSIX_CUI 7 // image runs in the Posix character subsystem.
#define IMAGE_SUBSYSTEM_NATIVE_WINDOWS 8 // image is a native Win9x driver.
#define IMAGE_SUBSYSTEM_WINDOWS_CE_GUI 9 // Image runs in the Windows CE subsystem.
#define IMAGE_SUBSYSTEM_EFI_APPLICATION 10 // Extensible Firmware Interface (EFI) application.
#define IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER 11 // EFI driver with boot services.
#define IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER 12 // EFI driver with run-time services.
#define IMAGE_SUBSYSTEM_EFI_ROM 13 // EFI ROM image.
#define IMAGE_SUBSYSTEM_XBOX 14 // Xbox system.
#define IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION 16 // Boot application.
  • DllCharacteristics The DLL characteristics of the image.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// DllCharacteristics Entries

// IMAGE_LIBRARY_PROCESS_INIT 0x0001 // Reserved.
// IMAGE_LIBRARY_PROCESS_TERM 0x0002 // Reserved.
// IMAGE_LIBRARY_THREAD_INIT 0x0004 // Reserved.
// IMAGE_LIBRARY_THREAD_TERM 0x0008 // Reserved.
#define IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE 0x0040 // DLL can move.
#define IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY 0x0080 // Code Integrity Image
#define IMAGE_DLLCHARACTERISTICS_NX_COMPAT 0x0100 // Image is NX compatible
#define IMAGE_DLLCHARACTERISTICS_NO_ISOLATION 0x0200 // Image understands isolation and doesn't want it
#define IMAGE_DLLCHARACTERISTICS_NO_SEH 0x0400 // Image does not use SEH. No SE handler may reside in this image
#define IMAGE_DLLCHARACTERISTICS_NO_BIND 0x0800 // Do not bind this image.
// 0x1000 // Reserved.
#define IMAGE_DLLCHARACTERISTICS_WDM_DRIVER 0x2000 // Driver uses WDM model
// 0x4000 // Reserved.
#define IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE 0x8000 // The image is terminal server aware.
  • SizeOfStackReserve The number of bytes to reserve for the stack. Only the memory specified by the SizeOfStackCommit member is committed at load time; the rest is made available one page at a time until this reserve size is reached.

  • SizeOfStackCommit The number of bytes to commit for the stack.

  • SizeOfHeapReserve The number of bytes to reserve for the local heap. Only the memory specified by the SizeOfHeapCommit member is committed at load time; the rest is made available one page at a time until this reserve size is reached.

  • SizeOfHeapCommit The number of bytes to commit for the local heap.

  • LoaderFlags This member is obsolete.

  • NumberOfRvaAndSizes The number of directory entries in the remainder of the optional header. Each entry describes a location and size.

  • DataDirectory A pointer to the first IMAGE_DATA_DIRECTORY structure in the data directory.

OptionalHeader的字段如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
->Optional Header
Magic: 0x010B (HDR32_MAGIC)
MajorLinkerVersion: 0x02
MinorLinkerVersion: 0x19 -> 2.25
SizeOfCode: 0x00007E00
SizeOfInitializedData: 0x0000B000
SizeOfUninitializedData: 0x00000C00
AddressOfEntryPoint: 0x000012C0
BaseOfCode: 0x00001000
BaseOfData: 0x00009000
ImageBase: 0x00400000
SectionAlignment: 0x00001000
FileAlignment: 0x00000200
MajorOperatingSystemVersion: 0x0004
MinorOperatingSystemVersion: 0x0000 -> 4.00
MajorImageVersion: 0x0001
MinorImageVersion: 0x0000 -> 1.00
MajorSubsystemVersion: 0x0004
MinorSubsystemVersion: 0x0000 -> 4.00
Win32VersionValue: 0x00000000
SizeOfImage: 0x00011000
SizeOfHeaders: 0x00000400
CheckSum: 0x00016430
Subsystem: 0x0003 (WINDOWS_CUI)
DllCharacteristics: 0x0000
SizeOfStackReserve: 0x00200000
SizeOfStackCommit: 0x00001000
SizeOfHeapReserve: 0x00100000
SizeOfHeapCommit: 0x00001000
LoaderFlags: 0x00000000
NumberOfRvaAndSizes: 0x00000010

DataDirectory

相关定义如下:

1
2
3
4
5
6
7
8
9
10
//
// Directory format.
//

typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress; // The relative virtual address of the table.
DWORD Size; // The size of the table, in bytes.
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16

数据目录列表如下,Offsets是相对于OptionalHeader的偏移:

Offset(PE/PE32+) Description
60h/70h Export table address and size
68h/78h Import table address and size
70h/80h Resource table address and size
78h/88h Exception table address and size
80h/90h Certificate table address and size
88h/98h Base relocation table address and size
90h/A0h Debugging information starting address and size
98h/A8h Architecture-specific data address and size
A0h/B0h Global pointer register relative virtual address
A8h/B8h Thread local storage (TLS) table address and size
B0h/C0h Load configuration table address and size
B8h/C8h Bound import table address and size
C0h/D0h Import address table address and size
C8h/D8h Delay import descriptor address and size
D0h/E0h The CLR header address and size
D8h/E8h Reserved

各字段如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
DataDirectory (16)            RVA        Size
------------- ---------- ----------
ExportTable 0x00000000 0x00000000
ImportTable 0x0000E000 0x00000834 (".idata")
Resource 0x00000000 0x00000000
Exception 0x00000000 0x00000000
Security 0x00000000 0x00000000
Relocation 0x00000000 0x00000000
Debug 0x00000000 0x00000000
Copyright 0x00000000 0x00000000
GlobalPtr 0x00000000 0x00000000
TLSTable 0x00010004 0x00000018 (".tls")
LoadConfig 0x00000000 0x00000000
BoundImport 0x00000000 0x00000000
IAT 0x0000E19C 0x00000124 (".idata")
DelayImport 0x00000000 0x00000000
COM 0x00000000 0x00000000
Reserved 0x00000000 0x00000000

区块表(Section Table)

PE头之后就是区块表(Section Table)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
00000170h: 00 00 00 00 00 00 00 00 2E 74 65 78 74 00 00 00 ; .........text...
00000180h: B8 7D 00 00 00 10 00 00 00 7E 00 00 00 04 00 00 ; 竲.......~......
00000190h: 00 00 00 00 00 00 00 00 00 00 00 00 60 00 50 60 ; ............`.P`
000001a0h: 2E 64 61 74 61 00 00 00 2C 00 00 00 00 90 00 00 ; .data...,....?.
000001b0h: 00 02 00 00 00 82 00 00 00 00 00 00 00 00 00 00 ; .....?.........
000001c0h: 00 00 00 00 40 00 30 C0 2E 72 64 61 74 61 00 00 ; ....@.0?rdata..
000001d0h: 14 08 00 00 00 A0 00 00 00 0A 00 00 00 84 00 00 ; .....?......?.
000001e0h: 00 00 00 00 00 00 00 00 00 00 00 00 40 00 60 40 ; ............@.`@
000001f0h: 2E 65 68 5F 66 72 61 6D 64 16 00 00 00 B0 00 00 ; .eh_framd....?.
00000200h: 00 18 00 00 00 8E 00 00 00 00 00 00 00 00 00 00 ; .....?.........
00000210h: 00 00 00 00 40 00 30 40 2E 62 73 73 00 00 00 00 ; ....@.0@.bss....
00000220h: 34 0A 00 00 00 D0 00 00 00 00 00 00 00 00 00 00 ; 4....?.........
00000230h: 00 00 00 00 00 00 00 00 00 00 00 00 80 00 60 C0 ; ............€.`?
00000240h: 2E 69 64 61 74 61 00 00 34 08 00 00 00 E0 00 00 ; .idata..4....?.
00000250h: 00 0A 00 00 00 A6 00 00 00 00 00 00 00 00 00 00 ; .....?.........
00000260h: 00 00 00 00 40 00 30 C0 2E 43 52 54 00 00 00 00 ; ....@.0?CRT....
00000270h: 18 00 00 00 00 F0 00 00 00 02 00 00 00 B0 00 00 ; .....?......?.
00000280h: 00 00 00 00 00 00 00 00 00 00 00 00 40 00 30 C0 ; ............@.0?
00000290h: 2E 74 6C 73 00 00 00 00 20 00 00 00 00 00 01 00 ; .tls.... .......
000002a0h: 00 02 00 00 00 B2 00 00 00 00 00 00 00 00 00 00 ; .....?.........
000002b0h: 00 00 00 00 40 00 30 C0 00 00 00 00 00 00 00 00 ; ....@.0?.......
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//
// 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;
  • Name An 8-byte, null-padded UTF-8 string. There is no terminating null character if the string is exactly eight characters long. For longer names, this member contains a forward slash (/) followed by an ASCII representation of a decimal number that is an offset into the string table. Executable images do not use a string table and do not support section names longer than eight characters.

  • Misc
    • PhysicalAddress The file address.
    • VirtualSize The total size of the section when loaded into memory, in bytes. If this value is greater than the SizeOfRawData member, the section is filled with zeroes. This field is valid only for executable images and should be set to 0 for object files.
  • VirtualAddress The address of the first byte of the section when loaded into memory, relative to the image base. For object files, this is the address of the first byte before relocation is applied.

  • SizeOfRawData The size of the initialized data on disk, in bytes. This value must be a multiple of the FileAlignment member of the IMAGE_OPTIONAL_HEADER structure. If this value is less than the VirtualSize member, the remainder of the section is filled with zeroes. If the section contains only uninitialized data, the member is zero.

  • PointerToRawData A file pointer to the first page within the COFF file. This value must be a multiple of the FileAlignment member of the IMAGE_OPTIONAL_HEADER structure. If a section contains only uninitialized data, set this member is zero.

  • PointerToRelocations A file pointer to the beginning of the relocation entries for the section. If there are no relocations, this value is zero.

  • PointerToLinenumbers A file pointer to the beginning of the line-number entries for the section. If there are no COFF line numbers, this value is zero.

  • NumberOfRelocations The number of relocation entries for the section. This value is zero for executable images.

  • NumberOfLinenumbers The number of line-number entries for the section.

  • Characteristics The characteristics of the image.

各种标志如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
//
// Section characteristics.
//
// IMAGE_SCN_TYPE_REG 0x00000000 // Reserved.
// IMAGE_SCN_TYPE_DSECT 0x00000001 // Reserved.
// IMAGE_SCN_TYPE_NOLOAD 0x00000002 // Reserved.
// IMAGE_SCN_TYPE_GROUP 0x00000004 // Reserved.
#define IMAGE_SCN_TYPE_NO_PAD 0x00000008 // Reserved.
// IMAGE_SCN_TYPE_COPY 0x00000010 // Reserved.

#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_OTHER 0x00000100 // Reserved.
#define IMAGE_SCN_LNK_INFO 0x00000200 // Section contains comments or some other type of information.
// IMAGE_SCN_TYPE_OVER 0x00000400 // Reserved.
#define IMAGE_SCN_LNK_REMOVE 0x00000800 // Section contents will not become part of image.
#define IMAGE_SCN_LNK_COMDAT 0x00001000 // Section contents comdat.
// 0x00002000 // Reserved.
// IMAGE_SCN_MEM_PROTECTED - Obsolete 0x00004000
#define IMAGE_SCN_NO_DEFER_SPEC_EXC 0x00004000 // Reset speculative exceptions handling bits in the TLB entries for this section.
#define IMAGE_SCN_GPREL 0x00008000 // Section content can be accessed relative to GP
#define IMAGE_SCN_MEM_FARDATA 0x00008000
// IMAGE_SCN_MEM_SYSHEAP - Obsolete 0x00010000
#define IMAGE_SCN_MEM_PURGEABLE 0x00020000
#define IMAGE_SCN_MEM_16BIT 0x00020000
#define IMAGE_SCN_MEM_LOCKED 0x00040000
#define IMAGE_SCN_MEM_PRELOAD 0x00080000

#define IMAGE_SCN_ALIGN_1BYTES 0x00100000 //
#define IMAGE_SCN_ALIGN_2BYTES 0x00200000 //
#define IMAGE_SCN_ALIGN_4BYTES 0x00300000 //
#define IMAGE_SCN_ALIGN_8BYTES 0x00400000 //
#define IMAGE_SCN_ALIGN_16BYTES 0x00500000 // Default alignment if no others are specified.
#define IMAGE_SCN_ALIGN_32BYTES 0x00600000 //
#define IMAGE_SCN_ALIGN_64BYTES 0x00700000 //
#define IMAGE_SCN_ALIGN_128BYTES 0x00800000 //
#define IMAGE_SCN_ALIGN_256BYTES 0x00900000 //
#define IMAGE_SCN_ALIGN_512BYTES 0x00A00000 //
#define IMAGE_SCN_ALIGN_1024BYTES 0x00B00000 //
#define IMAGE_SCN_ALIGN_2048BYTES 0x00C00000 //
#define IMAGE_SCN_ALIGN_4096BYTES 0x00D00000 //
#define IMAGE_SCN_ALIGN_8192BYTES 0x00E00000 //
// Unused 0x00F00000
#define IMAGE_SCN_ALIGN_MASK 0x00F00000

#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.

Section Table内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
->Section Header Table
1. item:
Name: .text
VirtualSize: 0x00007DB8
VirtualAddress: 0x00001000
SizeOfRawData: 0x00007E00
PointerToRawData: 0x00000400
PointerToRelocations: 0x00000000
PointerToLinenumbers: 0x00000000
NumberOfRelocations: 0x0000
NumberOfLinenumbers: 0x0000
Characteristics: 0x60500060
(CODE, INITIALIZED_DATA, EXECUTE, READ, ALIGN_16BYTES)

2. item:
Name: .data
VirtualSize: 0x0000002C
VirtualAddress: 0x00009000
SizeOfRawData: 0x00000200
PointerToRawData: 0x00008200
PointerToRelocations: 0x00000000
PointerToLinenumbers: 0x00000000
NumberOfRelocations: 0x0000
NumberOfLinenumbers: 0x0000
Characteristics: 0xC0300040
(INITIALIZED_DATA, READ, WRITE, ALIGN_4BYTES)

3. item:
Name: .rdata
VirtualSize: 0x00000814
VirtualAddress: 0x0000A000
SizeOfRawData: 0x00000A00
PointerToRawData: 0x00008400
PointerToRelocations: 0x00000000
PointerToLinenumbers: 0x00000000
NumberOfRelocations: 0x0000
NumberOfLinenumbers: 0x0000
Characteristics: 0x40600040
(INITIALIZED_DATA, READ, ALIGN_32BYTES)

4. item:
Name: .eh_fram
VirtualSize: 0x00001664
VirtualAddress: 0x0000B000
SizeOfRawData: 0x00001800
PointerToRawData: 0x00008E00
PointerToRelocations: 0x00000000
PointerToLinenumbers: 0x00000000
NumberOfRelocations: 0x0000
NumberOfLinenumbers: 0x0000
Characteristics: 0x40300040
(INITIALIZED_DATA, READ, ALIGN_4BYTES)

5. item:
Name: .bss
VirtualSize: 0x00000A34
VirtualAddress: 0x0000D000
SizeOfRawData: 0x00000000
PointerToRawData: 0x00000000
PointerToRelocations: 0x00000000
PointerToLinenumbers: 0x00000000
NumberOfRelocations: 0x0000
NumberOfLinenumbers: 0x0000
Characteristics: 0xC0600080
(UNINITIALIZED_DATA, READ, WRITE, ALIGN_32BYTES)

6. item:
Name: .idata
VirtualSize: 0x00000834
VirtualAddress: 0x0000E000
SizeOfRawData: 0x00000A00
PointerToRawData: 0x0000A600
PointerToRelocations: 0x00000000
PointerToLinenumbers: 0x00000000
NumberOfRelocations: 0x0000
NumberOfLinenumbers: 0x0000
Characteristics: 0xC0300040
(INITIALIZED_DATA, READ, WRITE, ALIGN_4BYTES)

7. item:
Name: .CRT
VirtualSize: 0x00000018
VirtualAddress: 0x0000F000
SizeOfRawData: 0x00000200
PointerToRawData: 0x0000B000
PointerToRelocations: 0x00000000
PointerToLinenumbers: 0x00000000
NumberOfRelocations: 0x0000
NumberOfLinenumbers: 0x0000
Characteristics: 0xC0300040
(INITIALIZED_DATA, READ, WRITE, ALIGN_4BYTES)

8. item:
Name: .tls
VirtualSize: 0x00000020
VirtualAddress: 0x00010000
SizeOfRawData: 0x00000200
PointerToRawData: 0x0000B200
PointerToRelocations: 0x00000000
PointerToLinenumbers: 0x00000000
NumberOfRelocations: 0x0000
NumberOfLinenumbers: 0x0000
Characteristics: 0xC0300040
(INITIALIZED_DATA, READ, WRITE, ALIGN_4BYTES)

一些常见的区块如下:

Name Meaning
.text 默认代码区块。
.data 默认读/写数据区块。全局变量/静态变量一般放在此处。
.rdata 默认只读数据区块,很少用到。
.idata 输入表,包含其他外来DLL的函数及数据信息。
.edata 输出表。
.rsrc 资源,包含模块的全部资源:图标、菜单、位图等。只读。
.bss 未初始化数据,很少用到。
.crt 支持CRT所添加的数据。
.tls 支持通过__declspec(thread)声明的线程局部存储变量的数据。
.reloc 可执行文件的基址重定位。

区块对齐

在PE文件头IMAGE_OPTIONAL_HEADER结构中,SectionAlignmentFileAlignment分别定义了区块在内存和文件中的对齐值,文件对齐值通常是0x200,x86系统下内存对齐值通常是0x1000

内存偏移与文件偏移的转换

由于内存对齐值与文件对齐值不一定相同,因此数据在内存与文件中的偏移也不一定相同,在一些情况下就需要将文件偏移与内存偏移进行转换。 DOS头、PE头、区块表在两种地址下的偏移都是一样的,而区块则会发生改变,若记\(~\Delta k~\)为内存偏移(RVA)与文件偏移(File Offset)的差,即 \[\Delta k = \rm RVA - File\,Offset = VA - ImageBase - File\,Offset\] 那么每个区块中的\(~\Delta k~\)都是相同的,可以通过上式,用RVA, File Offset, VA任意一个求出其余两者。

区块(Section )

Section Table之后是各个Section

输入表(Import Table)

每个被PE文件引入的DLL都对应了一个输入地址表(Import Address Table),IAT包含了一组函数指针。 OptionalHeaderDataDirectory的第二项就是Import Table,输入表以一个IMAGE_IMPORT_DESCRIPTOR(IID)数组开始,IID数组以一个全0的IID结构结束。

IMAGE_IMPORT_DESCRIPTOR

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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;
  • OriginalFirstThunk(Characteristics) 包含指向输入名称表(INT)的RVA,INT是一个IMAGE_THUNK_DATA数组,每个IMAGE_THUNK_DATA指向一个IMAGE_IMPORT_BY_NAME结构。

  • ForwarderChain 第一个被转向的API的索引,一般为0。在PE引用DLL中的API,该API又引用其他DLL中API时使用。

  • Name 指向DLL的名字。

  • FirstThunk 指向IAT的RVA,IAT也是一个IMAGE_THUNK_DATA数组。

OriginalFirstThunkFirstThunk指向两个本质上相同的IMAGE_THUNK_DATA数组,如图所示:

IMAGE_THUNK_DATA

INT与IAT中的两个IMAGE_THUNK_DATA数组均以全0结束。IMAGE_THUNK_DATA结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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;

#include "poppack.h" // Back to 4 byte packing

typedef struct _IMAGE_THUNK_DATA32 {
union {
DWORD ForwarderString; // PBYTE
DWORD Function; // PDWORD
DWORD Ordinal;
DWORD AddressOfData; // PIMAGE_IMPORT_BY_NAME
} u1;
} IMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;
Name Meaning
ForwarderString 指向一个转向者字符串的RVA
Function 被输入函数的内存地址
Ordinal 被输入函数的序号
AddressOfData 指向IMAGE_IMPORT_BY_NAME

IMAGE_IMPORT_BY_NAME

存储输入函数的相关信息。

1
2
3
4
typedef struct _IMAGE_IMPORT_BY_NAME {
WORD Hint;
BYTE Name[1];
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
  • Hint 该函数在其驻留DLL输出表中的序号。

  • Name[1] 函数名,ASCII码字符串,以NULL结束。

输入地址表(IAT)

IAT会被PE装载器重写,PE装载器搜索OriginalFirstThunk(如果有),然后迭代搜索数组中的指针,找到每个IMAGE_IMPORT_BY_NAME指向的输入函数的地址,然后装载器用函数真正入口地址替换IAT中IMAGE_THUNK_DATA中的值。PE文件装载内存后IAT如下:

考察“Hellor, world”中的输入表,.idata的RVA为00 00 E0 00,在文件中的偏移为00 00 A6 00\(\Delta k = \mathrm{0x3A00}\),跳转到此处:

1
2
3
4
5
6
7
8
0000a600h: 78 E0 00 00 00 00 00 00 00 00 00 00 34 E7 00 00 ; x?.........4?.
0000a610h: 9C E1 00 00 D8 E0 00 00 00 00 00 00 00 00 00 00 ; 溼..剜..........
0000a620h: 4C E7 00 00 FC E1 00 00 E4 E0 00 00 00 00 00 00 ; L?...溧......
0000a630h: 00 00 00 00 E4 E7 00 00 08 E2 00 00 74 E1 00 00 ; ....溏...?.t?.
0000a640h: 00 00 00 00 00 00 00 00 00 E8 00 00 98 E2 00 00 ; .........?.樷..
0000a650h: 88 E1 00 00 00 00 00 00 00 00 00 00 24 E8 00 00 ; 堘..........$?.
0000a660h: AC E2 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ..............
0000a670h: 00 00 00 00 00 00 00 00 C0 E2 00 00 D8 E2 00 00 ; ........棱..剽..

每个IID大小为0x14字节,可以看到这里IID数组共有5个元素,第一个元素的值如下:

1
2
3
4
5
6
1. ImageImportDescriptor:
OriginalFirstThunk: 0x0000E078
TimeDateStamp: 0x00000000 (GMT: Thu Jan 01 00:00:00 1970)
ForwarderChain: 0x00000000
Name: 0x0000E734 ("KERNEL32.dll")
FirstThunk: 0x0000E19C

第一个IID所指向INT的RVA为00 00 E0 78,对应文件偏移为00 00 E0 78 - 00 00 3A 00 = 00 00 A6 78

1
2
3
4
5
6
7
0000a670h: 00 00 00 00 00 00 00 00 C0 E2 00 00 D8 E2 00 00 ; ........棱..剽..
0000a680h: F0 E2 00 00 FE E2 00 00 0A E3 00 00 1C E3 00 00 ; 疴.....?..?.
0000a690h: 2C E3 00 00 3A E3 00 00 4C E3 00 00 5C E3 00 00 ; ,?.:?.L?.\?.
0000a6a0h: 70 E3 00 00 82 E3 00 00 9E E3 00 00 B4 E3 00 00 ; p?.傘..炪..淬..
0000a6b0h: C8 E3 00 00 E0 E3 00 00 F0 E3 00 00 06 E4 00 00 ; 茹..嚆..疸...?.
0000a6c0h: 24 E4 00 00 2C E4 00 00 3A E4 00 00 4C E4 00 00 ; $?.,?.:?.L?.
0000a6d0h: 5C E4 00 00 00 00 00 00 72 E4 00 00 7C E4 00 ; \?.....r?.|?

可以看到这个INT一共有23个元素,第一个元素的值是00 00 E2 C0,是其所指向的IMAGE_IMPORT_BY_NAME的RVA,文件偏移为00 00 A8 C0,跳到此处:

1
2
0000a8c0h: CF 00 44 65 6C 65 74 65 43 72 69 74 69 63 61 6C ; ?DeleteCritical
0000a8d0h: 53 65 63 74 69 6F 6E 00 EC 00 45 6E 74 65 72 43 ; Section.?EnterC

00 00 A8 C2处开始就是输入函数的名字,可以看到函数名为DeleteCriticalSection。同理可以得到所有IID相关信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
->Import Table
1. ImageImportDescriptor:
OriginalFirstThunk: 0x0000E078
TimeDateStamp: 0x00000000 (GMT: Thu Jan 01 00:00:00 1970)
ForwarderChain: 0x00000000
Name: 0x0000E734 ("KERNEL32.dll")
FirstThunk: 0x0000E19C

Ordinal/Hint API name
------------ ---------------------------------------
0x00CF "DeleteCriticalSection"
0x00EC "EnterCriticalSection"
0x0117 "ExitProcess"
0x012C "FindClose"
0x0130 "FindFirstFileA"
0x0141 "FindNextFileA"
0x0160 "FreeLibrary"
0x0184 "GetCommandLineA"
0x01FE "GetLastError"
0x0211 "GetModuleHandleA"
0x0241 "GetProcAddress"
0x02DE "InitializeCriticalSection"
0x02E8 "InterlockedExchange"
0x02FB "IsDBCSLeadByteEx"
0x032E "LeaveCriticalSection"
0x0331 "LoadLibraryA"
0x035C "MultiByteToWideChar"
0x0474 "SetUnhandledExceptionFilter"
0x0480 "Sleep"
0x0495 "TlsGetValue"
0x04BD "VirtualProtect"
0x04BF "VirtualQuery"
0x04DF "WideCharToMultiByte"

2. ImageImportDescriptor:
OriginalFirstThunk: 0x0000E0D8
TimeDateStamp: 0x00000000 (GMT: Thu Jan 01 00:00:00 1970)
ForwarderChain: 0x00000000
Name: 0x0000E74C ("msvcrt.dll")
FirstThunk: 0x0000E1FC

Ordinal/Hint API name
------------ ---------------------------------------
0x0050 "_strdup"
0x0052 "_stricoll"

3. ImageImportDescriptor:
OriginalFirstThunk: 0x0000E0E4
TimeDateStamp: 0x00000000 (GMT: Thu Jan 01 00:00:00 1970)
ForwarderChain: 0x00000000
Name: 0x0000E7E4 ("msvcrt.dll")
FirstThunk: 0x0000E208

Ordinal/Hint API name
------------ ---------------------------------------
0x0058 "__getmainargs"
0x0077 "__mb_cur_max"
0x0083 "__p__environ"
0x0085 "__p__fmode"
0x0099 "__set_app_type"
0x00DB "_cexit"
0x011D "_errno"
0x015E "_fullpath"
0x01A1 "_iob"
0x01A6 "_isctype"
0x02B1 "_onexit"
0x02BA "_pctype"
0x02F1 "_setmode"
0x043B "abort"
0x0443 "atexit"
0x0445 "atoi"
0x044A "calloc"
0x0466 "fputc"
0x046B "free"
0x0476 "fwrite"
0x047B "getenv"
0x049E "localeconv"
0x04A3 "malloc"
0x04AA "mbstowcs"
0x04AF "memcpy"
0x04C4 "realloc"
0x04CB "setlocale"
0x04CD "signal"
0x04D8 "strchr"
0x04DA "strcoll"
0x04E1 "strlen"
0x04FD "tolower"
0x0504 "vfprintf"
0x051C "wcslen"
0x052D "wcstombs"

4. ImageImportDescriptor:
OriginalFirstThunk: 0x0000E174
TimeDateStamp: 0x00000000 (GMT: Thu Jan 01 00:00:00 1970)
ForwarderChain: 0x00000000
Name: 0x0000E800 ("libgcc_s_dw2-1.dll")
FirstThunk: 0x0000E298

Ordinal/Hint API name
------------ ---------------------------------------
0x0024 "__deregister_frame_info"
0x0069 "__register_frame_info"
0x0075 "__udivdi3"
0x0077 "__umoddi3"

5. ImageImportDescriptor:
OriginalFirstThunk: 0x0000E188
TimeDateStamp: 0x00000000 (GMT: Thu Jan 01 00:00:00 1970)
ForwarderChain: 0x00000000
Name: 0x0000E824 ("libstdc++-6.dll")
FirstThunk: 0x0000E2AC

Ordinal/Hint API name
------------ ---------------------------------------
0x0F2A "_ZNSt8ios_base4InitC1Ev"
0x0F2C "_ZNSt8ios_base4InitD1Ev"
0x103A "_ZSt4cout"
0x10A4 "_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc"

输出表(Export Table)

输出表一般存在于DLL文件中,用于让其他EXE或DLL调用该DLL中的函数。数据目录表的第一个成员就是输出表,指向一个IMAGE_EXPORT_DIRECTORY(IED)结构。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//
// Export Format
//

typedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD Characteristics;
DWORD TimeDateStamp;
WORD MajorVersion;
WORD MinorVersion;
DWORD Name;
DWORD Base;
DWORD NumberOfFunctions;
DWORD NumberOfNames;
DWORD AddressOfFunctions; // RVA from base of image
DWORD AddressOfNames; // RVA from base of image
DWORD AddressOfNameOrdinals; // RVA from base of image
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
  • Characteristics 总是0

  • TimeDateStamp 输出表创建的GMT时间

  • MajorVersion 主版本号,设置为0

  • MinorVersion 次版本号,设置为0

  • Name 指向DLL名字的RVA。

  • Base 输出表的起始序数值,一般是1(不是必须),通过序数查询输出函数时,会减去这个值,然后作为输出地址表(EAT)的索引。

  • NumberOfFunctions EAT中的条目数量,如果为0,表明该序数值没有代码或数据输出。

  • NumberOfNames 输出函数名称表(ENT)的条目数量,总是小于等于NumberOfFunctions。小于时符号只通过序数输出。

  • AddressOfFunctions EAT的RVA,EAT是一个RVA数组。

  • AddressOfNames ENT的RVA,ENT是一个指向ASCII字符串的RVA数组,ASCII字符串按顺序排列。

  • AddressOfNameOrdinals 输出序数表的RVA。输出序数表是一个字的数组,将ENT中的数组索引映射到相应的输出地址表条目。

用VS2015新建一个默认的DLL文件test_dll.dll,转到IED处,

1
2
3
00006b20h: 00 00 00 00 0D CE EB 57 00 00 00 00 7A 7D 01 00 ; .....坞W....z}..
00006b30h: 01 00 00 00 05 00 00 00 05 00 00 00 48 7D 01 00 ; ............H}..
00006b40h: 5C 7D 01 00 70 7D 01 00 8F 12 01 00 0A 10 01 00 ; \}..p}..?......

各字段如下:

1
2
3
4
5
6
7
8
9
10
11
12
->Export Table
Characteristics: 0x00000000
TimeDateStamp: 0x57EBCE0D (GMT: Wed Sep 28 14:05:01 2016)
MajorVersion: 0x0000
MinorVersion: 0x0000 -> 0.00
Name: 0x00017D7A ("test_dll.dll")
Base: 0x00000001
NumberOfFunctions: 0x00000005
NumberOfNames: 0x00000005
AddressOfFunctions: 0x00017D48
AddressOfNames: 0x00017D5C
AddressOfNameOrdinals: 0x00017D70

0x00017D48对应的文件偏移为00006B48

1
2
00006b40h: 5C 7D 01 00 70 7D 01 00 8F 12 01 00 0A 10 01 00 ; \}..p}..?......
00006b50h: A3 12 01 00 BD 11 01 00 38 81 01 00 87 7D 01 00 ; ?..?..8?.噠..

第一个函数的RVA是00 01 12 8F,对应内容

1
2
3
4
0000068fh: E9 3C 03 00 00 E9 11 37 00 00 E9 72 15 00 00 E9 ; ?...?7..閞...?
0000069fh: 65 36 00 00 E9 A8 03 00 00 E9 53 0F 00 00 E9 1A ; e6..楱...镾...?
000006afh: 36 00 00 E9 C9 10 00 00 E9 0A 36 00 00 CC CC CC ; 6..樯...?6..烫?
000006bfh: CC ; ?

对应的汇编代码

再看函数名字,

1
2
3
00006b50h: A3 12 01 00 BD 11 01 00 38 81 01 00 87 7D 01 00 ; ?..?..8?.噠..
00006b60h: 9C 7D 01 00 BC 7D 01 00 DA 7D 01 00 ED 7D 01 00 ; 渳..紏..趠..韢..
00006b70h: 00 00 01 00 02 00 03 00 04 00 74 65 73 74 5F 64 ; ..........test_d

第一个名字RVA是00 01 7D 87,即文件偏移00 00 6B 87处,

1
2
3
4
5
6
7
8
00006b80h: 6C 6C 2E 64 6C 6C 00 3F 3F 30 43 74 65 73 74 5F ; ll.dll.??0Ctest_
00006b90h: 64 6C 6C 40 40 51 41 45 40 58 5A 00 3F 3F 34 43 ; dll@@QAE@XZ.??4C
00006ba0h: 74 65 73 74 5F 64 6C 6C 40 40 51 41 45 41 41 56 ; test_dll@@QAEAAV
00006bb0h: 30 40 24 24 51 41 56 30 40 40 5A 00 3F 3F 34 43 ; 0@$$QAV0@@Z.??4C
00006bc0h: 74 65 73 74 5F 64 6C 6C 40 40 51 41 45 41 41 56 ; test_dll@@QAEAAV
00006bd0h: 30 40 41 42 56 30 40 40 5A 00 3F 66 6E 74 65 73 ; 0@ABV0@@Z.?fntes
00006be0h: 74 5F 64 6C 6C 40 40 59 41 48 58 5A 00 3F 6E 74 ; t_dll@@YAHXZ.?nt
00006bf0h: 65 73 74 5F 64 6C 6C 40 40 33 48 41 00 00 00 00 ; est_dll@@3HA....

对应的字符串为??0Ctest_dll@@QAE@XZ。所有的5个名字如下:

1
2
3
4
5
6
7
Ordinal RVA        Symbol Name
------- ---------- ----------------------------------
0x0001 0x0001128F "??0Ctest_dll@@QAE@XZ"
0x0002 0x0001100A "??4Ctest_dll@@QAEAAV0@$$QAV0@@Z"
0x0003 0x000112A3 "??4Ctest_dll@@QAEAAV0@ABV0@@Z"
0x0004 0x000111BD "?fntest_dll@@YAHXZ"
0x0005 0x00018138 "?ntest_dll@@3HA"

基址重定位表(Base Relocation Table)

PE文件中的定位都假设了文件被装入默认的基地址,如果被装载到内存中其他地址,就需要重定位。基址重定位表位于.reloc区块,对应数据目录表中的IMAGE_DIRECTORY_ENTRY_BASERELOC,重定位表由重定位块组成,每个块存放4KB重定位信息,且各重定位块以DWORD对齐,重定位块是一个IMAGE_BASE_RELOCATION结构,所有重定位块以一个VirtualAddress0IMAGE_BASE_RELOCATION结束。

IMAGE_BASE_RELOCATION

1
2
3
4
5
6
7
8
9
10
//
// Based relocation format.
//

typedef struct _IMAGE_BASE_RELOCATION {
DWORD VirtualAddress;
DWORD SizeOfBlock;
// WORD TypeOffset[1];
} IMAGE_BASE_RELOCATION;
typedef IMAGE_BASE_RELOCATION UNALIGNED * PIMAGE_BASE_RELOCATION;
  • VirtualAddress 重定位数据的开始RVA地址,重定位项地址加上VirtualAddress才是完整的RVA地址。

  • SizeOfBlock 当前重定位结构大小。

  • TypeOffset 一个数组,每个元素两字节,高4位代表重定位类型;低12位代表重定位地址。常见的重定位类型如下:

#define Value Meaning
IMAGE_REL_BASED_ABSOLUTE 0 用于对齐
IMAGE_REL_BASED_HIGHLOW 3 重定位指向的整个地址都需要修正,几乎总是此情况
IMAGE_REL_BASED_DIR64 10 64位PE文件中,指向的整个地址需要修正

举例说明,下面是test_dll的重定位表的部分数据:

1
2
00008400h: 00 10 01 00 74 00 00 00 D1 36 E1 36 F2 36 05 37 ; ....t...???.7
00008410h: 68 37 BC 37 C0 37 C4 37 C8 37 F6 37 FB 37 0D 38 ; h7??????.8

第一个重定位块的VirtualAddress00 01 10 00TypeOffset见下表

字段 重定位项1 重定位项2
TypeOffset高4位 3 3
TypeOffset低12位 6D1 6E1
低12位+ VirtualAddress 000116D1 000116E1
文件偏移 00000AD1 00000AE1
1
2
00000ad0h: B8 CD 10 01 10 C3 CC CC CC CC CC CC CC CC CC CC ; 竿...锰烫烫烫烫?
00000ae0h: B8 E0 11 01 10 C3 CC CC CC CC CC CC CC CC CC CC ; 膏...锰烫烫烫烫?

于是10 01 10 CD10 01 11 E0便是需要重定位的数据,这些数据在重定位时,需要加上\(实际装入地址 -文件默认基地址\)

资源(Resource)

Windows程序的各种界面,包括加速键、位图、光标、对话框等都是资源。资源的具体格式已经在下面的注释中给出了。

IMAGE_RESOURCE_DIRECTORY

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//
// Resource Format.
//

//
// Resource directory consists of two counts, following by a variable length
// array of directory entries. The first count is the number of entries at
// beginning of the array that have actual names associated with each entry.
// The entries are in ascending order, case insensitive strings. The second
// count is the number of entries that immediately follow the named entries.
// This second count identifies the number of entries that have 16-bit integer
// Ids as their name. These entries are also sorted in ascending order.
//
// This structure allows fast lookup by either name or number, but for any
// given resource entry only one form of lookup is supported, not both.
// This is consistant with the syntax of the .RC file and the .RES file.
//

typedef struct _IMAGE_RESOURCE_DIRECTORY {
DWORD Characteristics;
DWORD TimeDateStamp;
WORD MajorVersion;
WORD MinorVersion;
WORD NumberOfNamedEntries;
WORD NumberOfIdEntries;
// IMAGE_RESOURCE_DIRECTORY_ENTRY DirectoryEntries[];
} IMAGE_RESOURCE_DIRECTORY, *PIMAGE_RESOURCE_DIRECTORY;

IMAGE_RESOURCE_DIRECTORY_ENTRY

IMAGE_RESOURCE_DIRECTORY之后就是IMAGE_RESOURCE_DIRECTORY_ENTRY,在匿名结构体中用到了位域成员。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//
// Each directory contains the 32-bit Name of the entry and an offset,
// relative to the beginning of the resource directory of the data associated
// with this directory entry. If the name of the entry is an actual text
// string instead of an integer Id, then the high order bit of the name field
// is set to one and the low order 31-bits are an offset, relative to the
// beginning of the resource directory of the string, which is of type
// IMAGE_RESOURCE_DIRECTORY_STRING. Otherwise the high bit is clear and the
// low-order 16-bits are the integer Id that identify this resource directory
// entry. If the directory entry is yet another resource directory (i.e. a
// subdirectory), then the high order bit of the offset field will be
// set to indicate this. Otherwise the high bit is clear and the offset
// field points to a resource data entry.
//

typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY {
union {
struct {
DWORD NameOffset:31;
DWORD NameIsString:1;
} DUMMYSTRUCTNAME;
DWORD Name;
WORD Id;
} DUMMYUNIONNAME;
union {
DWORD OffsetToData;
struct {
DWORD OffsetToDirectory:31;
DWORD DataIsDirectory:1;
} DUMMYSTRUCTNAME2;
} DUMMYUNIONNAME2;
} IMAGE_RESOURCE_DIRECTORY_ENTRY, *PIMAGE_RESOURCE_DIRECTORY_ENTRY;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//
// For resource directory entries that have actual string names, the Name
// field of the directory entry points to an object of the following type.
// All of these string objects are stored together after the last resource
// directory entry and before the first resource data object. This minimizes
// the impact of these variable length objects on the alignment of the fixed
// size directory entry objects.
//

typedef struct _IMAGE_RESOURCE_DIRECTORY_STRING {
WORD Length;
CHAR NameString[ 1 ];
} IMAGE_RESOURCE_DIRECTORY_STRING, *PIMAGE_RESOURCE_DIRECTORY_STRING;


typedef struct _IMAGE_RESOURCE_DIR_STRING_U {
WORD Length;
WCHAR NameString[ 1 ];
} IMAGE_RESOURCE_DIR_STRING_U, *PIMAGE_RESOURCE_DIR_STRING_U;

IMAGE_RESOURCE_DATA_ENTRY

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//
// Each resource data entry describes a leaf node in the resource directory
// tree. It contains an offset, relative to the beginning of the resource
// directory of the data for the resource, a size field that gives the number
// of bytes of data at that offset, a CodePage that should be used when
// decoding code point values within the resource data. Typically for new
// applications the code page would be the unicode code page.
//

typedef struct _IMAGE_RESOURCE_DATA_ENTRY {
DWORD OffsetToData;
DWORD Size;
DWORD CodePage;
DWORD Reserved;
} IMAGE_RESOURCE_DATA_ENTRY, *PIMAGE_RESOURCE_DATA_ENTRY;

第一级目录

结合test_dll来看,IMAGE_RESOURCE_DIRECTORY位于文件偏移00 00 7E 00

1
2
3
4
5
6
00007e00h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 ; ................
00007e10h: 18 00 00 00 18 00 00 80 00 00 00 00 00 00 00 00 ; .......€........
00007e20h: 00 00 00 00 00 00 01 00 02 00 00 00 30 00 00 80 ; ............0..€
00007e30h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 ; ................
00007e40h: 09 04 00 00 48 00 00 00 70 C1 01 00 7D 01 00 00 ; ....H...p?.}...
00007e50h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................

NumberOfIdEntries为1,接下来是紧随其后的entry,Name字段是00 00 00 18,最高位为0,此时作为ID使用,OffsetToData字段是80 00 00 18,最高位为1,表明该entry仍然是一个目录,低31位的值是0x18——所指向目录相对于资源起始地址的偏移,于是所指向目录的文件偏移为7E00+18=7E18。

第二级目录

NumberOfIdEntries仍然是1,entry的OffsetToData80 00 00 30,指向7E30处。

第三级目录

NumberOfIdEntries为1,entryOffsetToData最高位是0,低31位指向一个data entry,文件偏移为7E48。

data entry

OffsetToData=00 01 C1 70是data所在的RVA,Size=00 00 01 7D,其他两个字段均为0。此处data内容是一段字符:

1
2
3
4
5
6
00007f70h: 3C 3F 78 6D 6C 20 76 65 72 73 69 6F 6E 3D 27 31 ; <?xml version='1
00007f80h: 2E 30 27 20 65 6E 63 6F 64 69 6E 67 3D 27 55 54 ; .0' encoding='UT
00007f90h: 46 2D 38 27 20 73 74 61 6E 64 61 6C 6F 6E 65 3D ; F-8' standalone=
00007fa0h: 27 79 65 73 27 3F 3E 0D 0A 3C 61 73 73 65 6D 62 ; 'yes'?>..<assemb
00007fb0h: 6C 79 20 78 6D 6C 6E 73 3D 27 75 72 6E 3A 73 63 ; ly xmlns='urn:sc
00007fc0h: 68 65 6D 61 73 2D 6D 69 63 72 6F 73 6F 66 74 2D ; hemas-microsoft-

整体结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
->Resource Tree (detailed dump)
[Resource Directory (0)]:
Characteristics: 0x00000000
TimeDateStamp: 0x00000000 (Thu Jan 01 00:00:00 1970)
MajorVersion: 0x0000
MinorVersion: 0x0000 -> 0.00
NumberOfNamedEntries: 0x0000
NumberOfIdEntries: 0x0001
---------------------------------------------------------
[ResourceEntry]:
Name/Id: 0x00000018
OffsetToData: 0x80000018 (DATA_IS_DIRECTORY)
[Resource Directory (1)]:
Characteristics: 0x00000000
TimeDateStamp: 0x00000000 (Thu Jan 01 00:00:00 1970)
MajorVersion: 0x0000
MinorVersion: 0x0000 -> 0.00
NumberOfNamedEntries: 0x0000
NumberOfIdEntries: 0x0001
[ResourceEntry]:
Name/Id: 0x00000002
OffsetToData: 0x80000030 (DATA_IS_DIRECTORY)
[Resource Directory (2)]:
Characteristics: 0x00000000
TimeDateStamp: 0x00000000 (Thu Jan 01 00:00:00 1970)
MajorVersion: 0x0000
MinorVersion: 0x0000 -> 0.00
NumberOfNamedEntries: 0x0000
NumberOfIdEntries: 0x0001
[ResourceEntry]:
Name/Id: 0x00000409
OffsetToData: 0x00000048
[ResourceDataEntry]:
OffsetToData (RVA): 0x0001C170
Size: 0x0000017D
CodePage: 0x00000000
Reserved: 0x00000000

参考资料

------ 本文结束 ------

版权声明

Memory is licensed under a Creative Commons BY-NC-SA 4.0 International License.
博客采用知识共享署署名(BY)-非商业性(NC)-相同方式共享(SA)
本文首发于Memory,转载请保留出处。