# SSDT Hook 之内核函数ZwTerminateProcess实现监控结束进程
# 背景
我们要开始向大家演示 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 ZwTerminateProcess 内核函数实现监控结束进程。现在,我就把实现过程和原理,整理成文档,分享给大家。
# 实现过程
在 32 位系统下,SSDT 是导出的,所以可以直接获取 KeServiceDescriptorTable。然后,我们根据 ntdll.dll 获取 ZwTerminateProcess 函数的 SSDT 函数索引号。有了这个 SSDT 索引号,我们就可以根据 KeServiceDescriptorTable 获取函数 ZwTerminateProcess 在内核中的地址了。
那么,SSDT HOOK 的原理就是:将 SSDT 表中存储的 ZwTerminateProcess 函数地址修改成新的函数的地址,这样,当系统从 SSDT 获取 ZwTerminateProcess 函数地址的时候,获取到的就是指向我们新函数的地址,从而执行我们的新函数。
其中,SSDT 表的内存是有 Write Protect 写保护属性,所以,我们采用 MDL 方式来修改 SSDT 表的内存,写入我们的新函数地址。
那么,在新的 New_ZwTerminateProcess 函数中的操作就是:
通过参数判断是否是我们保护的进程,若不是,则调用原来的内核函数 ZwTerminateProcess 结束进程;若是,则直接返回,不操作。
这样,就可以实现监控结束进程了。
# 编码实现
## Hook
```c++
// SSDT Hook
BOOLEAN SSDTHook()
{
UNICODE_STRING ustrDllFileName;
ULONG ulSSDTFunctionIndex = 0;
PMDL pMdl = NULL;
PVOID pNewAddress = NULL;
ULONG ulNewFuncAddr = 0;
RtlInitUnicodeString(&ustrDllFileName, L"\\??\\C:\\Windows\\System32\\ntdll.dll");
// 从 ntdll.dll 中获取 SSDT 函数索引号
ulSSDTFunctionIndex = GetSSDTFunctionIndex(ustrDllFileName, "ZwTerminateProcess");
// 根据索引号, 从SSDT表中获取对应函数地址
g_pOldSSDTFunctionAddress = (PVOID)KeServiceDescriptorTable.ServiceTableBase[ulSSDTFunctionIndex];
if (NULL == g_pOldSSDTFunctionAddress)
{
DbgPrint("Get SSDT Function Error!\n");
return FALSE;
}
// 使用 MDL 方式修改 SSDT
pMdl = MmCreateMdl(NULL, &KeServiceDescriptorTable.ServiceTableBase[ulSSDTFunctionIndex], sizeof(ULONG));
if (NULL == pMdl)
{
DbgPrint("MmCreateMdl Error!\n");
return FALSE;
}
MmBuildMdlForNonPagedPool(pMdl);
pNewAddress = MmMapLockedPages(pMdl, KernelMode);
if (NULL == pNewAddress)
{
IoFreeMdl(pMdl);
DbgPrint("MmMapLockedPages Error!\n");
return FALSE;
}
// 写入新函数地址
ulNewFuncAddr = (ULONG)New_ZwTerminateProcess;
RtlCopyMemory(pNewAddress, &ulNewFuncAddr, sizeof(ULONG));
// 释放
MmUnmapLockedPages(pNewAddress, pMdl);
IoFreeMdl(pMdl);
return TRUE;
}
```
## Unhook
```c++
// SSDT Unhook
BOOLEAN SSDTUnhook()
{
UNICODE_STRING ustrDllFileName;
ULONG ulSSDTFunctionIndex = 0;
PVOID pSSDTFunctionAddress = NULL;
PMDL pMdl = NULL;
PVOID pNewAddress = NULL;
ULONG ulOldFuncAddr = 0;
RtlInitUnicodeString(&ustrDllFileName, L"\\??\\C:\\Windows\\System32\\ntdll.dll");
// 从 ntdll.dll 中获取 SSDT 函数索引号
ulSSDTFunctionIndex = GetSSDTFunctionIndex(ustrDllFileName, "ZwTerminateProcess");
// 使用 MDL 方式修改 SSDT
pMdl = MmCreateMdl(NULL, &KeServiceDescriptorTable.ServiceTableBase[ulSSDTFunctionIndex], sizeof(ULONG));
if (NULL == pMdl)
{
DbgPrint("MmCreateMdl Error!\n");
return FALSE;
}
MmBuildMdlForNonPagedPool(pMdl);
pNewAddress = MmMapLockedPages(pMdl, KernelMode);
if (NULL == pNewAddress)
{
IoFreeMdl(pMdl);
DbgPrint("MmMapLockedPages Error!\n");
return FALSE;
}
// 写入原函数地址
ulOldFuncAddr = (ULONG)g_pOldSSDTFunctionAddress;
RtlCopyMemory(pNewAddress, &ulOldFuncAddr, sizeof(ULONG));
// 释放
MmUnmapLockedPages(pNewAddress, pMdl);
IoFreeMdl(pMdl);
return TRUE;
}
```
## 新函数
```c++
// 新函数
NTSTATUS New_ZwTerminateProcess(HANDLE hProcess, NTSTATUS ntExitCode)
{
NTSTATUS status = STATUS_SUCCESS;
PEPROCESS pEProcess = NULL;
PCHAR lpProcessName = NULL;
if ((HANDLE)0xFFFFFFFF == hProcess ||
(HANDLE)0x0 == hProcess)
{
// 返回无效句柄
return STATUS_INVALID_HANDLE;
}
// 通过进程句柄来获取该进程所对应的FileObject对象,由于这里是进程对象,自然获得的是EPROCESS对象
status = ObReferenceObjectByHandle(hProcess, FILE_READ_DATA, NULL, KernelMode, &pEProcess, NULL);
if (NT_SUCCESS(status))
{
// 获取进程名称
lpProcessName = PsGetProcessImageFileName(pEProcess);
DbgPrint("Terminate Process:%s\n", lpProcessName);
// 保护进程
if (NULL != strstr(lpProcessName, "520"))
{
DbgPrint("[Deny Terminate Process:%s]\n", lpProcessName);
return STATUS_ACCESS_DENIED;
}
}
// 调用原函数, 结束进程
typedef NTSTATUS (*typedef_ZwTerminateProcess)(HANDLE, NTSTATUS);
status = ((typedef_ZwTerminateProcess)g_pOldSSDTFunctionAddress)(hProcess, ntExitCode);
return status;
}
```
# 程序测试
在 Win7 32 位系统下,驱动程序正常执行:
![](http://www.writebug.com/myres/static/uploads/2021/10/19/053425b2d09dd633cd0d3d331ed0f3a7.writebug)
在 Win10 32 位系统下,驱动程序正常执行:
![](http://www.writebug.com/myres/static/uploads/2021/10/19/46cb9360e3a2196e91b01102e84da22b.writebug)
# 总结
其中,我们对 SSDT 函数 ZwTerminateProcess SSDT Hook 的时候,修改内存的方法使用 MDL 方式,这样,可以突破内存的 Write Protect 写保护。
# 参考
参考自《[Windows黑客编程技术详解](https://www.write-bug.com/article/1811.html "Windows黑客编程技术详解")》一书