为什么需要移动各种表?

  1. 表的重要性
    这些表由编译器生成,存储了程序运行过程中不可或缺的信息。
  2. 初始化的作用
    系统在程序启动时,会依据这些表完成初始化,例如将使用的 DLL 中函数的地址加载到 IAT 表中。
  3. 保护程序的需求
    为了保护程序,可以对 .exe 的二进制代码进行加密。但问题在于,各种表和用户代码及数据混合在一起。加密后,系统在初始化时会因无法正确解析表信息而出错。

总结:

掌握表的移动技巧,是程序加密与反破解的基础。

移动导出表

通常步骤:新增一个节(Section),并将表移动到新节。移动后需要重新计算 ROV。

具体操作步骤:

  1. 新增节
    在 DLL 中创建一个新的节,记录新增后的 FOA。
  2. **复制 AddressOfFunctions**:
    长度:4 × NumberOfFunctions
  3. **复制 AddressOfNameOrdinals**:
    长度:2 × NumberOfNames
  4. **复制 AddressOfNames**:
    长度:4 × NumberOfNames
  5. 复制函数名字符串
    长度不固定,在复制时直接修复 AddressOfNames
  6. 复制 IMAGE_EXPORT_DIRECTORY 结构
  7. 修复 IMAGE_EXPORT_DIRECTORY 中的字段
    • AddressOfFunctions
    • AddressOfNameOrdinals
    • AddressOfNames
  8. 更新目录项中的值
    指向新的 IMAGE_EXPORT_DIRECTORY

为什么函数名字符串也要移动?

很多人会有疑问,为什么要移动函数名字符串?不解密 DLL 后直接使用不行吗?以下是原因:

  1. 调用依赖函数名
    外部调用 DLL 时,需要提前知道函数名,而加密后的 DLL 在解密前无法直接访问其内部内容。

  2. 调用示例

    使用导出函数的代码可能如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #include <iostream>
    #include <windows.h>
    using namespace std;
    int main()
    {
    HMODULE h = LoadLibrary(L"reloca.dll");
    typedef void(*PFUNC)();
    PFUNC my_func = (PFUNC)GetProcAddress(h, "Print");
    my_func();
    return 0;
    }

    以上代码在调用 GetProcAddress 时,依赖于函数名字符串。因此,即使 DLL 解密成功,解密前调用者仍需要通过函数名找到目标函数。

结论:

函数名字符串必须提前妥善移动,以确保解密前调用过程不出问题。

移动重定位表

  1. 移动方法
    将重定位表原样复制到新的节中,并修改数据目录的偏移。
  2. 修改节属性
    与移动导出表类似,调整新增节的属性,使之符合预期。
  3. 保持原内容
    保持重定位表原始内容不变,只需更新相关指针和偏移即可。

Work:移动导出表-重定位表

示例代码

PE.h

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
#pragma once
#include <iostream>
#include <windows.h>


//* 作用:读取PE数据
// lpszName: 文件路径
//* 返回:PE数据指针
extern "C" LPVOID ReadPE(
IN LPCSTR lpszName
);


//* 作用:数据对其
// n: 数据大小
// alignment: 对其大小
//* 返回:PE数据指针
size_t Align(
int n,
int alignment
);

//* 作用:拉伸PE
// pe: 文件PE指针
//* 返回:拉伸后的PE指针(内存中的PE)
LPVOID StretchPE(
IN LPVOID pe
);

//* 作用:收缩PE
// pe: 内存PE指针
//* 返回:收缩后的PE指针(硬盘中的PE)
LPVOID ShrinkPE(
IN LPVOID pe
);

//* 作用:解析PE
// pe: PE指针
// pDosHeader: DOS结构体指针
// pFileHeader: FILE结构体指针
// pOptionalHeader: OPTIONAL结构体指针
// pSectionArr: SECTION结构体数组指针
//* 返回:无
void AnlyzePE(
IN LPVOID pe,
OUT PIMAGE_DOS_HEADER& pDosHeader,
OUT PIMAGE_FILE_HEADER& pFileHeader,
OUT PIMAGE_OPTIONAL_HEADER32& pOptionalHeader,
OUT PIMAGE_SECTION_HEADER*& pSectionArr
);

//* 作用:内存偏移转文件偏移
// pe: PE指针
// rva: 地址在内存中的偏移
//* 返回:文件偏移(失败返回-1)
int RvaToFoa(
IN LPVOID pe,
IN UINT_PTR rva
);


//* 作用:文件偏移转内存偏移
// pe: PE指针
// foa: 地址在文件中的偏移
//* 返回:内存偏移(失败返回-1)
int FoaToRva(
IN LPVOID pe,
IN DWORD foa
);

//* 作用:打印导出表
// pe: PE指针
//* 返回:无
void PrintExport(
IN LPVOID pe
);

//* 作用:字符串比较
// s1: 字符串1
// s2: 字符串2
//* 返回:比较结果(成功返回true,失败返回false)
bool M_strcmp(
IN char* s1,
IN char* s2
);

//* 作用:根据名称获取函数地址
// pe: PE指针
// funcName: 函数名称
//* 返回:函数地址
LPVOID GetFunctionAddrByName(
IN LPVOID pe,
IN LPCSTR funcName
);

//* 作用:根据序号获取函数地址
// pe: PE指针
// exportNumber: 导出序号
//* 返回:函数地址
LPVOID GetFunctionAddrByOrdinal(
IN LPVOID pe,
IN DWORD exportNumber
);

//* 作用:添加节区
// pe: PE指针
// size: 节区大小
//* 返回:添加结果(成功返回true, 失败返回false)
bool CreateSection(
IN LPVOID& pe,
IN size_t size
);

//* 作用:将PE数据写入到文件中
// pe: PE指针
// size: 节区大小
//* 返回:写入结果(成功返回true, 失败返回false)
bool WriteMyFilePeData(
IN LPVOID pe,
IN LPCSTR filePath
);

//* 作用:扩容最后一个节区
// pe: PE指针
// size: 扩容大小
//* 返回:扩容后的PE指针
LPVOID SectionExpand(
IN LPVOID pe,
IN size_t size
);

//* 作用:移动重定位表
// pe: PE指针
// index: 移动节区的下标(1开始)
//* 返回:移动结果(成功返回true, 失败返回false)
bool RelocMove(
IN LPVOID pe,
IN int index = -1
);

//* 作用:设置内存镜像基址
// pe: PE指针
// imageBase: 新的内存镜像基址
//* 返回:修改结果(成功返回true, 失败返回false)
bool SetImageBase(
IN LPVOID pe,
IN DWORD imageBase
);

//* 作用:移动导出表
// pe: PE指针
// index: 移动节区的下标(1开始)
//* 返回:移动结果(成功返回true, 失败返回false)
bool ExportTableMove(
IN LPVOID pe,
IN int index = -1
);

PE.cpp

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
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
#include "PE.h"


LPVOID ReadPE(
IN LPCSTR lpszName
) {
//打开文件
FILE* file = nullptr;
fopen_s(&file, lpszName, "rb");
if (!file)
{
printf("打开文件失败!\n");
return nullptr;
}

//获取文件大小
fseek(file, 0, SEEK_END);
size_t size = ftell(file);
fseek(file, 0, SEEK_SET);

//申请内存
LPVOID fileBuff = malloc(size);
if (!fileBuff)
{
printf("申请内存空间失败!\n");
fclose(file);
return nullptr;
}
//将数据写入申请的内存中
fread_s(fileBuff, size, 1, size, file);

//判断头两个字节是不是MZ
WORD mz = *((PWORD)fileBuff);
if (mz != 0x5a4d)
{
printf("该文件不是pe可执行程序!\n");
fclose(file);
free(fileBuff);
return nullptr;
}

return fileBuff;
}

size_t Align(
int n,
int alignment
) {
int result = alignment;
while (result < n)
{
result += alignment;
}
return result;
}

//* 拉伸PE
LPVOID StretchPE(
IN LPVOID pe
) {
PIMAGE_DOS_HEADER dosHeader = nullptr;
PIMAGE_FILE_HEADER fileHeader = nullptr;
PIMAGE_OPTIONAL_HEADER32 opHeader = nullptr;
PIMAGE_SECTION_HEADER* sectTab = nullptr;
AnlyzePE(pe, dosHeader, fileHeader, opHeader, sectTab);

/*>>>> 核心代码 <<<<*/
LPVOID imageBuff = malloc(opHeader->SizeOfImage); //申请内存空间
if (imageBuff) //如果申请成功
{
//将申请的内存空间清零
memset(imageBuff, 0, opHeader->SizeOfImage);
//获取所有头内存对其后的大小
size_t headerAlign = Align(opHeader->SizeOfHeaders, opHeader->SectionAlignment);
//将文件对其的所有头/表复制到内存中
memcpy_s(imageBuff, headerAlign, pe, opHeader->SizeOfHeaders);
//将每个节区依次复制到内存中
for (DWORD i = 0; i < fileHeader->NumberOfSections; i++)
{
PIMAGE_SECTION_HEADER section = (*sectTab + i);
memcpy_s(
(PCHAR)imageBuff + section->VirtualAddress,
(size_t)((PCHAR)pe + section->SizeOfRawData),
(PCHAR)pe + section->PointerToRawData,
(size_t)((PCHAR)pe + section->SizeOfRawData)
);
}
}
/*>>>> 核心代码 <<<<*/
free(sectTab);
return imageBuff;
}

//收缩PE
LPVOID ShrinkPE(
IN LPVOID pe
) {
PIMAGE_DOS_HEADER dosHeader = nullptr;
PIMAGE_FILE_HEADER fileHeader = nullptr;
PIMAGE_OPTIONAL_HEADER32 opHeader = nullptr;
PIMAGE_SECTION_HEADER* sectTab = nullptr;
AnlyzePE(pe, dosHeader, fileHeader, opHeader, sectTab);

/*>>>> 核心代码 <<<<*/
//获取要申请的空间大小(文件PE的大小)
size_t size = opHeader->SizeOfHeaders;
for (DWORD i = 0; i < fileHeader->NumberOfSections; i++)
{
size += (*sectTab + i)->SizeOfRawData;
}
//申请文件PE内存空间
LPVOID fileBuff = malloc(size);
if (fileBuff)
{
memset(fileBuff, 0, size);
//将内存PE的所有头/表复制到文件PE内存空间
memcpy_s(fileBuff, opHeader->SizeOfHeaders, pe, opHeader->SizeOfHeaders);
//依次复制每一个节区到文件PE内存空间
for (DWORD i = 0; i < fileHeader->NumberOfSections; i++)
{
PIMAGE_SECTION_HEADER section = (*sectTab + i);
memcpy_s(
(PCHAR)fileBuff + section->PointerToRawData,
section->SizeOfRawData,
(PCHAR)pe + section->VirtualAddress,
section->Misc.VirtualSize
);
}
}
/*>>>> 核心代码 <<<<*/
free(sectTab);
return fileBuff;
}

//解析PE
void AnlyzePE(
IN LPVOID pe,
OUT PIMAGE_DOS_HEADER& pDosHeader,
OUT PIMAGE_FILE_HEADER& pFileHeader,
OUT PIMAGE_OPTIONAL_HEADER32& pOptionalHeader,
OUT PIMAGE_SECTION_HEADER*& pSectionArr
) {

//解析DOS
pDosHeader = (PIMAGE_DOS_HEADER)pe;
//解析标准PE头(+4 跳过PE标识)
pFileHeader = (PIMAGE_FILE_HEADER)((PCHAR)pe + pDosHeader->e_lfanew + 4);
//解析可选PE头(+20 跳过标准PE头)
pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((PCHAR)pe + pDosHeader->e_lfanew + 4 + 20);
//为节表数组申请内存空间
pSectionArr = (PIMAGE_SECTION_HEADER*)malloc(pFileHeader->NumberOfSections * sizeof(IMAGE_SECTION_HEADER));
if (pSectionArr != nullptr)
{
//依次解析每一个节表
for (int i = 0; i < pFileHeader->NumberOfSections; i++)
{
*(pSectionArr + i) = (PIMAGE_SECTION_HEADER)((PCHAR)pe + pDosHeader->e_lfanew + 4 + 20 + pFileHeader->SizeOfOptionalHeader + (i * sizeof(IMAGE_SECTION_HEADER)));
}
}
}

//内存地址转文件偏移
int RvaToFoa(
IN LPVOID pe,
IN UINT_PTR rva
) {
PIMAGE_DOS_HEADER dos = nullptr;
PIMAGE_FILE_HEADER file = nullptr;
PIMAGE_OPTIONAL_HEADER32 optional = nullptr;
PIMAGE_SECTION_HEADER* section = nullptr;
AnlyzePE(pe, dos, file, optional, section);
DWORD foa = -1;

/*>>>>核心代码<<<<*/
for (WORD i = 0; i < file->NumberOfSections; i++)
{
//获取节区在内存中的开始地址和结束地址
UINT_PTR begin = (*section + i)->VirtualAddress;
UINT_PTR end = (*section + i)->VirtualAddress + (*section + i)->SizeOfRawData;
//如果内存地址在节区中
if (begin <= rva && rva <= end)
{
//转为文件偏移
foa = rva - begin + (*section + i)->PointerToRawData;
break;
}
}
/*>>>>核心代码<<<<*/
free(section);
return foa;
}

//文件偏移转内存地址
int FoaToRva(
IN LPVOID pe,
IN DWORD foa
) {
PIMAGE_DOS_HEADER dos = nullptr;
PIMAGE_FILE_HEADER file = nullptr;
PIMAGE_OPTIONAL_HEADER32 optional = nullptr;
PIMAGE_SECTION_HEADER* section = nullptr;
AnlyzePE(pe, dos, file, optional, section);
DWORD rva = -1;

for (WORD i = 0; i < file->NumberOfSections; i++)
{
DWORD begin = (*section + i)->PointerToRawData;
DWORD end = (*section + i)->PointerToRawData + (*section + i)->SizeOfRawData;
if (foa >= begin && foa <= end)
{
rva = foa - begin + (*section + i)->VirtualAddress;
break;
}
}
return rva;
}

//打印导出表
void PrintExport(
IN LPVOID pe
) {
PIMAGE_DOS_HEADER dos = nullptr;
PIMAGE_FILE_HEADER file = nullptr;
PIMAGE_OPTIONAL_HEADER32 optional = nullptr;
PIMAGE_SECTION_HEADER* section = nullptr;
AnlyzePE(pe, dos, file, optional, section);

DWORD offset = RvaToFoa(pe, optional->DataDirectory[0].VirtualAddress);
PIMAGE_EXPORT_DIRECTORY exportTable = (PIMAGE_EXPORT_DIRECTORY)((PCHAR)pe + offset);
printf(">>>> 导出表 <<<<\n");
printf("Characteristics =%x\n", exportTable->Characteristics);
printf("TimeDateStamp =%x\n", exportTable->TimeDateStamp);
printf("MajorVersion =%x\n", exportTable->MajorVersion);
printf("MinorVersion =%x\n", exportTable->MinorVersion);
printf("Name =%x\n", exportTable->Name);
printf("Base =%x\n", exportTable->Base);
printf("NumberOfFunctions =%x\n", exportTable->NumberOfFunctions);
printf("NumberOfNames =%x\n", exportTable->NumberOfNames);
printf("AddressOfFunctions =%x\n", exportTable->AddressOfFunctions);
printf("AddressOfNames =%x\n", exportTable->AddressOfNames);
printf("AddressOfNameOrdinals =%x\n", exportTable->AddressOfNameOrdinals);

DWORD(*function)[1];
function = (DWORD(*)[1])((PCHAR)pe + RvaToFoa(pe, exportTable->AddressOfFunctions));
printf(">>>> Functions <<<<\n");
for (DWORD i = 0; i < exportTable->NumberOfFunctions; i++)
{
printf("%d = %x\n", i, *(*(function)+i));
}

WORD(*ordinal)[1];
ordinal = (WORD(*)[1])((PCHAR)pe + RvaToFoa(pe, exportTable->AddressOfNameOrdinals));
printf(">>>> Ordinals <<<<\n");
for (DWORD i = 0; i < exportTable->NumberOfFunctions; i++)
{
printf("%d = %x\n", i, *(*(ordinal)+i));
}

DWORD(*name)[1];
name = (DWORD(*)[1])((PCHAR)pe + RvaToFoa(pe, exportTable->AddressOfNames));
printf(">>>> Names <<<<\n");
for (DWORD i = 0; i < exportTable->NumberOfFunctions; i++)
{
printf("%d = %s\n", i, (PCHAR)pe + RvaToFoa(pe, *(*(name)+i)));
}
free(section);
return;
}

//字符串比较
bool M_strcmp(
IN char* s1,
IN char* s2
) {
int length = strlen(s1);
if (length != strlen(s2))
{
return false;
}
else
{
for (int i = 0; i < length; i++)
{
if (s1[i] != s2[i])
{
return false;
}
}
}
return true;
}

//根据名称获取函数地址
LPVOID GetFunctionAddrByName(
IN LPVOID pe,
IN LPCSTR funcName
) {
PIMAGE_DOS_HEADER dos = nullptr;
PIMAGE_FILE_HEADER file = nullptr;
PIMAGE_OPTIONAL_HEADER32 optional = nullptr;
PIMAGE_SECTION_HEADER* section = nullptr;
AnlyzePE(pe, dos, file, optional, section);

//计算导出表在文件PE中的偏移
DWORD offset = RvaToFoa(pe, optional->DataDirectory[0].VirtualAddress);
//获取导出表
PIMAGE_EXPORT_DIRECTORY exportTable = (PIMAGE_EXPORT_DIRECTORY)((PCHAR)pe + offset);

//四字节数组指针
DWORD(*function)[1];
//指针指向导出表的子表"函数地址表"
function = (DWORD(*)[1])((PCHAR)pe + RvaToFoa(pe, exportTable->AddressOfFunctions));
//二字节数组指针
WORD(*ordinal)[1];
//指针指向导出表的子表"序号表"
ordinal = (WORD(*)[1])((PCHAR)pe + RvaToFoa(pe, exportTable->AddressOfNameOrdinals));
//四字节数组指针
DWORD(*name)[1];
//指针指向导出表的子表"函数名称表"
name = (DWORD(*)[1])((PCHAR)pe + RvaToFoa(pe, exportTable->AddressOfNames));
/*>>>> 关键代码 <<<<*/
//根据函数地址数量进行循环
for (DWORD i = 0; i < exportTable->NumberOfFunctions; i++)
{
//根据当前索引获取"函数名称表"中对应的名称
LPCSTR tempName = (PCHAR)pe + RvaToFoa(pe, *(*(name)+i));
//如果名称与寻找的名称相同
if (M_strcmp((char*)tempName, (char*)funcName))
{
//根据当前索引获取"序号表"中对应的数值
DWORD funcIndex = *(*(ordinal)+i);
free(section);
//返回函数地址
return (LPVOID) * (*(function)+funcIndex);
}
}
/*>>>> 关键代码 <<<<*/
free(section);
return nullptr;
}

//根据导出序号获取函数地址
LPVOID GetFunctionAddrByOrdinal(
IN LPVOID pe,
IN DWORD exportNumber
) {
PIMAGE_DOS_HEADER dos = nullptr;
PIMAGE_FILE_HEADER file = nullptr;
PIMAGE_OPTIONAL_HEADER32 optional = nullptr;
PIMAGE_SECTION_HEADER* section = nullptr;
AnlyzePE(pe, dos, file, optional, section);

//获取导出表在文件PE中的偏移
DWORD offset = RvaToFoa(pe, optional->DataDirectory[0].VirtualAddress);
//获取导出表
PIMAGE_EXPORT_DIRECTORY exportTable = (PIMAGE_EXPORT_DIRECTORY)((PCHAR)pe + offset);

//四字节数组指针指向"函数地址表"
DWORD(*function)[1];
function = (DWORD(*)[1])((PCHAR)pe + RvaToFoa(pe, exportTable->AddressOfFunctions));

free(section);
//根据(寻找的序号 - 序号表的基序号)在"函数地址表"中寻找对应的地址返回
return (LPVOID) * (*(function)+(exportNumber - exportTable->Base));
}

bool CreateSection(
IN LPVOID& pe,
IN size_t size
) {
PIMAGE_DOS_HEADER dos = nullptr;
PIMAGE_FILE_HEADER file = nullptr;
PIMAGE_OPTIONAL_HEADER32 optional = nullptr;
PIMAGE_SECTION_HEADER* section = nullptr;
AnlyzePE(pe, dos, file, optional, section);

//拉升除DOS头以外的头/表
size_t ntHeaderAndSection = 24 + file->SizeOfOptionalHeader + (40 * file->NumberOfSections);
size_t allHeaderOrSection = 64 + ntHeaderAndSection;
memcpy_s(((PCHAR)pe) + 64, _msize(pe), ((PCHAR)file) - 4, ntHeaderAndSection);
//修改NT头偏移
dos->e_lfanew = 64;

free(section);
section = NULL;
AnlyzePE(pe, dos, file, optional, section);
//将残留数据清零
memset((PCHAR)pe + allHeaderOrSection, 0, optional->SizeOfHeaders - allHeaderOrSection);

//修改节表数量
file->NumberOfSections++;
//修改内存大小
optional->SizeOfImage += size;

/*>>>> 关键代码 <<<<*/
if (optional->SizeOfHeaders - allHeaderOrSection >= (40 * 2))
{
//在最后一个节表的后面写入一个新的节表(新节表复制于.text)
memcpy_s(((PCHAR)pe) + allHeaderOrSection, 40, section[1], 40);
//获取最后一个节表(-2是因为在前面就已经修改了节表数量,原本是-1)
PIMAGE_SECTION_HEADER lastSection = (*section + file->NumberOfSections - 2);
//根据最后一个节表加上节表大小,获取新节表
PIMAGE_SECTION_HEADER newSectionTab = (PIMAGE_SECTION_HEADER)(((UINT_PTR)lastSection) + 40);
//新节表的名称
BYTE newName[] = { '.', 'n', 'e', 'w', '\0' };
//修改新节表的名称
memcpy_s(newSectionTab->Name, 8, newName, sizeof(newName));
//修改新节表在内存中的地址(最后一个节表在内存中的地址 + 最后一个节表的(SizeofRawData 或 VirtualSize,两者谁大取谁))
newSectionTab->VirtualAddress = Align(lastSection->VirtualAddress + (lastSection->Misc.VirtualSize > lastSection->SizeOfRawData ? lastSection->Misc.VirtualSize : lastSection->SizeOfRawData), optional->SectionAlignment);
//修改新节表在文件中的偏移(最后一个节表在文件中的偏移 + 最后一个节表在文件中对其后的大小)
newSectionTab->PointerToRawData = Align(lastSection->PointerToRawData + lastSection->SizeOfRawData, optional->FileAlignment);
//将新节表在内存中的大小置零(因为是新节表。没数据)
newSectionTab->Misc.VirtualSize = 0;
//修改新节表在文件中的对其后的大小
newSectionTab->SizeOfRawData = Align(size, optional->FileAlignment);
//修改新节表的属性(所有节表拥有的属性)
for (DWORD i = 0; i < file->NumberOfSections; i++)
{
newSectionTab->Characteristics |= (*section + i)->Characteristics;
}

LPVOID newPE = malloc(_msize(pe) + size);
if (newPE)
{
memset(newPE, 0, _msize(newPE));
memcpy_s(newPE, _msize(newPE), pe, _msize(pe));
free(pe);
pe = NULL;
pe = newPE;
return true;
}
}
/*>>>> 关键代码 <<<<*/
return false;
}


bool WriteMyFilePeData(
IN LPVOID pe,
IN LPCSTR filePath
) {
FILE* newProgram = nullptr;
bool result = true;
size_t size = NULL;

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

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

END:
if (newProgram)
fclose(newProgram);

return result;
}

//节区扩容
LPVOID SectionExpand(
IN LPVOID pe,
IN size_t size
) {
PIMAGE_DOS_HEADER dos = nullptr;
PIMAGE_FILE_HEADER file = nullptr;
PIMAGE_OPTIONAL_HEADER32 optional = nullptr;
PIMAGE_SECTION_HEADER* section = nullptr;

//计算扩容后的大小
size_t expanedSize = _msize(pe) + size;
LPVOID expanedBuffer = malloc(expanedSize);

if (expanedBuffer != nullptr)
{
//将原来PE数据复制到将扩充后的PE内存中(此时已经在空间上)
memset(expanedBuffer, 0, expanedSize);
memcpy_s(expanedBuffer, expanedSize, pe, _msize(pe));

AnlyzePE((PCHAR)expanedBuffer, dos, file, optional, section);
//扩容字节内存对其
size_t alignmentSize = Align(size, optional->SectionAlignment);
//修改内存镜像大小
optional->SizeOfImage += alignmentSize;
//扩容字节文件对其
alignmentSize = Align(size, optional->FileAlignment);
//修改数据在文件对其后的大小
section[file->NumberOfSections - 1]->SizeOfRawData += alignmentSize;
}
else
{
return nullptr;
}

free(section);
free(pe);
pe = NULL;
return expanedBuffer;
}

//移动重定位表到节区中
bool RelocMove(
IN LPVOID pe,
IN int index
) {
PIMAGE_DOS_HEADER dos = nullptr;
PIMAGE_FILE_HEADER file = nullptr;
PIMAGE_OPTIONAL_HEADER32 optional = nullptr;
PIMAGE_SECTION_HEADER* section = nullptr;
AnlyzePE(pe, dos, file, optional, section);

if (index == -1)
{
index = file->NumberOfSections - 1;
}

//获取重定位表
DWORD offset = RvaToFoa(pe, optional->DataDirectory[5].VirtualAddress);
PIMAGE_BASE_RELOCATION relocationTable = (PIMAGE_BASE_RELOCATION)((PCHAR)pe + offset);

//获取重定位表大小
size_t relocationSize = NULL;
while (relocationTable->VirtualAddress != NULL || relocationTable->SizeOfBlock != NULL)
{
relocationSize += relocationTable->SizeOfBlock;
relocationTable = (PIMAGE_BASE_RELOCATION)((PCHAR)relocationTable + relocationTable->SizeOfBlock);
}
relocationTable = (PIMAGE_BASE_RELOCATION)((PCHAR)pe + offset);

//如果节区剩余空白字节大于重定位表大小
if (section[index]->SizeOfRawData - section[index]->Misc.VirtualSize >= relocationSize)
{
//获取要写入节区空白字节的开始地址在文件PE中的偏移
DWORD writeMemory = RvaToFoa(pe, (UINT_PTR)((PCHAR)section[index]->VirtualAddress + section[index]->Misc.VirtualSize));
//写入重定位表
memcpy_s((PCHAR)pe + writeMemory, relocationSize, (PCHAR)relocationTable, relocationSize);
//修改节区真实数据大小
section[index]->Misc.VirtualSize += relocationSize;
//修改重定位表内存偏移
optional->DataDirectory[5].VirtualAddress = (writeMemory - section[index]->PointerToRawData) + section[index]->VirtualAddress;
return true;
}
else
{
return false;
}
}


bool SetImageBase(
IN LPVOID pe,
IN DWORD imageBase
) {
PIMAGE_DOS_HEADER dos = nullptr;
PIMAGE_FILE_HEADER file = nullptr;
PIMAGE_OPTIONAL_HEADER32 optional = nullptr;
PIMAGE_SECTION_HEADER* section = nullptr;
AnlyzePE(pe, dos, file, optional, section);

//获取新基址和原本基址的差值
DWORD difference = imageBase - optional->ImageBase;

//定位第一张重定位表
DWORD relocOffset = RvaToFoa(pe, optional->DataDirectory[5].VirtualAddress);
PIMAGE_BASE_RELOCATION relocTab = (PIMAGE_BASE_RELOCATION)((PCHAR)pe + relocOffset);

//循环所有重定位表
while (relocTab->VirtualAddress != NULL && relocTab->SizeOfBlock != NULL)
{
//循环所有项
for (DWORD i = 0; i < (relocTab->SizeOfBlock - 8) / 2; i++)
{
//高4位
WORD hige = *((PWORD)((PCHAR)relocTab + 8) + i) >> 12;
//低12位
WORD low = *((PWORD)((PCHAR)relocTab + 8) + i) & 0x0FFF;
//判断是否需要修复
if (hige == 3)
{
//获取需要修复的地址在文件PE中的偏移
DWORD offset = RvaToFoa(pe, relocTab->VirtualAddress + low);
printf("%x = %x = %x = %x\n", offset, relocTab->VirtualAddress + low, *((PDWORD)((PCHAR)pe + offset)), *((PDWORD)((PCHAR)pe + offset)) + difference);
//地址 + 1000
*((PDWORD)((PCHAR)pe + offset)) += difference;
}
}
//定位下一个重定位表
relocTab = (PIMAGE_BASE_RELOCATION)((PCHAR)relocTab + relocTab->SizeOfBlock);
}
//修改内存基址
optional->ImageBase = imageBase;
return true;
}

//移动导出表
bool ExportTableMove(
IN LPVOID pe,
IN int index /*= -1 */
) {
//解析PE结构
PIMAGE_DOS_HEADER dos = nullptr;
PIMAGE_FILE_HEADER file = nullptr;
PIMAGE_OPTIONAL_HEADER32 optional = nullptr;
PIMAGE_SECTION_HEADER* section = nullptr;
AnlyzePE(pe, dos, file, optional, section);

if (index == -1)
{
index = file->NumberOfSections - 1;
}

//获取导出表
DWORD offset = RvaToFoa(pe, optional->DataDirectory[0].VirtualAddress);
PIMAGE_EXPORT_DIRECTORY exportTab = (PIMAGE_EXPORT_DIRECTORY)((PCHAR)pe + offset);

size_t exportTabSize = 0x28 + (exportTab->NumberOfFunctions * 4) + (exportTab->NumberOfNames * 2);
for (DWORD i = 0, n = 0; n < exportTab->NumberOfNames; i++)
{
PDWORD n1 = (PDWORD)((PCHAR)pe + RvaToFoa(pe, exportTab->AddressOfNames));
PCHAR name = (PCHAR)pe + RvaToFoa(pe, *(n1 + i));
if (name)
{
exportTabSize += strlen(name) + 1;
n++;
}
}

//拷贝导出表到节区中
if (section[index]->SizeOfRawData - section[index]->Misc.VirtualSize >= exportTabSize)
{
//复制AddressOfFunction表
memcpy_s(
(PCHAR)pe + section[index]->PointerToRawData + section[index]->Misc.VirtualSize,
exportTab->NumberOfFunctions * 4,
(PCHAR)pe + RvaToFoa(pe, exportTab->AddressOfFunctions),
exportTab->NumberOfFunctions * 4
);
exportTab->AddressOfFunctions = FoaToRva(pe, section[index]->PointerToRawData + section[index]->Misc.VirtualSize);
section[index]->Misc.VirtualSize += exportTab->NumberOfFunctions * 4;

//复制AddressOfOrdinal表
memcpy_s(
(PCHAR)pe + section[index]->PointerToRawData + section[index]->Misc.VirtualSize,
exportTab->NumberOfNames * 2,
(PCHAR)pe + RvaToFoa(pe, exportTab->AddressOfNameOrdinals),
exportTab->NumberOfNames * 2
);
exportTab->AddressOfNameOrdinals = FoaToRva(pe, section[index]->PointerToRawData + section[index]->Misc.VirtualSize);
section[index]->Misc.VirtualSize += exportTab->NumberOfNames * 2;

//复制AddressOfName表
memcpy_s(
(PCHAR)pe + section[index]->PointerToRawData + section[index]->Misc.VirtualSize,
exportTab->NumberOfNames * 4,
(PCHAR)pe + RvaToFoa(pe, exportTab->AddressOfNames),
exportTab->NumberOfNames * 4
);
exportTab->AddressOfNames = FoaToRva(pe, section[index]->PointerToRawData + section[index]->Misc.VirtualSize);
DWORD addrOfName = section[index]->PointerToRawData + section[index]->Misc.VirtualSize;
section[index]->Misc.VirtualSize += exportTab->NumberOfNames * 4;

//拷贝函数名
for (DWORD i = 0, n = 0; n < exportTab->NumberOfNames; i++)
{
PDWORD n1 = (PDWORD)((PCHAR)pe + RvaToFoa(pe, exportTab->AddressOfNames));
PCHAR name = (PCHAR)pe + RvaToFoa(pe, *(n1 + i));
if (name)
{
memcpy_s(
(PCHAR)pe + section[index]->PointerToRawData + section[index]->Misc.VirtualSize,
strlen(name) + 1,
name,
strlen(name) + 1
);
*((PDWORD)((PCHAR)pe + addrOfName) + n) = FoaToRva(pe, section[index]->PointerToRawData + section[index]->Misc.VirtualSize);
section[index]->Misc.VirtualSize += strlen(name) + 1;
n++;
}
}

//拷贝导出表
memcpy_s(
(PCHAR)pe + section[index]->PointerToRawData + section[index]->Misc.VirtualSize,
0x28,
(PCHAR)pe + offset,
0x28
);
optional->DataDirectory[0].VirtualAddress = FoaToRva(pe, section[index]->PointerToRawData + section[index]->Misc.VirtualSize);
section[index]->Misc.VirtualSize += 0x28;

}

//修改导出表在内存中的偏移
free(section);
return true;
}

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int main()
{

LPVOID pe = ReadPE(R"(D:\source\repos\dllmain\Debug\MyDll.dll)");
if (pe && CreateSection(pe, 0x1000))
{
PIMAGE_DOS_HEADER dos = nullptr;
PIMAGE_FILE_HEADER file = nullptr;
PIMAGE_OPTIONAL_HEADER32 optional = nullptr;
PIMAGE_SECTION_HEADER* section = nullptr;
AnlyzePE(pe, dos, file, optional, section);

//移动重定位表
RelocMove(pe);
//修改内存镜像基址,并修复根据重定位表修复全局变量地址
SetImageBase(pe, optional->ImageBase + 0x10000);
//移动导出表
ExportTableMove(pe, file->NumberOfSections - 1);
WriteMyFilePeData(pe, R"(C:\Users\lenovo\Desktop\dll.dll)");
free(pe);
}

return 0;
}

运行结果

image-20250101222936061