0%

PE头说明

对PE头的了解直接决定了对它的利用程度

PE结构整体图

程序读取文件PE头

PE头分为DOS头和NT头,其中NT头又包括PE标识、PE标准头和PE可选头三个,虽然叫做可选头,但它也是不可缺少的。

这里通过一个程序将其内容都读取出来,并对关键内容进行了标识,其中所列出来的PE头信息都是必须要记住的,加 * 的内容都是重中之重,加 - 的内容是编译器填写并且在现在的程序中是没有用处的,即修改不影响运行的,我们可以对其进行利用。

这里先给出最后的运行结果图

代码如下:

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
#include <stdio.h>
#include <windows.h>

int filesize(FILE* file) {
fseek(file, 0, SEEK_END);
int size = ftell(file);
fseek(file, 0, SEEK_SET);
return size;
}

int DOS_Header(LPSTR pFileBuffer) {
printf("----------DOSHeader----------\n");
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;

// *(PWORD)pDosHeader != IMAGE_DOS_SIGNATURE
if ((WORD)pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
printf("DOSHeader failed:MZ");
return 0;
}
printf("* e_magic:%x\n", (WORD)pDosHeader->e_magic);
printf("* e_lfanew:%08x\n", (DWORD)pDosHeader->e_lfanew);

return pDosHeader->e_lfanew;
}

int NT_Header(LPSTR pFileBuffer, int NT_addr) {
printf("----------NTHeader----------\n");
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)(pFileBuffer + NT_addr);
if ((WORD)pNtHeader->Signature != IMAGE_NT_SIGNATURE) {
printf("NTHeader failed:NT");
return 0;
}
printf("Signature:%08x\n", (DWORD)pNtHeader->Signature);

printf("----------FileHeader----------\n");
PIMAGE_FILE_HEADER FileHeader = (PIMAGE_FILE_HEADER)((DWORD)pNtHeader + 4);
printf("* Machine:%04x\n", FileHeader->Machine);
printf("* NumberOfSections:%04x\n", FileHeader->NumberOfSections);
printf("TimeDateStamp:%08x\n", FileHeader->TimeDateStamp);
printf("* SizeOfOptionalHeader:%04x\n", FileHeader->SizeOfOptionalHeader);
printf("Characteristics:%04x\n", FileHeader->Characteristics);

printf("----------OptionalHeader----------\n");
PIMAGE_OPTIONAL_HEADER32 OptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pNtHeader + 4 + sizeof(_IMAGE_FILE_HEADER));
printf("* Magic:%04x\n", OptionalHeader->Magic);
printf("- SizeOfCode:%08x\n", OptionalHeader->SizeOfCode);
printf("- SizeOfInitializedData:%08x\n", OptionalHeader->SizeOfInitializedData);
printf("- SizeOfUninitializedData:%08x\n", OptionalHeader->SizeOfUninitializedData);
printf("* AddressOfEntryPoint:%08x\n", OptionalHeader->AddressOfEntryPoint);
printf("- BaseOfCode:%08x\n", OptionalHeader->BaseOfCode);
printf("- BaseOfData:%08x\n", OptionalHeader->BaseOfData);
printf("* ImageBase:%08x\n", OptionalHeader->ImageBase);
printf("* SectionAlignment:%08x\n", OptionalHeader->SectionAlignment);
printf("* FileAlignment:%08x\n", OptionalHeader->FileAlignment);
printf("* SizeOfImage:%08x\n", OptionalHeader->SizeOfImage);
printf("* SizeOfHeaders:%08x\n", OptionalHeader->SizeOfHeaders);
printf("CheckSum:%08x\n", OptionalHeader->CheckSum);
printf("SizeOfStackReserve:%08x\n", OptionalHeader->SizeOfStackReserve);
printf("SizeOfStackCommit:%08x\n", OptionalHeader->SizeOfStackCommit);
printf("SizeOfHeapReserve:%08x\n", OptionalHeader->SizeOfHeapReserve);
printf("SizeOfHeapCommit:%08x\n", OptionalHeader->SizeOfHeapCommit);
printf("NumberOfRvaAndSizes:%08x\n", OptionalHeader->NumberOfRvaAndSizes);

return 0;
}

int main(int argc, char* argv[]) {
FILE* tFile = fopen("C:\\Users\\veselwuxin\\Desktop\\wtest.exe", "rb");
if (tFile == NULL) {
printf("file open failed\n");
return 0;
}
int size = filesize(tFile);
char* FileBuffer = (char*)malloc(sizeof(char) * size);
if (!FileBuffer) {
printf("Alloc FileBuffer Failed\n");
return 0;
}
memset(FileBuffer, 0, sizeof(char) * size);

int freadsize = fread(FileBuffer, 1, sizeof(char) * size, tFile);
if (freadsize != sizeof(char) * size) {
printf("FileBuffer Write Failed\n");
return 0;
}

printf("FileBuffer address:%p\n", FileBuffer);

//_IMAGE_DOS_HEADER
//_IMAGE_NT_HEADERS
//_IMAGE_FILE_HEADER
//_IMAGE_OPTIONAL_HEADER

int NT_addr = DOS_Header(FileBuffer);
NT_Header(FileBuffer, NT_addr);

free(FileBuffer);
FileBuffer = NULL;
tFile = NULL;

return 0;
}

代码地址:GitHub

PE头结构体

_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;

_IMAGE_NT_HEADERS

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;

_IMAGE_FILE_HEADER

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;

_IMAGE_OPTIONAL_HEADER

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;
  • 最后的IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];也是比较重要的一块,这里暂时不提。

部分PE头说明

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
WORD e_magic     "MZ标记" 用于判断是否为可执行文件
DWORD e_lfanew PE头相对于文件的偏移,用于定位PE文件

WORD Machine 程序运行的CPU型号:0x0 任何处理器/0x14C 386及后续处理器
WORD NumberOfSections 文件中存在的节的总数,如果要新增节或者合并节 就要修改这个值
DWORD TimeDateStamp 时间戳:文件的创建时间(和操作系统的创建时间无关),编译器填写的
WORD SizeOfOptionalHeader 可选PE头的大小,32位PE文件默认E0h 64位PE文件默认为F0h 大小可以自定义
WORD Characteristics 每个位有不同的含义,可执行文件值为10F 即0 1 2 3 8位置1

WORD Magic 说明文件类型:10B 32位下的PE文件 20B 64位下的PE文件
DWORD SizeOfCode 所有代码节的和,必须是FileAlignment的整数倍 编译器填的 没用
DWORD SizeOfInitializedData 已初始化数据大小的和,必须是FileAlignment的整数倍 编译器填的,没用
DWORD SizeOfUninitializedData 未初始化数据大小的和,必须是FileAlignment的整数倍 编译器填的,没用
DWORD AddressOfEntryPoint 程序入口
DWORD BaseOfCode 代码开始的基址,编译器填的,没用
DWORD BaseOfData 数据开始的基址,编译器填的,没用
DWORD ImageBase 内存镜像基址
DWORD SectionAlignment 内存对齐
DWORD FileAlignment 文件对齐
DWORD SizeOfImage 内存中整个PE文件的映射的尺寸,可以比实际的值大,但必须是SectionAlignment的整数倍
DWORD SizeOfHeaders 所有头+节表按照文件对齐后的大小,否则加载会出错
DWORD CheckSum 校验和,一些系统文件有要求,用来判断文件是否被修改,一般是驱动级的程序需要,但是算法是已知的,可以修改完成后自己重新计算出CheckSum的值
DWORD SizeOfStackReserve 初始化时保留的堆栈大小
DWORD SizeOfStackCommit 初始化时实际提交的大小
DWORD SizeOfHeapReserve 初始化时保留的堆大小
DWORD SizeOfHeapCommit 初始化时实践提交的大小
DWORD NumberOfRvaAndSizes 目录项数目