前面提到过,文件从硬盘加载到内存运行时会经过“拉伸”操作,那么在经过简单拉伸之后,程序是否就可以运行呢?答案是否定的。在完成基本拉伸操作后,程序还不能立即运行,还需要进行一些额外的处理。不过此时已经非常接近能够运行了。

Misc.VirtualSize 和 SizeOfRawData 的大小对比

在内存中的节有一个 Misc.VirtualSize 属性,表示在内存中拉伸后的实际大小;而 SizeOfRawData 则表示文件在磁盘上的大小。

这两者的大小通常不同,因为内存中可能包含一些未初始化的数据,而这些数据不会在文件中实际保存,但在加载到内存时会被计算并为其预留空间。因此,通常情况下,Misc.VirtualSize 会大于 SizeOfRawData

总结:

  • Misc.VirtualSize 是加载到内存中时的实际大小,且它是加载之前未对齐的大小。
  • SizeOfRawData 是磁盘文件中已对齐的数据大小。

拷贝文件内容到内存时,应该以两者中较小的数值为准。

image-20241018123256633

如何算出内存中节存储的某个地址在文件中对应的地址

举个例子,假设在某个节中有一个内存地址是 0x501234

  1. 首先,计算该地址相对于基地址的偏移:

    1
    0x501234 - 基地址(0x500000) = 0x1234
  2. 接下来,查看节表信息:

    • 节1:PointerToRawData = 0x400,VirtualAddress = 0x1000
    • 节2:PointerToRawData = 0x600,VirtualAddress = 0x2000
    • 节3:PointerToRawData = 0x800,VirtualAddress = 0x3000
  3. 判断该地址位于哪个节:

    • 地址 0x501234 对应的 RVA 是 0x1234
    • 0x1234 大于节1的 VirtualAddress (0x1000),小于节2的 VirtualAddress (0x2000),因此该地址位于节1。
  4. 计算该地址在节表中的偏移:

    1
    0x501234 - 0x500000 - 0x1000 = 0x234
  5. 最终,文件中的实际地址就是:

    1
    文件中的第一个节的 PointerToRawData + 0x234 = 0x400 + 0x234 = 文件中的地址 0x634

补充:

  • RVA(Relative Virtual Address):相对于内存中基地址的偏移,指的是内存中的虚拟地址。
  • FOA(File Offset Address):文件中的实际偏移地址,即文件中某个节在硬盘上存储的实际位置。

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
#include <iostream>
#include <windows.h>
#include "malloc.h"
#include <malloc.h>

//变量声明
PIMAGE_DOS_HEADER dosHeader = nullptr; //DOS结构
PIMAGE_FILE_HEADER fileHeader = nullptr; //FILE结构
PIMAGE_OPTIONAL_HEADER32 optionalHeader = nullptr; //OPTIONAL结构
PIMAGE_SECTION_HEADER* sectionArr = nullptr; //SECTION数组
LPVOID MemoryData = nullptr; //拉伸后的pe结构

//读取程序数据
LPVOID ReadProgramData(LPCSTR programPath)
{
FILE* program = nullptr;
size_t size = NULL;
LPVOID data = nullptr;

//打开程序
fopen_s(&program, programPath, "rb");
if (program == nullptr)
{
printf("failed to open by program!\n");
goto END;
}

//获取程序字节大小
if (fseek(program, 0, SEEK_END) == 0)
{
size = ftell(program);
if (fseek(program, 0, SEEK_SET) != 0)
{
printf("failed to move the pointer to begin!\n");
goto END;
}
}
else
{
printf("failed to move the pointer to end!\n");
goto END;
}

//申请内存空间存储数据
data = malloc(size);
if (data != nullptr)
{
memset(data, '\0', size);
fread_s(data, size, 1, size, program);
}
else
{
printf("failed to apply by memory\n");
goto END;
}

END:
if (program)
fclose(program);

return data;
}

//解析pe结构
void AnalyzePeStruct(PCHAR fileData, PIMAGE_DOS_HEADER& dos, PIMAGE_FILE_HEADER& file, PIMAGE_OPTIONAL_HEADER32& optional, PIMAGE_SECTION_HEADER*& section)
{
//解析DOS结构
dos = (PIMAGE_DOS_HEADER)fileData;
fileData = &fileData[dos->e_lfanew + 4];

//解析FILE结构
file = (PIMAGE_FILE_HEADER)fileData;
fileData = &fileData[20];

//解析OPTIONAL结构
optional = (PIMAGE_OPTIONAL_HEADER32)fileData;
fileData = &fileData[file->SizeOfOptionalHeader];

//解析节表
section = (PIMAGE_SECTION_HEADER*)malloc(file->NumberOfSections * sizeof(IMAGE_SECTION_HEADER));
if (section != nullptr)
{
for (size_t index = 0; index < file->NumberOfSections; index++)
{
section[index] = (PIMAGE_SECTION_HEADER)fileData;
fileData = &fileData[40];
}
}
}

//缩小pe结构
LPVOID ShrinkData(LPVOID memoryData)
{
LPVOID fileData = nullptr;
LPVOID tempMemoryDataPointer = nullptr;
LPVOID tempFileDataPointer = nullptr;
int copyCharNumber = NULL;

//计算硬盘pe结构大小
size_t fileDataSize = optionalHeader->SizeOfHeaders;
for (size_t index = 0; index < fileHeader->NumberOfSections; index++)
{
fileDataSize += sectionArr[index]->SizeOfRawData;
}

//申请内存空间
fileData = malloc(fileDataSize);
if (fileData != nullptr)
{
memset(fileData, '\0', fileDataSize);
tempMemoryDataPointer = memoryData;
tempFileDataPointer = fileData;
copyCharNumber = optionalHeader->FileAlignment;

while (copyCharNumber < optionalHeader->SizeOfHeaders)
{
copyCharNumber *= 2;
}
//写入所有头+节表
memcpy_s(tempFileDataPointer, copyCharNumber, tempMemoryDataPointer, optionalHeader->SizeOfHeaders);
//写入节区
for (size_t index = 0; index < fileHeader->NumberOfSections; index++)
{
copyCharNumber = optionalHeader->FileAlignment;
tempFileDataPointer = (LPVOID)((UINT_PTR)fileData + sectionArr[index]->PointerToRawData);
tempMemoryDataPointer = (LPVOID)((UINT_PTR)memoryData + sectionArr[index]->VirtualAddress);
while (copyCharNumber < sectionArr[index]->SizeOfRawData)
{
copyCharNumber *= 2;
}
memcpy_s(tempFileDataPointer, copyCharNumber, tempMemoryDataPointer, sectionArr[index]->SizeOfRawData);
}
}
return fileData;
}

//拉伸pe结构
LPVOID StretchData(PCHAR fileData)
{
LPVOID tempMemoryDataPointer = nullptr; //拉伸后的pe结构指针(用于指向写入的位置)
PCHAR tempFileDataPointer = nullptr; //拉伸前的pe结构指针(用于指向读取的位置)
int copyCharNumber = NULL; //内存对其

//读取pe结构
AnalyzePeStruct(fileData, dosHeader, fileHeader, optionalHeader, sectionArr);
//申请内存pe结构空间
MemoryData = malloc(optionalHeader->SizeOfImage);
if (MemoryData != nullptr)
{
memset(MemoryData, '\0', optionalHeader->SizeOfImage);
tempMemoryDataPointer = MemoryData;
tempFileDataPointer = fileData;
copyCharNumber = optionalHeader->SectionAlignment;

//写入所有头+节表
while (copyCharNumber < optionalHeader->SizeOfHeaders)
{
copyCharNumber *= 0x2;
}//这里循环的意义是为了保证大小是SectionAliment的整数倍,内存对齐
memcpy_s(tempMemoryDataPointer, copyCharNumber, tempFileDataPointer, optionalHeader->SizeOfHeaders);

//写入节区
for (size_t index = 0; index < fileHeader->NumberOfSections; index++)
{
copyCharNumber = optionalHeader->SectionAlignment;
tempMemoryDataPointer = (LPVOID)((UINT_PTR)MemoryData + sectionArr[index]->VirtualAddress);
tempFileDataPointer = (PCHAR)((UINT_PTR)fileData + sectionArr[index]->PointerToRawData);
while (copyCharNumber < sectionArr[index]->SizeOfRawData)
{
copyCharNumber *= 2;
}
memcpy_s(tempMemoryDataPointer, copyCharNumber, tempFileDataPointer, sectionArr[index]->SizeOfRawData);
}
}

return MemoryData;
}

//写入缩小后的pe结构
bool WriteMyFilePeData(LPVOID fileData, LPCSTR newProgramPath)
{
FILE* newProgram = nullptr;
bool result = true;
size_t size = NULL;

//打开文件
fopen_s(&newProgram, newProgramPath, "wb");
if (newProgram == nullptr)
{
printf("failed to create by program!\n");
result = false;
goto END;
}

size = _msize(fileData);
fwrite(fileData, 1, size, newProgram);



END:
if (newProgram)
fclose(newProgram);

return result;
}

int main()
{
PCHAR fileData = (PCHAR)ReadProgramData("D:\\c\\0.exe");
LPVOID memoryData = StretchData(fileData);
LPVOID myFileData = ShrinkData(memoryData);
WriteMyFilePeData(myFileData, "D:\\c\\0.exe");
getchar();
free(memoryData);
free(myFileData);
free(fileData);
return 0;
}