二进制安全-PE文件入门-遍历各个头

前言

在上一节中讲到了NT头中的OptionalHeader结构,该结构在PE文件中是可选PE头。继续往下讲PE的一些其他结构以及作用。

节表


_IMAGE_SECTION_HEADER节表结构,该结构位与可选PE头之后。在整个PE文件中节表结构还是比较重要的,不管是对PE文件加壳还是加密都需要使用到节表结构。

遍历各个头信息

前置知识
在遍历PE信息之前,讲解一下PE文件在计算机上的状态和在内存中的状态,在计算机存盘中的时候是FileBuffer的状态,在内存中的时候是ImageBuffer状态。
使用fopen_s打开文件,fread_s读取文件,fseek用来控制FILE指针的位置

1
errno_t fopen_s( FILE** pFile, const char *filename, const char *mode );

1.pFile
执行文件指针的指针,保存文件的一些基本信息
2.filename
文件的名称
3.mode
打开格式,”rb”为只读模式
返回值
如果函数执行成功,返回0.

1
2
3
4
5
6
7
size_t fread_s(
void *buffer,
size_t bufferSize,
size_t elementSize,
size_t count,
FILE *stream
);

1.buffer
数据的存储位置
2.bufferSize
buffer的大小,字节为单位
3.elementSize
读取数据的大小,字节为单位
4.count
读取多少数据的最大量
5.stream
返回FILE的指针
返回值
如果函数执行成功,返回0.

1
int fseek( FILE *stream, long offset, int origin );

1.stream
FILE指针
2.offset
偏移
3.origin
FILE初始位置
origin使用SEEK_END可以快速把指针移动到文件尾。
使用ftell函数获取当前指针位置。
origin使用SEEK_SET可以快速把指针移动到文件首。

DOS—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
#include<iostream>
#include<Windows.h>

using namespace std;

int main(int argc, char* argv[])
{
// 打开文件
FILE* p_file;
errno_t fopen_result = fopen_s(&p_file, "D:\\source\\repos\\PE_Tools\\Release\\PE_Tools.exe", "rb");
if (fopen_result || p_file == nullptr)
{
return ERROR;
}

// 获取文件大小
fseek(p_file, 0, SEEK_END);
long fileSize = ftell(p_file);
fseek(p_file, 0, SEEK_SET);

// 读取文件
DWORD* fileBuffer = (DWORD*)malloc(fileSize);
if (fileBuffer == NULL)
{
return ERROR;
}
memset(fileBuffer, 0, fileSize);
size_t fread_result = fread_s(fileBuffer, fileSize, 1, fileSize, p_file);


return 0;
}

在最后return处下一个断点观察fileBuffer的数据。

可以发现这些数据与在010中看到的一致,接下来就是使用结构体去给这些数据分配个各个属性了。
PIMAGE_DOS_HEADER结构体,在winnt.h中定义,是Windows提供好的。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 遍历DOS头
void PrintDosHeader(const IMAGE_DOS_HEADER* dosHeader) {
printf("e_magic: 0x%X\n", dosHeader->e_magic);
printf("e_cblp: %x\n", dosHeader->e_cblp);
printf("e_cp: %x\n", dosHeader->e_cp);
printf("e_crlc: %x\n", dosHeader->e_crlc);
printf("e_cparhdr: %x\n", dosHeader->e_cparhdr);
printf("e_minalloc: %x\n", dosHeader->e_minalloc);
printf("e_maxalloc: %x\n", dosHeader->e_maxalloc);
printf("e_ss: 0x%X\n", dosHeader->e_ss);
printf("e_sp: 0x%X\n", dosHeader->e_sp);
printf("e_csum: 0x%X\n", dosHeader->e_csum);
printf("e_ip: 0x%X\n", dosHeader->e_ip);
printf("e_cs: 0x%X\n", dosHeader->e_cs);
printf("e_lfarlc: 0x%X\n", dosHeader->e_lfarlc);
printf("e_ovno: 0x%X\n", dosHeader->e_ovno);
printf("e_res[4]: {%x, %x, %x, %x}\n", dosHeader->e_res[0], dosHeader->e_res[1], dosHeader->e_res[2], dosHeader->e_res[3]);
printf("e_oemid: 0x%X\n", dosHeader->e_oemid);
printf("e_oeminfo: 0x%X\n", dosHeader->e_oeminfo);
printf("e_res2[10]: {...}%x:\n",dosHeader->e_res2[0]); // 没有写完
printf("e_lfanew: 0x%X\n", dosHeader->e_lfanew);
}

NT-Header

NT定位
在NT头和DOS中间由一个DOS存根数据,这个数据是我们不需要的,定位NT头需要使用到DOS头的最后一个变量,该变量表示NT头在文件中开始的位置,例如e_lfanew值为0x100,那么表示了NT头在文件中的位置就是0x100处。
这样我们使用fileBuffer加上e_lfanew就可以定位到NT头了。
PS:在使用指针加法运算的时候要注意指针的数据类型,当指针的数据类型为DWORD的时候,指针+1实际上是加了4个字节。

FILE_HEADER

FILE-HEADER是属于NT头的成员变量,只不过他也是一个结构体,定位FILE-HEADER有两个方法:
1.直接在NT头中使用取地址符号&获取到FILE-HEADER的地址。
2.使用FileBuffer+e_lfanew+sizeof(Signature) 的值定位到FILE-HEADER。
使用第二种方式定位FILE-HEADER特别容易忽略掉NT头中的Signature变量,导致定位不正确。

1
2
3
4
5
6
7
// NT头
PIMAGE_NT_HEADERS nt_headers = (PIMAGE_NT_HEADERS)(DWORD*)((BYTE*)fileBuffer + dos_headers->e_lfanew);
// 标准PE头
PIMAGE_FILE_HEADER file_headers = (PIMAGE_FILE_HEADER)(DWORD*)((BYTE*)fileBuffer + dos_headers->e_lfanew + sizeof(DWORD));
//PIMAGE_FILE_HEADER file_headers_two = (PIMAGE_FILE_HEADER)&nt_headers->FileHeader;
// 遍历标准PE头
PrintfFileHeader(file_headers);
1
2
3
4
5
6
7
8
9
10
void PrintfFileHeader(const PIMAGE_FILE_HEADER FileHeader) {
printf("FILE_HEADER:\n");
printf("Machine: 0x%X\n", FileHeader->Machine);
printf("NumberOfSections: %x\n", FileHeader->NumberOfSections);
printf("TimeDateStamp: 0x%X\n", FileHeader->TimeDateStamp);
printf("PointerToSymbolTable: 0x%X\n", FileHeader->PointerToSymbolTable);
printf("NumberOfSymbols: %d\n", FileHeader->NumberOfSymbols);
printf("SizeOfOptionalHeader: %x\n", FileHeader->SizeOfOptionalHeader);
printf("Characteristics: 0x%X\n", FileHeader->Characteristics);
}

OPTIONAL_HEADER

OPTIONAL_HEADER的定位,在FILE_HEADER的基础上加上FILE_HEADER的大小;或者直接从NT中获取到OPTIONAL_HEADER的地址。

1
2
3
4
// 遍历可选PE头
//PIMAGE_OPTIONAL_HEADER option_headers = (PIMAGE_OPTIONAL_HEADER)(DWORD*)((BYTE*)fileBuffer + dos_headers->e_lfanew + sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER));
PIMAGE_OPTIONAL_HEADER option_headers_two = (PIMAGE_OPTIONAL_HEADER)&nt_headers->OptionalHeader;
PrintfOptionHeader(option_headers_two);
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
void PrintfOptionHeader(const PIMAGE_OPTIONAL_HEADER OptionalHeader) {
printf("OPTIONAL_HEADER:\n");
printf("Magic: %X\n", OptionalHeader->Magic);
printf("MajorLinkerVersion: %d\n", OptionalHeader->MajorLinkerVersion);
printf("MinorLinkerVersion: %d\n", OptionalHeader->MinorLinkerVersion);
printf("SizeOfCode: %X\n", OptionalHeader->SizeOfCode);
printf("SizeOfInitializedData: %X\n", OptionalHeader->SizeOfInitializedData);
printf("SizeOfUninitializedData: %X\n", OptionalHeader->SizeOfUninitializedData);
printf("AddressOfEntryPoint: %X\n", OptionalHeader->AddressOfEntryPoint);
printf("BaseOfCode: %X\n", OptionalHeader->BaseOfCode);
printf("ImageBase: %X\n", OptionalHeader->ImageBase);
printf("SectionAlignment: %X\n", OptionalHeader->SectionAlignment);
printf("FileAlignment: %X\n", OptionalHeader->FileAlignment);
printf("MajorOperatingSystemVersion: %d\n", OptionalHeader->MajorOperatingSystemVersion);
printf("MinorOperatingSystemVersion: %d\n", OptionalHeader->MinorOperatingSystemVersion);
printf("MajorImageVersion: %d\n", OptionalHeader->MajorImageVersion);
printf("MinorImageVersion: %d\n", OptionalHeader->MinorImageVersion);
printf("MajorSubsystemVersion: %d\n", OptionalHeader->MajorSubsystemVersion);
printf("MinorSubsystemVersion: %d\n", OptionalHeader->MinorSubsystemVersion);
printf("Win32VersionValue: %X\n", OptionalHeader->Win32VersionValue);
printf("SizeOfImage: %X\n", OptionalHeader->SizeOfImage);
printf("SizeOfHeaders: %X\n", OptionalHeader->SizeOfHeaders);
printf("CheckSum: %X\n", OptionalHeader->CheckSum);
printf("Subsystem: %X\n", OptionalHeader->Subsystem);
printf("DllCharacteristics: %x\n", OptionalHeader->DllCharacteristics);
printf("SizeOfStackReserve: %x\n", OptionalHeader->SizeOfStackReserve);
printf("SizeOfStackCommit: %X\n", OptionalHeader->SizeOfStackCommit);
printf("SizeOfHeapReserve: %X\n", OptionalHeader->SizeOfHeapReserve);
printf("SizeOfHeapCommit: %X\n", OptionalHeader->SizeOfHeapCommit);
printf("LoaderFlags: %X\n", OptionalHeader->LoaderFlags);
printf("NumberOfRvaAndSizes: %d\n", OptionalHeader->NumberOfRvaAndSizes);
}

完整代码

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
119
120
121
122
#include<iostream>
#include<Windows.h>

using namespace std;


void PrintDosHeader(const IMAGE_DOS_HEADER* dosHeader) {
printf("e_magic: 0x%X\n", dosHeader->e_magic);
printf("e_cblp: %x\n", dosHeader->e_cblp);
printf("e_cp: %x\n", dosHeader->e_cp);
printf("e_crlc: %x\n", dosHeader->e_crlc);
printf("e_cparhdr: %x\n", dosHeader->e_cparhdr);
printf("e_minalloc: %x\n", dosHeader->e_minalloc);
printf("e_maxalloc: %x\n", dosHeader->e_maxalloc);
printf("e_ss: 0x%X\n", dosHeader->e_ss);
printf("e_sp: 0x%X\n", dosHeader->e_sp);
printf("e_csum: 0x%X\n", dosHeader->e_csum);
printf("e_ip: 0x%X\n", dosHeader->e_ip);
printf("e_cs: 0x%X\n", dosHeader->e_cs);
printf("e_lfarlc: 0x%X\n", dosHeader->e_lfarlc);
printf("e_ovno: 0x%X\n", dosHeader->e_ovno);
printf("e_res[4]: {%x, %x, %x, %x}\n", dosHeader->e_res[0], dosHeader->e_res[1], dosHeader->e_res[2], dosHeader->e_res[3]);
printf("e_oemid: 0x%X\n", dosHeader->e_oemid);
printf("e_oeminfo: 0x%X\n", dosHeader->e_oeminfo);
printf("e_res2[10]: {...}%x:\n",dosHeader->e_res2[0]); // 没有写完
printf("e_lfanew: 0x%X\n", dosHeader->e_lfanew);
}

void PrintfFileHeader(const PIMAGE_FILE_HEADER FileHeader) {
printf("FILE_HEADER:\n");
printf("Machine: 0x%X\n", FileHeader->Machine);
printf("NumberOfSections: %x\n", FileHeader->NumberOfSections);
printf("TimeDateStamp: 0x%X\n", FileHeader->TimeDateStamp);
printf("PointerToSymbolTable: 0x%X\n", FileHeader->PointerToSymbolTable);
printf("NumberOfSymbols: %d\n", FileHeader->NumberOfSymbols);
printf("SizeOfOptionalHeader: %x\n", FileHeader->SizeOfOptionalHeader);
printf("Characteristics: 0x%X\n", FileHeader->Characteristics);
}


void PrintfOptionHeader(const PIMAGE_OPTIONAL_HEADER OptionalHeader) {
printf("OPTIONAL_HEADER:\n");
printf("Magic: %X\n", OptionalHeader->Magic);
printf("MajorLinkerVersion: %d\n", OptionalHeader->MajorLinkerVersion);
printf("MinorLinkerVersion: %d\n", OptionalHeader->MinorLinkerVersion);
printf("SizeOfCode: %X\n", OptionalHeader->SizeOfCode);
printf("SizeOfInitializedData: %X\n", OptionalHeader->SizeOfInitializedData);
printf("SizeOfUninitializedData: %X\n", OptionalHeader->SizeOfUninitializedData);
printf("AddressOfEntryPoint: %X\n", OptionalHeader->AddressOfEntryPoint);
printf("BaseOfCode: %X\n", OptionalHeader->BaseOfCode);
printf("ImageBase: %X\n", OptionalHeader->ImageBase);
printf("SectionAlignment: %X\n", OptionalHeader->SectionAlignment);
printf("FileAlignment: %X\n", OptionalHeader->FileAlignment);
printf("MajorOperatingSystemVersion: %d\n", OptionalHeader->MajorOperatingSystemVersion);
printf("MinorOperatingSystemVersion: %d\n", OptionalHeader->MinorOperatingSystemVersion);
printf("MajorImageVersion: %d\n", OptionalHeader->MajorImageVersion);
printf("MinorImageVersion: %d\n", OptionalHeader->MinorImageVersion);
printf("MajorSubsystemVersion: %d\n", OptionalHeader->MajorSubsystemVersion);
printf("MinorSubsystemVersion: %d\n", OptionalHeader->MinorSubsystemVersion);
printf("Win32VersionValue: %X\n", OptionalHeader->Win32VersionValue);
printf("SizeOfImage: %X\n", OptionalHeader->SizeOfImage);
printf("SizeOfHeaders: %X\n", OptionalHeader->SizeOfHeaders);
printf("CheckSum: %X\n", OptionalHeader->CheckSum);
printf("Subsystem: %X\n", OptionalHeader->Subsystem);
printf("DllCharacteristics: %x\n", OptionalHeader->DllCharacteristics);
printf("SizeOfStackReserve: %x\n", OptionalHeader->SizeOfStackReserve);
printf("SizeOfStackCommit: %X\n", OptionalHeader->SizeOfStackCommit);
printf("SizeOfHeapReserve: %X\n", OptionalHeader->SizeOfHeapReserve);
printf("SizeOfHeapCommit: %X\n", OptionalHeader->SizeOfHeapCommit);
printf("LoaderFlags: %X\n", OptionalHeader->LoaderFlags);
printf("NumberOfRvaAndSizes: %d\n", OptionalHeader->NumberOfRvaAndSizes);
}

int main(int argc, char* argv[])
{
// 打开文件
FILE* p_file;
errno_t fopen_result = fopen_s(&p_file, "D:\\source\\repos\\PE_Tools\\Release\\PE_Tools.exe", "rb");
if (fopen_result || p_file == nullptr)
{
return ERROR;
}

// 获取文件大小
fseek(p_file, 0, SEEK_END);
long fileSize = ftell(p_file);
fseek(p_file, 0, SEEK_SET);

// 读取文件
DWORD* fileBuffer = (DWORD*)malloc(fileSize);
if (fileBuffer == NULL)
{
return ERROR;
}
memset(fileBuffer, 0, fileSize);
size_t fread_result = fread_s(fileBuffer, fileSize, 1, fileSize, p_file);

// 遍历DOS头
printf("******************DOS-HEADERS******************\n");
PIMAGE_DOS_HEADER dos_headers = (PIMAGE_DOS_HEADER)fileBuffer;
PrintDosHeader(dos_headers);

printf("******************FILE-HEADERS******************\n");

// NT头
PIMAGE_NT_HEADERS nt_headers = (PIMAGE_NT_HEADERS)(DWORD*)((BYTE*)fileBuffer + dos_headers->e_lfanew);
// 标准PE头
PIMAGE_FILE_HEADER file_headers = (PIMAGE_FILE_HEADER)(DWORD*)((BYTE*)fileBuffer + dos_headers->e_lfanew + sizeof(DWORD));
//PIMAGE_FILE_HEADER file_headers_two = (PIMAGE_FILE_HEADER)&nt_headers->FileHeader;

// 遍历标准PE头
PrintfFileHeader(file_headers);

printf("******************OPTION-HEADERS******************\n");

// 遍历可选PE头
//PIMAGE_OPTIONAL_HEADER option_headers = (PIMAGE_OPTIONAL_HEADER)(DWORD*)((BYTE*)fileBuffer + dos_headers->e_lfanew + sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER));
PIMAGE_OPTIONAL_HEADER option_headers_two = (PIMAGE_OPTIONAL_HEADER)&nt_headers->OptionalHeader;
PrintfOptionHeader(option_headers_two);

return 0;
}