步骤

扩大节区会修改原本的节表属性,新增节不会,通常大部分加壳软件都会新增节来移动或者备份各种目录项数据

新增一个0x1000字节大小的节区

  1. 判断头中是否有空间存下一个节表,为了便于验证默认空间不足。最后一个节表后面必须跟40个字节0,所以必须有大于80个字节的空间
  2. 抹除DOS Stub,将PE标识开始移动到DOS Stub处
  3. 修改IMAGE_DOS_HEADER.e_lfanew 为0x40(DOS Stub开始处)
  4. 在最后一个节表的结尾处填充刚才删除的字节数(IMAGE_NT_HEADERS.Signature的地址 - 0x40 = 填充字节数),一定要在节表的末尾处填充,只能移动头部,节区是不能移动的。
  5. 在最后一个节表后添加一个新的节表,将其VirtualSize和SizeOfRawData修改为0x1000,将其PointerToRawData修改为上一个节的SizeOfRawData + PointerToRawData(结果也就是PE文件结尾处)。
    将其VirtualAddress修改为上一个节的VirtualAddress + VirtualSize,结果按照内存对齐
  6. 修改IMAGE_FILE_HEADER.NumberOfSections
  7. 修改IMAGE_OPTIONAL_HEADER.SizeOfImage
  8. 修改节区包含代码段和可执行属性,修改IMAGE_SECTION_HEADER.Characteristics,将节的IMAGE_SCN_CNT_CODE和IMAGE_SCN_MEM_EXECUTE标志位置1。

新增节不需要拉伸,直接通过最后一个节区就可以计算出新节地址


实现

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

/*功能函数,抹除DOS Stub用于新增节*/
BOOL deleteDOSStub(LPVOID lpFileBuffer) {
PIMAGE_DOS_HEADER pDOS = (PIMAGE_DOS_HEADER)lpFileBuffer;
PIMAGE_NT_HEADERS pNT = (PIMAGE_NT_HEADERS)((DWORD)pDOS + pDOS->e_lfanew);
PIMAGE_FILE_HEADER pFile = (PIMAGE_FILE_HEADER)((DWORD)pNT + 4);
PIMAGE_OPTIONAL_HEADER pOptional = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFile + 20);
PIMAGE_SECTION_HEADER pSections = (PIMAGE_SECTION_HEADER)((DWORD)pOptional + pFile->SizeOfOptionalHeader);
//定位到最后一个节头
PIMAGE_SECTION_HEADER pLastSection = (PIMAGE_SECTION_HEADER)((DWORD)pSections + (pFile->NumberOfSections - 1) * 40);


/*
*****************************
抹除DOS Stub用于新增节表
*/

/*计算节表后的空白区,为了便于验证不作判断*/
DWORD dwSpace = ((DWORD)pDOS + pOptional->SizeOfHeaders) - ((DWORD)pLastSection + 40);

/*计算移动数据的大小(PE头到最后一个节表的结尾处)*/
DWORD dwMoveSize = ((DWORD)pSections + (pFile->NumberOfSections) * 40) - (DWORD)pNT;
/*分配临时内存,储存移动数据*/
LPVOID lpTemp = malloc(dwMoveSize);
memset(lpTemp, 0, dwMoveSize);

/*备份数据*/
memcpy(lpTemp, pNT, dwMoveSize);

/*清空原本PE头*/
memset(pNT, 0, dwMoveSize);

/*在原本DOS Stub开头处写入PE头到最后一个节表结束的数据
IMAGE_DOS_HEADER占64字节,偏移64字节就是DOS Stub*/
memcpy((LPVOID)(DWORD(pDOS) + 64), lpTemp, dwMoveSize);

/*修正PE头偏移(DOS头占64字节,后面紧跟PE头)*/
pDOS->e_lfanew = 0x40;

free(lpTemp);

return TRUE;

}

/*
新增节表并修改属性*/
LPVOID addNewSection(LPVOID lpFileBuffer, DWORD size) {

deleteDOSStub(lpFileBuffer);

PIMAGE_DOS_HEADER pDOS = (PIMAGE_DOS_HEADER)lpFileBuffer;
PIMAGE_NT_HEADERS pNT = (PIMAGE_NT_HEADERS)((DWORD)pDOS + pDOS->e_lfanew);
PIMAGE_FILE_HEADER pFile = (PIMAGE_FILE_HEADER)((DWORD)pNT + 4);
PIMAGE_OPTIONAL_HEADER pOptional = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFile + 20);
PIMAGE_SECTION_HEADER pSections = (PIMAGE_SECTION_HEADER)((DWORD)pOptional + pFile->SizeOfOptionalHeader);
//定位到最后一个节头
PIMAGE_SECTION_HEADER pLastSection = (PIMAGE_SECTION_HEADER)((DWORD)pSections + (pFile->NumberOfSections - 1) * 40);

/*新增节的地址*/
PIMAGE_SECTION_HEADER pNewSection = PIMAGE_SECTION_HEADER((DWORD)pLastSection + 40);


memcpy(pNewSection, pLastSection, 40);

/*增加节的记数*/
pFile->NumberOfSections = pFile->NumberOfSections + 1;

/*修改内存和文件对齐后的大小*/
pNewSection->SizeOfRawData = PEAlign(size, pOptional->FileAlignment);
pNewSection->Misc.VirtualSize = PEAlign(size, pOptional->SectionAlignment);

/*修改地址*/
pNewSection->PointerToRawData =
PEAlign((pLastSection->PointerToRawData + pLastSection->SizeOfRawData), pOptional->FileAlignment);
pNewSection->VirtualAddress =
PEAlign((pLastSection->VirtualAddress + pLastSection->Misc.VirtualSize), pOptional->SectionAlignment);


/*修改包含代码段和可执行属性*/
pNewSection->Characteristics =
(pNewSection->Characteristics | IMAGE_SCN_CNT_CODE) | IMAGE_SCN_MEM_EXECUTE;

/*修改内存大小*/
pOptional->SizeOfImage =
PEAlign((pOptional->SizeOfImage + pNewSection->Misc.VirtualSize), pOptional->SectionAlignment);

/*申请新内存存放修改后的PE文件*/
LPVOID lpTemp = malloc(pNewSection->PointerToRawData + pNewSection->SizeOfRawData);
memset(lpTemp, 0, pNewSection->PointerToRawData + pNewSection->SizeOfRawData);
memcpy(lpTemp, lpFileBuffer, pNewSection->PointerToRawData);

return lpTemp;
}

int main() {
LPVOID lpFileBuffer = NULL;
lpFileBuffer = createFileBuffer(DLLPATH);
PIMAGE_SECTION_HEADER lpNewSection = NULL;

LPVOID lpModifyBuffer = (PIMAGE_SECTION_HEADER)addNewSection(lpFileBuffer, 0x1000);

PIMAGE_DOS_HEADER pDOS = (PIMAGE_DOS_HEADER)lpModifyBuffer;
PIMAGE_NT_HEADERS pNT = (PIMAGE_NT_HEADERS)((DWORD)pDOS + pDOS->e_lfanew);
PIMAGE_FILE_HEADER pFile = (PIMAGE_FILE_HEADER)((DWORD)pNT + 4);
PIMAGE_OPTIONAL_HEADER pOptional = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFile + 20);
PIMAGE_SECTION_HEADER pSections = (PIMAGE_SECTION_HEADER)((DWORD)pOptional + pFile->SizeOfOptionalHeader);
//定位到新增节
PIMAGE_SECTION_HEADER pNewSection = (PIMAGE_SECTION_HEADER)((DWORD)pSections + (pFile->NumberOfSections - 1) * 40);


FILE *fp;
fopen_s(&fp, OUTFILEPATH, "wb+");
if (!fp) {
printf("fopen error\n");
getchar();
return 0;
}
fseek(fp, 0, SEEK_SET);

DWORD count=fwrite(lpModifyBuffer, pNewSection->PointerToRawData+pNewSection->SizeOfRawData,1, fp);

if (count) {
printf("写入成功!!\n");
getchar();
}



fclose(fp);

free(lpFileBuffer);
free(lpModifyBuffer);

}

节表的两个偏移和大小要按照拓展PE头对齐,否则是无法正常执行的。