DLL注入

First Post:

Last Update:

Word Count:
2.8k

Read Time:
12 min

DLL注入

远程线程注入

参考:

https://bbs.kanxue.com/thread-282885.htm

https://blog.csdn.net/Cody_Ren/article/details/100053434

https://blog.csdn.net/y281252548/article/details/120265888

注入代码

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
//main.cpp
#include <windows.h>
#include <tlhelp32.h>
#include <stdio.h>
#include <vector>
#include <map>
char dllPath[] = R"(C:\Users\15386\Desktop\temp\hookDll.dll)";
std::vector<DWORD64>ve;
std::map<DWORD64, bool>mp;
BYTE jmpBytes[] = {
0xE9, 0xE0, 0x26, 0x16, 0x00
};
BYTE originBytes[] = {
0x4C, 0x8B, 0xD1, 0xB8, 0x50, 0x00, 0x00, 0x00, 0xF6, 0x04, 0x25, 0x08, 0x03, 0xFE, 0x7F, 0x01,
0x75, 0x03, 0x0F, 0x05, 0xC3, 0xCD, 0x2E, 0xC3
};
std::vector<DWORD64> GetPidByProcName(const char* processName) {
HANDLE hProcessSnap = INVALID_HANDLE_VALUE;
PROCESSENTRY32 pe32 = { 0 };

std::vector<DWORD64> vec;
vec.clear();
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hProcessSnap == INVALID_HANDLE_VALUE) {
vec;
}

pe32.dwSize = sizeof(PROCESSENTRY32);

if (Process32First(hProcessSnap, &pe32)) {
do {
if (strcmp(pe32.szExeFile, processName) == 0) {
vec.push_back(pe32.th32ProcessID);
}
} while (Process32Next(hProcessSnap, &pe32));
}
CloseHandle(hProcessSnap);
return vec;
}

DWORD64 GetModuleBase(DWORD64 pid,const char* ModuleName)
{
HANDLE hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
if (hModuleSnap != INVALID_HANDLE_VALUE) {
MODULEENTRY32 me32;
me32.dwSize = sizeof(MODULEENTRY32);
if (Module32First(hModuleSnap, &me32)) {
do {
if (_stricmp(me32.szModule, ModuleName) == 0) {
return (DWORD64)me32.modBaseAddr;
}
} while (Module32Next(hModuleSnap, &me32));
}
CloseHandle(hModuleSnap);
}
return 0;
}

BOOL InjectDll(HANDLE hProcess, LPCSTR dllPath) {

LPVOID pRemoteDllPath = VirtualAllocEx(hProcess, NULL, strlen(dllPath) + 1, MEM_COMMIT, PAGE_READWRITE);
if (pRemoteDllPath == NULL) {
printf("VirtualAllocEx Failed:[%d]\n", GetLastError());
return FALSE;
}

if (!WriteProcessMemory(hProcess, pRemoteDllPath, dllPath, strlen(dllPath) + 1, NULL)) {
printf("WriteProcessMemory Failed:[%d]\n", GetLastError());
VirtualFreeEx(hProcess, pRemoteDllPath, 0, MEM_RELEASE);
return FALSE;
}

LPTHREAD_START_ROUTINE lpLoadLibrary = (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryA");
if (lpLoadLibrary == NULL) {
printf("GetProcAddress Failed:[%d]\n", GetLastError());
VirtualFreeEx(hProcess, pRemoteDllPath, 0, MEM_RELEASE);
return FALSE;
}

HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, lpLoadLibrary, pRemoteDllPath, 0, NULL);
if (hThread == NULL) {
printf("CreateRemoteThread Failed:[%d]\n", GetLastError());
VirtualFreeEx(hProcess, pRemoteDllPath, 0, MEM_RELEASE);
return FALSE;
}

WaitForSingleObject(hThread, INFINITE);

DWORD dwExitCode;
if (GetExitCodeThread(hThread, &dwExitCode) && dwExitCode == 0) {
printf("LoadLibraryA Failed in remote process\n");
CloseHandle(hThread);
VirtualFreeEx(hProcess, pRemoteDllPath, 0, MEM_RELEASE);
return FALSE;
}

CloseHandle(hThread);
VirtualFreeEx(hProcess, pRemoteDllPath, 0, MEM_RELEASE);

return TRUE;
}

BOOL RemoveHook(HANDLE hProcess,PVOID unHookAddr, BYTE* originBytes)
{
PDWORD oldProtect = 0;
if (!WriteProcessMemory(hProcess, (PVOID)unHookAddr, originBytes, sizeof(originBytes), 0))
{
printf("RemoveHook Failed!!!: [%d]\n", GetLastError());
return FALSE;
}
}
int main() {
while (1)
{
int Num = 0;
ve = GetPidByProcName("WorkingService.exe");//获取所有进程pid
if (!ve.empty())
{
printf("There are currently %d processes\n", ve.size());
//依次注入并用map标记是否被注入过
for (auto i : ve)
{
auto it = mp.find(i);
if (it == mp.end())//没有被注入过
{
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, i);//要用管理员权限运行,要不然为返回空
if (!hProcess) printf("OpenProcess Error:[%d]", GetLastError());
DWORD64 unHookAddr = GetModuleBase(i,"ntdll.dll") + 0xA0990;//NtProtectVirtualMemory地址,ntdll + 0xA0990
if (RemoveHook(hProcess, (PVOID)unHookAddr, originBytes))//先取消NtProtectVirtualMemory钩子再注入
{
if (InjectDll(hProcess, dllPath))
{
Num++;
mp.insert({ i,true });//注入成功后进行标记
printf("Inject process Success!\n", Num);
}
}

}
else continue;//曾经注入过该进程
}
}
else printf("Wating WorkingService.exe ...\n");
Sleep(1000);
system("cls");

}
return 0;
}

DLL代码

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
//dllmain.cpp
#include "pch.h"
#include <windows.h>
#include <shellapi.h>
#include <detours.h>
#include <tlhelp32.h>
#include <stdlib.h>
#pragma comment(lib,"detours.lib")
#define _KDEBUG
#define DBGMGEBOX(fmt, ...) \
do { \
/* 假设最大长度为1024,根据需要调整大小 */ \
wsprintfA(out, fmt, __VA_ARGS__); \
MessageBoxA(NULL, out, "提示", MB_OK); \
} while(0)
char out[100];
DWORD tlsIndex;//tls索引
typedef BOOL(WINAPI* ShellExecuteExA_t)(SHELLEXECUTEINFOA*);
typedef HANDLE (WINAPI* CreateFileA_t)(
LPCSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile
);
ShellExecuteExA_t TrueShellExecuteExA = NULL;
CreateFileA_t TrueCreateFileA = NULL;
BOOL WINAPI HookedShellExecuteExA(SHELLEXECUTEINFOA* pExecInfo) {
#if 1
//执行第一个ShellExecuteExA守护进程
static int Num = 0;
DBGMGEBOX("ShellExecuteExA 被调用:Num = %d\nhProcess = %p", Num, pExecInfo->hProcess);
if (Num == 0)
{
Num++;
return TrueShellExecuteExA(pExecInfo);
}
else
{
return TrueShellExecuteExA(pExecInfo);
}
#else
//执行第二个ShellExecuteExA病毒进程
static int Num = 0;
DBGMGEBOX("ShellExecuteExA 被调用:Num = %d \n调用者窗口句柄 = 0x%p\n", Num, pExecInfo->hwnd);
if (Num == 0)
{
Num++;
DBGMGEBOX("[2]:当前线程ID:%d", GetCurrentThreadId());
pExecInfo->lpFile = "C:\\Users\\Administrator\\Desktop\\自动F8直到call.txt";//修改参数导致重启失败;
return TrueShellExecuteExA(pExecInfo);

}
else
{

DBGMGEBOX("[1]:当前线程ID:%d", GetCurrentThreadId());
return TrueShellExecuteExA(pExecInfo);
}
#endif
}
HANDLE WINAPI HookCreateFileA(
LPCSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile
)
{

//判断线程是否是第一次运行CreateFileA,是的话就放行,不是第一次运行就终止线程
// 获取当前线程的TLS值
LPVOID tlsValue = TlsGetValue(tlsIndex);
if ((DWORD_PTR)tlsValue <= 0x10)
{
//
#ifdef _KDEBUG
DBGMGEBOX("放行\nlpFileName:%s\n", lpFileName);
#endif
dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
TlsSetValue(tlsIndex, (LPVOID)((DWORD_PTR)tlsValue + 1));
}
else
{
static int Num = 0;//如果终止了十六个线程,再终止进程,使得守护线程再生工作进程
// 不是第一次运行,终止线程
#ifdef _KDEBUG
DBGMGEBOX("终止\nlpFileName:%s\n", lpFileName);
#endif
Num++;
ExitThread(0);
if (Num == 16) exit(0);
}
return TrueCreateFileA(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH:

DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());

//ShellExecuteExA hook
/*TrueShellExecuteExA = (ShellExecuteExA_t)DetourFindFunction("shell32.dll", "ShellExecuteExA");
DetourAttach(&(PVOID&)TrueShellExecuteExA, HookedShellExecuteExA);*/

tlsIndex = TlsAlloc();//初始化TLS
TrueCreateFileA = (CreateFileA_t)DetourFindFunction("kernelbase.dll", "CreateFileA");
DetourAttach(&(PVOID&)TrueCreateFileA, HookCreateFileA);

DetourTransactionCommit();
break;
case DLL_PROCESS_DETACH:
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourDetach(&(PVOID&)TrueShellExecuteExA, HookedShellExecuteExA);
DetourDetach(&(PVOID&)TrueCreateFileA, HookCreateFileA);
TlsFree(tlsIndex);//清理TLS

DetourTransactionCommit();
break;
}
return TRUE;
}

APC注入

APC注入的原理?

APC是一个简称,即“异步过程调用”。APC注入的原理是利用当线程被唤醒时,APC中的注册函数会被执行,并以此去执行我们的DLL加载代码,进而完成DLL注入的目的。在线程下一次被调度的时候,就会执行APC函数,APC有两种形式,由系统产生的APC称为内核模式APC,由应用程序产生的APC被称为用户模式APC。

其实现流程为:

1)当EXE里某个线程执行到SleepEx()或者WaitForSingleObjectEx()时,系统就会产生一个软中断。
2)当线程再次被唤醒时,此线程会首先执行APC队列中的被注册的函数。
3)利用QueueUserAPC()这个API可以在软中断时向线程的APC队列插入一个函数指针,如果我们插入的是Loadlibrary()执行函数的话,就能达到注入DLL的目的。

使用方法:

1.利用快照枚举所有的线程

2.写入远程内存,写入的是Dll的路径

3.插入我们的DLL即可

注入代码

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
void CAPCInjectorDlg::OnBnClickedApcattack()
{
// TODO: 在此添加控件通知处理程序代码
//1.查找窗口
HWND hWnd = ::FindWindow(NULL, TEXT("APCTest"));
if (NULL == hWnd)
{
return;
}
/*2.获得进程的PID,当然通用的则是你把进程PID当做要注入的程序,这样不局限
于窗口了.这里简单编写,进程PID可以快照遍历获取
*/
DWORD dwPid = 0;
DWORD dwTid = 0;
dwTid = GetWindowThreadProcessId(hWnd, &dwPid);

//3.打开进程
HANDLE hProcess = NULL;
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
if (NULL == hProcess)
{
return;
}
//4.成功了,申请远程内存
void *lpAddr = NULL;
lpAddr = VirtualAllocEx(hProcess, 0, 0x1000, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (NULL == lpAddr)
{
return;
}
//5.写入我们的DLL路径,这里我写入当前根目录下的路径
char szBuf[] = "MyDll.dll";
BOOL bRet = WriteProcessMemory(hProcess, lpAddr, szBuf, strlen(szBuf) + 1, NULL);
if (!bRet)
{
return;
}
//6.根据线程Tid,打开线程句柄
HANDLE hThread = NULL;
hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, dwTid);
if (NULL == hThread)
{
return;
}
//7.给APC队列中插入回调函数
QueueUserAPC((PAPCFUNC)LoadLibraryA, hThread, (ULONG_PTR)lpAddr);

CloseHandle(hThread);
CloseHandle(hProcess);
}

注册表注入

利用在Windows系统中,当REG中的以下键值中存在有DLL文件路径时,会跟随EXE文件的启动加载这个DLL文件路径中的DLL文件。当如果遇到有多个DLL文件时,需要用逗号或者空格隔开多个DLL文件的路径。

注册表项:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLL

REG注入就是Windows 系统在给EXE文件加载DLL文件的过程中,多加载了一个比如说AppInit_DLL中的DLL文件,这个DLL文件可以是合法的,也可以是病毒文件的DLL,这就要看使用者是怎么利用这个特性了。

换个说法理解一下:

在系统中每一个进程加载User32.dll时,会收到DLL_PROCESS_ATTACH通知,当User32.dll对其进行处理时,会取得注册表键值HKEY_LOCAL_MACHINE\Software\Microsoft\windowsNT\CurrentVresion\Windows\AppInit_Dlls,并调用LoadLibrary来载入这个字符串中指定的每个DLL。被调用的DLL会在系统调用它们的DllMain函数,并将参数fdwReason的值设为DLL_PROCESS_ATTACH时,对自己进行初始化。所以我们在这个键值中添加我们的Dll路径,即可实现注入。

再换个说法理解一下:

注册表中默认提供了AppInit_Dlls与LoadAppInit_Dlls两个注册表项

在注册表编辑器中,将要注入的DLL路径字符串写入AppInit_Dlls项目,然后把LoadAppInit_Dlls项目值设置为1,重启后,指定DLL会注入所有运行进程。

工作原理:User32.dll被加载到进程是,会读取AppInit_DLLs注册表项,若有值,则调用LoadLibrary()API加载用户DLL,所以,严格的说,想应DLL并不会被加载到所有进程,而只是加载user32.dll的进程,Windows Xp忽略LoadAppInit.DLLs项。

注入流程:

打开注册表键值如下:
HKEY_LOCAL_MACHINE\SoftWare\MicroSoft\Windows NT\CurrentVersion\Windows\

  1. 在上面的注册表项中操作 AppInit_DLLs 键值,在该键值中添加自己的DLL的全路径加dll名,多个DLL以逗号或者空格分开(因此在文件名中应该尽量不要存在空格),该键值只有第一个dll文件名可以包含路径,后面的都不能包含,因此我们最好将dll放在系统路径 下,这样就可以不用包含路径也能被加载了。

  2. 在该注册表项中添加键值 LoadAppInit_DLLs ,类型为 DWORD,并将其值置为 1 。

总结

REG注入操作简单易懂,甚至不用写程序都可以完成注入操作,但是正是由于他的简单性,每个EXE都被注入,效率低,程序的扩展性差。

ComRes注入

ComRes注入的原理是利用Windows系统中C:\WINDOWS\system32目录下的ComRes.dll这个文件,当待注入EXE如果使用CoCreateInstance()这个API时,COM服务器会加载ComRes.dll到EXE中,利用这个加载过程,我们可以将ComRes.dll替换掉,并在伪造的ComRes.dll,然后利用LoadLibrary()将事先准备好的DLL加载到目标EXE中。

注意事项:

由于直接拷贝comres.dll文件到C:\WINDOWS\system32目录下会引起winows的文件系统保护机制,所以首先需要将C:\WINDOWS\system32\dllcache下的文件替换掉,然后再将其C:\WINDOWS\system32文件替换为我们伪造的文件。

ComRes注入只需伪造与替换就可以完成,编程要求不高,方便使用,但是由于加载了ComRes.dll后,再想替换ComRes.dll文件就不可能了,因此想反复测试ComRes.dll文件就比较麻烦。

劫持进程创建注入

劫持进程创建注入原理是利用Windows系统中的CreateProcess()这个API创建一个进程,并且将第六个参数设置为CREATE_SUSPENDED,进而创建一个挂起状态的进程,利用这个进程状态进行远程线程注入DLL,然后用ResumeThread()函数回复进程。

劫持进程创建注入其实就是远程线程注入的前期加强版,他可以在进程启动前进行注入,由于进程的线程没有启动,这样就可以躲过待注入进程的检测,提高注入的成功率。

消息钩子注入

消息钩子注入原理是利用Windows系统中的SetWindowsHookEx()这个API,他可以拦截目标进程的消息到指定的DLL中的导出函数,利用这个特性,我们可以将DLL注入到指定的进程中。

注意:

消息钩子注入只需要对SetWindowsHookEx()和DLL导出函数有深刻的认识就可以很容编写,所以代码简单,比较容易实现。

打赏点小钱
支付宝 | Alipay
微信 | WeChat