步骤

  1. 将读取到的文件拉伸到内存,头和节区按照SectionAlignment对齐,如果直接按照文件大小合并,合并为一个节后在内存中没办法对每个节进行拉伸。

头部直接按照SizeOfHeaders写入到ImageBuffer就行了,没必要对齐,因为节区是从VA开始写入的

  1. 将第一个节的内存大小、文件大小改成一样

    Max = SizeOfRawData > VirtualSize ? SizeOfRawData : VirtualSize

    SizeOfRawData = VirtualSize = 最后一个节的VirtualAddress + Max - SizeOfHeaders内存对齐后的大小

  2. 将第一个节的属性修改为包含所有节的属性

  3. 修改节的数量为1

  4. 将ImageBuffer重新构建为FileBuffer

由于拉伸内存时将PointerToRawData写到VirtualAddress,在ImageBuffer中合并节后,第一个节的PointerTorawData实际为VirtualAddress,所以合并节后需要调整下,否则程序无法正常运行。有两种方法,一是直接修改PointerTorawData的值,或者将ImageBuffer重新构建为FileBuffer


实现

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
#include<Windows.h>
#include<stdio.h>
#include<stdlib.h>
#include<PETool.h>
#pragma comment(lib,"PETool.lib")
#define FILEPATH TEXT("D:\\absolution\\temporary\\test.exe")
#define OUTFILEPATH TEXT("D:\\absolution\\temporary\\output.exe")



LPVOID createImageBuffer(LPVOID lpFileBuffer) {
PIMAGE_DOS_HEADER pDos =
(PIMAGE_DOS_HEADER)lpFileBuffer;
PIMAGE_NT_HEADERS pNT =
(PIMAGE_NT_HEADERS)((DWORD)lpFileBuffer + pDos->e_lfanew);
PIMAGE_FILE_HEADER pFile =
(PIMAGE_FILE_HEADER)((DWORD)pNT + 4);
PIMAGE_OPTIONAL_HEADER pOption =
(PIMAGE_OPTIONAL_HEADER)((DWORD)pFile + IMAGE_SIZEOF_FILE_HEADER);
PIMAGE_SECTION_HEADER pFirstSection =
PIMAGE_SECTION_HEADER((DWORD)pOption + pFile->SizeOfOptionalHeader);

/*按照拉伸后的大小申请内存*/
LPVOID lpImageBuffer = malloc(pOption->SizeOfImage);

memset(lpImageBuffer, 0, pOption->SizeOfImage);

/*写入头部,不需要对齐,下面从VA开始写入*/
memcpy(lpImageBuffer, lpFileBuffer, pOption->SizeOfHeaders);

/*遍历写入节区,按照内存对齐写入*/
PIMAGE_SECTION_HEADER pSectionHeader = pFirstSection;
for (int i = 0; i < pFile->NumberOfSections; i++) {
memcpy(
(LPVOID)((DWORD)lpImageBuffer + pSectionHeader->VirtualAddress),
(LPVOID)((DWORD)lpFileBuffer + pSectionHeader->PointerToRawData),
pSectionHeader->Misc.VirtualSize
);
/*指向下一个节表*/
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pSectionHeader + IMAGE_SIZEOF_SECTION_HEADER);
}


return lpImageBuffer;
}

/*合并节*/
LPVOID mergeSection(LPVOID lpImageBuffer) {
PIMAGE_DOS_HEADER pDos =
(PIMAGE_DOS_HEADER)lpImageBuffer;
PIMAGE_NT_HEADERS pNT =
(PIMAGE_NT_HEADERS)((DWORD)lpImageBuffer + pDos->e_lfanew);
PIMAGE_FILE_HEADER pFile =
(PIMAGE_FILE_HEADER)((DWORD)pNT + 4);
PIMAGE_OPTIONAL_HEADER pOption =
(PIMAGE_OPTIONAL_HEADER)((DWORD)pFile + IMAGE_SIZEOF_FILE_HEADER);
PIMAGE_SECTION_HEADER pFirstSection =
(PIMAGE_SECTION_HEADER)((DWORD)pOption + pFile->SizeOfOptionalHeader);

/*获取头内存对齐后的大小*/
DWORD alignHeaderSize = PEAlign(pOption->SizeOfHeaders, pOption->SectionAlignment);
/*获取最后一个节*/
PIMAGE_SECTION_HEADER pLastSection =
PIMAGE_SECTION_HEADER((DWORD)pFirstSection + ((pFile->NumberOfSections - 1) * IMAGE_SIZEOF_SECTION_HEADER));

/*计算最后一个节的大小中更大的一个*/
DWORD max = pLastSection->SizeOfRawData < pLastSection->Misc.VirtualSize ? pLastSection->Misc.VirtualSize : pLastSection->SizeOfRawData;

/*计算整个节区的大小*/
pFirstSection->SizeOfRawData = pFirstSection->Misc.VirtualSize = (DWORD)pLastSection->VirtualAddress + max - alignHeaderSize;

pFirstSection->PointerToRawData = pFirstSection->VirtualAddress;
/*遍历修改第一个节表的属性 包含所有节表的属性*/
PIMAGE_SECTION_HEADER pSectionHeader = pFirstSection;
for (int i = 0; i < pFile->NumberOfSections; i++) {
pFirstSection->Characteristics |= pSectionHeader->Characteristics;
pFirstSection = (PIMAGE_SECTION_HEADER)((DWORD)pFirstSection + 40);
}

/*将节表的数量置1*/
pFile->NumberOfSections = 1;

FILE *fp;
errno_t err = fopen_s(&fp, OUTFILEPATH, "wb+");

fwrite(lpImageBuffer, pOption->SizeOfImage, 1, fp);

fclose(fp);
return NULL;

}

int main() {
/*读取PE文件*/
LPVOID lpFileBuffer = createFileBuffer(FILEPATH);

if (!lpFileBuffer) {
return 0;
}

LPVOID lpImageBuffer = createImageBuffer(lpFileBuffer);


mergeSection(lpImageBuffer);

free(lpImageBuffer);

free(lpFileBuffer);

}

修改完成后