文件对齐与内存对齐

程序最初存储在硬盘上,运行时需要加载到内存中。在加载过程中,文件的不同部分会进行重新排列和对齐,以便能够正确运行。视频中硬盘的对齐单位为0x200h,而内存中的对齐单位为0x1000h。这种对齐的差异导致文件在加载到内存中时需要进行“拉伸”处理,使文件能够符合内存的对齐要求,从而正确执行。

image-20241016234330141

内核重载与虚拟地址

程序一般从0x40000的虚拟地址开始执行,这个地址留给系统,增加了程序的安全性。虚拟地址由操作系统映射到实际的物理地址。

然后关于为什么程序一般从0x40000开始呢,视频里说是因为留出空间,如果访问这个内存就会报错,因为这是系统的空间,增加代码的安全性。

内核在加载程序时,会将程序的虚拟地址映射到实际的物理地址。常见的程序起始虚拟地址为0x40000,保留了前面的地址空间用于系统,这样当指针错误时不会访问系统关键区域。

部分PE结构解析:

DOS头

  • WORD e_magic: 固定值为“MZ”,标识这是一个可执行文件。
  • DWORD e_lfanew: PE头相对于文件起始位置的偏移,用于定位PE头的位置。

标准PE头

  • WORD Machine: 表示运行该程序的CPU架构。
  • WORD NumberOfSections: 文件中包含的节(Section)的数量。如果需要增加或合并节,需修改该值。
  • DWORD TimeDateStamp: 文件的时间戳,标记文件创建的时间(与操作系统时间无关)。
  • WORD SizeOfOptionalHeader: 可选PE头的大小,32位文件通常为0xE0,64位文件为0xF0。大小可以自定义。
  • WORD Characteristics: 标记文件的特性,每个位对应不同含义。对于可执行文件,通常值为0x10F。

可选PE头

  • WORD Magic: 文件类型标志。0x10B表示32位PE文件,0x20B表示64位PE文件。
  • DWORD SizeOfCode: 所有代码节的总和,必须是FileAlignment的整数倍。
  • DWORD SizeOfInitializedData: 已初始化数据的总和,必须是FileAlignment的整数倍。
  • DWORD SizeOfUninitializedData: 未初始化数据的总和,必须是FileAlignment的整数倍。
  • DWORD AddressOfEntryPoint: 程序的入口地址,表示程序运行开始的位置。
  • DWORD BaseOfCode: 代码段的起始地址,编译器自动填写。
  • DWORD BaseOfData: 数据段的起始地址,编译器自动填写。
  • DWORD ImageBase: 文件在内存中的镜像基址,即程序加载到内存中后的起始地址。
  • DWORD SectionAlignment: 内存中的对齐大小,通常为0x1000h。
  • DWORD FileAlignment: 文件中的对齐大小,通常为0x200h。
  • DWORD SizeOfImage: 文件映像在内存中的大小,必须是SectionAlignment的整数倍。
  • DWORD SizeOfHeaders: 文件头的大小,包含所有的头部信息和节表,按文件对齐计算。
  • DWORD CheckSum: 校验和,用于判断文件是否被篡改,通常只在系统文件中有要求。
  • DWORD SizeOfStackReserve: 初始化时保留的堆栈大小。
  • DWORD SizeOfStackCommit: 初始化时实际提交的堆栈大小。
  • DWORD SizeOfHeapReserve: 初始化时保留的堆大小。
  • DWORD SizeOfHeapCommit: 初始化时实际提交的堆大小。
  • DWORD NumberOfRvaAndSizes: 指定数据目录的数目。

Work

示例代码

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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <windows.h>


//DOC结构
typedef struct _DOC_HEADER
{
WORD e_magic;
WORD e_cblp;
WORD e_cp;
WORD e_crlc;
WORD e_cparhar;
WORD e_minalloc;
WORD e_maxalloc;
WORD e_ss;
WORD e_sp;
WORD e_csum;
WORD e_ip;
WORD e_cs;
WORD e_lfarlc;
WORD e_ovno;
WORD e_res[4];
WORD e_oemid;
WORD e_oeminfo;
WORD e_res2[10];
DWORD e_lfanew;
}PeDoc;
//FILE结构
typedef struct _STANDARD_PE_HEADER
{
WORD Machine;
WORD NumberOfSections;
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;
WORD Characteristics;
}StandardPeHeader;
//OPTIONAL结构
typedef struct _OPTIONAL_PE_HEADER
{
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
DWORD BaseOfData;
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;
}OptionalPeHeader;

/// <summary>
/// 读取pe数据
/// </summary>
/// <param name="lpszFile">pe文件路径</param>
/// <returns>pe数据数组</returns>
char* ReadPeFile(const char* peFIle)
{
//变量声明
FILE* peFile = fopen(peFIle, "rb"); //pe文件对象
unsigned int peSize = NULL; //pe文件大小
char* peData = nullptr; //pe数据(所有字节)

if (peFile == NULL)
{
printf("文件打开失败!(%d)\n", __LINE__);
goto END;
}

//获取pe文件大小
if (fseek(peFile, 0, SEEK_END) != 0)
{
printf("指针移动失败!(%d)\n", __LINE__);
goto END;
}
peSize = ftell(peFile);
if (peSize == 0)
{
printf("程序没有任何数据!(%d)\n", __LINE__);
goto END;
}

if (fseek(peFile, 0, SEEK_SET) != 0)
{
printf("指针移动失败!(%d)\n", __LINE__);
goto END;
}

//根据pe文件大小开辟内存存放pe数据
peData = (char*)malloc(peSize);
if (peData != NULL)
{
memset(peData, '0', peSize);
//读取pe数据
fread(peData, sizeof(char), peSize, peFile);
}

END:
if (peFile)
fclose(peFile);

return peData;
}

/// <summary>
/// 读取DOC/FILE/OPTIONAL表数据
/// </summary>
/// <param name="peData">pe结构数组头</param>
/// <param name="doc">doc结构指针</param>
/// <param name="standard">file结构指针</param>
/// <param name="optional">optional结构指针</param>
void ReadPeData(char* peData, PeDoc*& doc, StandardPeHeader*& standard, OptionalPeHeader*& optional)
{
doc = (PeDoc*)peData;
peData = &peData[doc->e_lfanew + 4];
standard = (StandardPeHeader*)peData;
peData = &peData[20];
optional = (OptionalPeHeader*)peData;
}

//输出doc数据
void printDoc(PeDoc* doc)
{
printf(">>>> DOC <<<<\n");
printf("e_magic = %x\n", doc->e_magic);
printf("e_cblp = %x\n", doc->e_cblp);
printf("e_cp = %x\n", doc->e_cp);
printf("e_crlc = %x\n", doc->e_crlc);
printf("e_cparhar = %x\n", doc->e_cparhar);
printf("e_minalloc = %x\n", doc->e_minalloc);
printf("e_maxalloc = %x\n", doc->e_maxalloc);
printf("e_ss = %x\n", doc->e_ss);
printf("e_sp = %x\n", doc->e_ss);
printf("e_csum = %x\n", doc->e_csum);
printf("e_ip = %x\n", doc->e_ip);
printf("e_cs = %x\n", doc->e_cs);
printf("e_lfarlc = %x\n", doc->e_lfarlc);
printf("e_ovno = %x\n", doc->e_ovno);
printf("e_res = %x%x%x%x\n", doc->e_res[0], doc->e_res[1], doc->e_res[2], doc->e_res[3]);
printf("e_oemid = %x\n", doc->e_oemid);
printf("e_oeminfo = %x\n", doc->e_oeminfo);
printf("e_res2[10] = %x%x%x%x%x%x%x%x%x%x\n", doc->e_res2[0], doc->e_res2[1], doc->e_res2[2], doc->e_res2[3], doc->e_res2[4], doc->e_res2[5], doc->e_res2[6], doc->e_res2[7], doc->e_res2[8], doc->e_res2[9]);
printf("e_lfanew = %x\n", doc->e_lfanew);
printf(">>>> DOC <<<<\n\n\n");
}
//输出file数据
void printStandar(StandardPeHeader* standard)
{
printf(">>>> STANDARD <<<<\n");
printf("Machine = %x\n", standard->Machine);
printf("NumberOfSections = %x\n", standard->NumberOfSections);
printf("TimeDateStamp = %x\n", standard->TimeDateStamp);
printf("PointerToSymbolTable = %x\n", standard->PointerToSymbolTable);
printf("NumberOfSymbols = %x\n", standard->NumberOfSymbols);
printf("SizeOfOptionalHeader = %x\n", standard->SizeOfOptionalHeader);
printf("Characteristics = %x\n", standard->Characteristics);
printf(">>>> STANDARD <<<<\n\n\n");
}
//输出optional数据
void printOptional(OptionalPeHeader* optional)
{
printf(">>>> OPTIONAL <<<<\n");
printf("Magic = %x\n", optional->Magic);
printf("MajorLinkerVersion = %x\n", optional->MajorLinkerVersion);
printf("MinorLinkerVersion = %x\n", optional->MinorLinkerVersion);
printf("SizeOfCode = %x\n", optional->SizeOfCode);
printf("SizeOfInitializedData = %x\n", optional->SizeOfInitializedData);
printf("SizeOfUninitializedData = %x\n", optional->SizeOfUninitializedData);
printf("AddressOfEntryPoint = %x\n", optional->AddressOfEntryPoint);
printf("BaseOfCode = %x\n", optional->BaseOfCode);
printf("BaseOfData = %x\n", optional->BaseOfData);
printf("ImageBase = %x\n", optional->ImageBase);
printf("SectionAlignment = %x\n", optional->SectionAlignment);
printf("FileAlignment = %x\n", optional->FileAlignment);
printf("MajorOperatingSystemVersion = %x\n", optional->MajorOperatingSystemVersion);
printf("MinorOperatingSystemVersion = %x\n", optional->MinorOperatingSystemVersion);
printf("MajorImageVersion = %x\n", optional->MajorImageVersion);
printf("MinorImageVersion = %x\n", optional->MinorImageVersion);
printf("MajorSubsystemVersion = %x\n", optional->MajorSubsystemVersion);
printf("MinorSubsystemVersion = %x\n", optional->MinorSubsystemVersion);
printf("Win32VersionValue = %x\n", optional->Win32VersionValue);
printf("SizeOfImage = %x\n", optional->SizeOfImage);
printf("SizeOfHeaders = %x\n", optional->SizeOfHeaders);
printf("CheckSum = %x\n", optional->CheckSum);
printf("Subsystem = %x\n", optional->Subsystem);
printf("DllCharacteristics = %x\n", optional->DllCharacteristics);
printf("SizeOfStackReserve = %x\n", optional->SizeOfStackReserve);
printf("SizeOfStackCommit = %x\n", optional->SizeOfStackCommit);
printf("SizeOfHeapReserve = %x\n", optional->SizeOfHeapReserve);
printf("SizeOfHeapCommit = %x\n", optional->SizeOfHeapCommit);
printf("LoaderFlags = %x\n", optional->LoaderFlags);
printf("NumberOfRvaAndSizes = %x\n", optional->NumberOfRvaAndSizes);
printf(">>>> OPTIONAL <<<<\n\n\n");
}

//中控台
void printPeHeaderInfo(const char* peFile)
{
char* peData = ReadPeFile(peFile);
PeDoc* doc = { NULL };
StandardPeHeader* peStandard = { NULL };
OptionalPeHeader* peOptional = { NULL };
ReadPeData(peData, doc, peStandard, peOptional);
printDoc(doc);
printStandar(peStandard);
printOptional(peOptional);
free(peData);
}


int main()
{
printPeHeaderInfo("D:\\PE.Tools.v1.9.762.2018\\PETools.exe");
getchar();
}

运行结果

image-20241014231156720