CreateRemoteThread和RtlCreateUserThread进程创建之后注入DLL(简单的注入器)
1. CreateRemoteThread
本文参考前辈的文章https://www.cnblogs.com/wf751620780/,对原理有了很大了解
首先CreateRemoteThread函数的原型如下,它很像CreateThread函数。不同的是,前者是远程创建线程,后者是在自己的进程下创建线程。我们用前者来注入DLL:
HANDLE WINAPI CreateRemoteThread(
_In_ HANDLE hProcess,
_In_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ SIZE_T dwStackSize,
_In_ LPTHREAD_START_ROUTINE lpStartAddress,
_In_ LPVOID lpParameter,
_In_ DWORD dwCreationFlags,
_Out_ LPDWORD lpThreadId
);
两个函数的区别就是第一个句柄行参,hProcess表示创建的新线程属于哪一个进程。
注入的过程如下:
(1).用VirtualAllocEx函数在目标进程的地址空间中分配一块足够大的内存用于保存被注入的dll的路径。
(2).用WriteProcessMemory函数把本进程中保存dll路径的内存中的数据拷贝到第(1)步得到的目标进程的内存中。
(3).用GetProcAddress函数获得LoadLibraryW函数的起始地址。LoadLibraryW函数位于Kernel32.dll中。
(4).用CreateRemoteThread函数让目标进程执行LoadLibraryW来加载被注入的dll。函数结束将返回载入dll后的模块句柄。
(5).用VirtualFreeEx释放第(1)步开辟的内存。
在需要卸载dll时我们可以在上述第(5)步的基础上继续执行以下步骤:
(6).用GetProcAddress函数获得FreeLibrary函数的起始地址。FreeLibrary函数位于Kernel32.dll中。
(7).用CreateRemoteThread函数让目标进程执行FreeLibrary来卸载被注入的dll。(其参数是第(4)步返回的模块句柄)。
如果不在上述步骤基础上执行操作,卸载dll时你需要这么做:
(1).获得被注入的dll在目标进程的模块句柄。
(2).重复上述步骤的第(6)、(7)两步。
需要明确几点,首先用的调用dll函数是LoadLibraryW,因为不管是LoadLibrary还是LoadLibraryA,翻译到底层用的都是宽字符函数LoadLibraryW。第二使用GetProcAddress获得LoadLibraryW函数起始位置。
第三,DLL的绝对路径需要放在用WriteProcessMemory写到目标程序的内存空间下面,而分配这个内存空间使用的函数是VirtualAllocEx
这两个函数原型是:
LPVOID WINAPI VirtualAllocEx(
_In_ HANDLE hProcess,
_In_opt_ LPVOID lpAddress,
_In_ SIZE_T dwSize,
_In_ DWORD flAllocationType,
_In_ DWORD flProtect
);
BOOL WINAPI WriteProcessMemory( _In_ HANDLE hProcess, _In_ LPVOID lpBaseAddress, _In_ LPCVOID lpBuffer, _In_ SIZE_T nSize, _Out_ SIZE_T *lpNumberOfBytesWritten );
运行环境win10+vs2019。代码如下:
#include "windows.h" #include "stdio.h" #include "tlhelp32.h" #include "io.h" #include "tchar.h" //判断某模块(dll)是否在相应的进程中 //dwPID 进程的PID //szDllPath 查询的dll的完整路径 BOOL CheckDllInProcess(DWORD dwPID, LPCTSTR szDllPath) { BOOL bMore = FALSE; HANDLE hSnapshot = INVALID_HANDLE_VALUE; MODULEENTRY32 me = { sizeof(me), }; if (INVALID_HANDLE_VALUE == (hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID)))//获得进程的快照 { _tprintf(L"CheckDllInProcess() : CreateToolhelp32Snapshot(%d) failed!!! [%d]\n", dwPID, GetLastError()); return FALSE; } bMore = Module32First(hSnapshot, &me);//遍历进程内得的所有模块 for (; bMore; bMore = Module32Next(hSnapshot, &me)) { if (!_tcsicmp(me.szModule, szDllPath) || !_tcsicmp(me.szExePath, szDllPath))//模块名或含路径的名相符 { CloseHandle(hSnapshot); return TRUE; } } CloseHandle(hSnapshot); return FALSE; } //向指定的进程注入相应的模块 //dwPID 目标进程的PID //szDllPath 被注入的dll的完整路径 BOOL InjectDll(DWORD dwPID, LPCTSTR szDllPath) { HANDLE hProcess = NULL;//保存目标进程的句柄 LPVOID pRemoteBuf = NULL;//目标进程开辟的内存的起始地址 DWORD dwBufSize = (DWORD)(_tcslen(szDllPath) + 1) * sizeof(TCHAR);//开辟的内存的大小 LPTHREAD_START_ROUTINE pThreadProc = NULL;//loadLibreayW函数的起始地址 HMODULE hMod = NULL;//kernel32.dll模块的句柄 BOOL bRet = FALSE; if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)))//打开目标进程,获得句柄 { _tprintf(L"目标进程打开失败 OpenProcess(%d)!!! [%d]\n", dwPID, GetLastError()); goto INJECTDLL_EXIT; } pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE);//在目标进程空间开辟一块内存 if (pRemoteBuf == NULL) { _tprintf(L"分配空间失败VirtualAllocEx() failed!!! [%d]\n", GetLastError()); goto INJECTDLL_EXIT; } if (!WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllPath, dwBufSize, NULL))//向开辟的内存复制dll的路径 { _tprintf(L"向目标空间复制路径失败 WriteProcessMemory() failed!!! [%d]\n", GetLastError()); goto INJECTDLL_EXIT; } hMod = GetModuleHandle(L"kernel32.dll");//获得本进程kernel32.dll的模块句柄 if (hMod == NULL) { _tprintf(L"InjectDll() : GetModuleHandle(\"kernel32.dll\") failed!!! [%d]\n", GetLastError()); goto INJECTDLL_EXIT; } pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "LoadLibraryW");//获得LoadLibraryW函数的起始地址 if (pThreadProc == NULL) { _tprintf(L"InjectDll() : GetProcAddress(\"LoadLibraryW\") failed!!! [%d]\n", GetLastError()); goto INJECTDLL_EXIT; } if (!CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL))//执行远程线程 { _tprintf(L"InjectDll() : MyCreateRemoteThread() failed!!!\n"); goto INJECTDLL_EXIT; } INJECTDLL_EXIT: bRet = CheckDllInProcess(dwPID, szDllPath);//确认结果 if (pRemoteBuf) { MessageBox(NULL,L"注入成功",L"注入成功",NULL); VirtualFreeEx(hProcess, pRemoteBuf, 0, MEM_RELEASE); } if (hProcess) CloseHandle(hProcess); return bRet; } //让指定的进程卸载相应的模块 //dwPID 目标进程的PID //szDllPath 被注入的dll的完整路径,注意:路径不要用“/”来代替“\\” BOOL EjectDll(DWORD dwPID, LPCTSTR szDllPath) { BOOL bMore = FALSE, bFound = FALSE, bRet = FALSE; HANDLE hSnapshot = INVALID_HANDLE_VALUE; HANDLE hProcess = NULL; MODULEENTRY32 me = { sizeof(me), }; LPTHREAD_START_ROUTINE pThreadProc = NULL; HMODULE hMod = NULL; TCHAR szProcName[MAX_PATH] = { 0, }; if (INVALID_HANDLE_VALUE == (hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID))) { _tprintf(L"EjectDll() : CreateToolhelp32Snapshot(%d) failed!!! [%d]\n", dwPID, GetLastError()); goto EJECTDLL_EXIT; } bMore = Module32First(hSnapshot, &me); for (; bMore; bMore = Module32Next(hSnapshot, &me))//查找模块句柄 { if (!_tcsicmp(me.szModule, szDllPath) || !_tcsicmp(me.szExePath, szDllPath)) { bFound = TRUE; break; } } if (!bFound) { _tprintf(L"EjectDll() : There is not %s module in process(%d) memory!!!\n", szDllPath, dwPID); goto EJECTDLL_EXIT; } if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID))) { _tprintf(L"EjectDll() : OpenProcess(%d) failed!!! [%d]\n", dwPID, GetLastError()); goto EJECTDLL_EXIT; } hMod = GetModuleHandle(L"kernel32.dll"); if (hMod == NULL) { _tprintf(L"EjectDll() : GetModuleHandle(\"kernel32.dll\") failed!!! [%d]\n", GetLastError()); goto EJECTDLL_EXIT; } pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "FreeLibrary"); if (pThreadProc == NULL) { _tprintf(L"EjectDll() : GetProcAddress(\"FreeLibrary\") failed!!! [%d]\n", GetLastError()); goto EJECTDLL_EXIT; } if (!CreateRemoteThread(hProcess, NULL, 0, pThreadProc, me.modBaseAddr, 0, NULL)) { _tprintf(L"EjectDll() : MyCreateRemoteThread() failed!!!\n"); goto EJECTDLL_EXIT; } bRet = TRUE; EJECTDLL_EXIT: if (hProcess) CloseHandle(hProcess); if (hSnapshot != INVALID_HANDLE_VALUE) CloseHandle(hSnapshot); return bRet; } int main() { InjectDll(19220, L"S:\\panny_dll.dll"); EjectDll(19220, L"S:\\panny_dll.dll"); return 0; }
我们启动一个demo并获得它的pid:
然后获得我们需要注入的DLL的绝对路径,开始攻击:
用的查询注入是否成功的方法是用进程快照搜索进程空间是否有目标dll的方式,同反调试技术,运行就会弹出注入成功框:
同时Process Explore可以看到目标dll:
2. RtlCreateUserThread
不同于前面直接调用win api, RtlCreateUserThread是CreateRemoteThread的底层实现,所以使用RtlCreateUserThread的原理是和使用CreateRemoteThread的原理是一样的,这个函数可以实现跨会话创建线程。唯一的问题就是:当我们在Vista 之前的操作系统调用此函数所创建的线程并没有通知给csrss 进程,它没有完整的初始化,在调用一些网络或者其它的系统函数的时候他可能返回失败,而通过CreateRemoteThread 创建的线程没有任何问题,在Vista+的操作系统上调用此函数也没有问题。因此我们需要判断系统版本,当目标系统为Vista-的时候,我们应该通过RtlCreateUserThread创建一个挂起的线程,然后调用CsrClientCallServer通知csrss 这个线程的创建,然后csrss 做相应的记录及初始化工作之后,我们再恢复线程的执行。最后,我们新创建的线程必须自己调用ExitThread退出,否则也会产生崩溃的情况。
LoadLibraryW函数调用原型:
typedef DWORD(WINAPI* pRtlCreateUserThread)( //函数原型 IN HANDLE ProcessHandle, IN PSECURITY_DESCRIPTOR SecurityDescriptor, IN BOOL CreateSuspended, IN ULONG StackZeroBits, IN OUT PULONG StackReserved, IN OUT PULONG StackCommit, IN LPVOID StartAddress, IN LPVOID StartParameter, OUT HANDLE ThreadHandle, OUT LPVOID ClientID );
LoadLibraryW
函数shellcode:
BYTE StaticShellCode[31] = { 0xE8,0,0,0,0, // call (5字节) 4 0x5D, // pop ebp 5 0x8B,0xC5, // mov eax,ebp 7 0x83,0xC0,0x1A, // add eax,1a 10 0x50, // push eax 11 0xB8,0,0,0,0, // mov eax,LoadLibraryW 16 0xFF,0xD0, // call eax 18 0x6A,0, // push 0 20 0xB8,0,0,0,0, // mov eax,ExitThread 25 0xFF,0xD0, // call eax 27 0xC3, // ret 4 28 0};
代码如下:
(整体逻辑一样,就不详细备注)
#include <Windows.h> #include <tlhelp32.h> #include <stdio.h> #include <iostream> #include <string> DWORD findPidByName(const char* pname) //获得pid函数,对上面的查找进行改进 { HANDLE h; PROCESSENTRY32 procSnapshot; h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); procSnapshot.dwSize = sizeof(PROCESSENTRY32); do { if (!_stricmp(procSnapshot.szExeFile, pname)) { DWORD pid = procSnapshot.th32ProcessID; CloseHandle(h); #ifdef _DEBUG printf(("[+]进程地址: %ld\n"), pid); #endif return pid; } } while (Process32Next(h, &procSnapshot)); CloseHandle(h); return 0; } typedef DWORD(WINAPI* pRtlCreateUserThread)( //函数申明 IN HANDLE ProcessHandle, IN PSECURITY_DESCRIPTOR SecurityDescriptor, IN BOOL CreateSuspended, IN ULONG StackZeroBits, IN OUT PULONG StackReserved, IN OUT PULONG StackCommit, IN LPVOID StartAddress, IN LPVOID StartParameter, OUT HANDLE ThreadHandle, OUT LPVOID ClientID ); DWORD RtlCreateUserThread(LPCSTR pszLibFile, DWORD dwProcessId) { pRtlCreateUserThread RtlCreateUserThread = NULL; HANDLE hRemoteThread = NULL; HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId); //打开目标进程pid if (hProcess == NULL) { printf("[-] Error: Could not open process for PID (%d).\n", dwProcessId); exit(1); } LPVOID LoadLibraryAddress = (LPVOID)GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA"); //load函数地址 if (LoadLibraryAddress == NULL) { printf(("[-] Error: Could not find LoadLibraryA function inside kernel32.dll library.\n")); exit(1); } RtlCreateUserThread = (pRtlCreateUserThread)GetProcAddress(GetModuleHandle(("ntdll.dll")), ("RtlCreateUserThread"));//获取Rtl函数地址 if (RtlCreateUserThread == NULL) { exit(1); } #ifdef _DEBUG printf(("[+]RtlCreateUserThread函数地址: 0x%08x\n"), (UINT)RtlCreateUserThread); printf(("[+]LoadLibraryA函数地址: 0x%08x\n"), (UINT)LoadLibraryAddress); #endif DWORD dwSize = (strlen(pszLibFile) + 1) * sizeof(char); LPVOID lpBaseAddress = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);//开辟一段地址 if (lpBaseAddress == NULL) { printf(("[-] Error: Could not allocate memory inside PID (%d).\n"), dwProcessId); exit(1); } BOOL bStatus = WriteProcessMemory(hProcess, lpBaseAddress, pszLibFile, dwSize, NULL); //目标dll写入 if (bStatus == 0) { printf(("[-] Error: Could not write any bytes into the PID (%d) address space.\n"), dwProcessId); return(1); } bStatus = (BOOL)RtlCreateUserThread( hProcess, NULL, 0, 0, 0, 0, LoadLibraryAddress, //shellcode lpBaseAddress, &hRemoteThread, NULL); if (bStatus < 0) { printf(("[-]注入失败\n")); return(1); } else { printf(("[+]注入成功...\n")); WaitForSingleObject(hRemoteThread, INFINITE); CloseHandle(hProcess); VirtualFreeEx(hProcess, lpBaseAddress, dwSize, MEM_RELEASE); return(0); } return(0); } int main() { const char* name = ("菜单dome.exe"); DWORD pId = findPidByName(name); LPCSTR location = ("S:\\panny_dll.dll"); RtlCreateUserThread(location, pId); }
还是运行菜单dome.exe,:
(其实变蓝就已经在提示有dll注入了)