背景
进程隐藏,顾名思义就是运行注入程序之后,可以在任务管理器中让可见的目标进程消失。
当然,进程隐藏的方法有很多,例如 DLL 劫持、DLL注入、代码注入、进程内存替换、HOOK API 等等。我们本文要介绍的就是 HOOK API 函数 ZwQuerySystemInformation 实现的隐藏指定进程。
主要思路是通过Hook API技术抓ZwQuerySystemInformation函数,对它获取的进程列表信息进行修改,把有我们要隐藏的进程信息从中去掉,那么 ZwQuerySystemInformation 就返回了我们修改后的信息,其它程序获取这个被修的信息后,自然获取不到我们隐藏的进程,这样,达到进程隐藏的目的。
基本原理
1.利用INLINE HOOK API技术
Inline HOOK API的核心原理是在获取目标进程中指定API函数的地址后,修改该API函数入口地址的前几个字节数据,写入一个跳转指令,使其跳转到自定义的新函数中执行。
注意:32位系统需要更改函数的前5字节数据;而64位系统需要更改前12字节数据
Inline HOOK的具体流程如下所示:
(1)先获取API函数的地址,通过GetModuleHandle和GetProcAddress函数获取API函数在进程中的地址。
这里我们主要要找ntdll.dll中的ZwQuerySystemInformation函数
(2)然后。根据版本的不同,修改相应的HOOK API函数的前几字节数据
(3)再然后,修改API函数的前几字节数据的页面保护属性,这是为了修改后内存能够执行
(4)最后,为了能够还原操作,要在修改数据前先对数据进行备份,然后再修改数据,并还原页面保护属性
2.ZwQuerySystemInformation HOOK函数
完成上述操作后,程序会跳转到我们自定义的“ZwQuerySystemInformation”函数,设定目标进程PID并且判断是否要隐藏进程PID
核心代码实现
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 ZwQuerySystemInformation这个函数,然后写入我们自定义的ZwQuerySystemInformation函数代替它
要注意 Inline Hook API 的时候,在 32 位系统和 64 位系统下的差别。
在 32 位使用 jmp _NewAddress 跳转语句,机器码是 5 字节,而且要注意理解它的跳转偏移的计算方式:
跳转偏移 = 跳转地址 - 下一跳指令的地址
在 64 位使用的是的汇编指令是:
mov rax, _NewAddress
jmp rax
机器码是 12 字节。
在Windows7 32位旗舰版以及Windows10 64位专业版上进行测试,均能成功隐藏指定进程,程序在32位和64位全平台系统均能正常工作。要注意一点就是,建议以管理员身份运行程序,否则我们的全局钩子不能成功注入到一些高权限的进程中。
另外,通过对比ZwQuerySystemInformation函数所在的PE文件在本地和内存中的数据,可以检测是否存在Inline Hook,进行安全对抗。