64 位 SSDT HOOK

64位和32位的区别:

    都有写保护,但是 64 位下因为微软的特殊处理导致可以HOOK的范围只有512M大小的地址空间, 超出512M地址空间的会被无效处理掉,也就导致普通的HOOK会被无效处理
    所以我们需要借助一个跳板,也就是 KeBugCheckEx 函数(其是在512M有效的地址空间中),通过InlineHook让操作系统调用这个 KeBugCheckEx 函数时会跳向我们的函数代码
    针对我们的想要Hook的函数都可以用被修改的 KeBugCheckEx 函数地址替代

驱动代码如下:

#include <Ntifs.h>
#include <windef.h>
// 64位的 SSDT 表HOOK
// 定义系统服务表结构体
typedef struct _SERVICETABLE
{
       PULONG ServiceTableAddr; // 函数地址表的地址 (4字节成员)
       PULONG Count; // 当前系统服务表被调用的次数
       ULONG ServiceLimit; // 系统服务表中函数的个数
       PUCHAR ArgMentTable; // 系统参数表地址 (1字节成员)
}SERVICETABLE, *PSERVICETABLE;
// 定义函数指针
typedef NTSTATUS(*pNtTerminateProcess)(HANDLE ProcessHandle, NTSTATUS ExitStatus);
pNtTerminateProcess OldNtTerminateProcess;
// 未声明函数
// 获取 EPROCESS 结构中的进程名称
PCHAR PsGetProcessImageFileName(PEPROCESS Process);
PSERVICETABLE KeServiceDescriptorTable = NULL;
KIRQL irQl;
// 修改Cr0寄存器, 去除写保护(内存保护机制)
KIRQL RemovWP()
{
       DbgPrint("RemovWP\n");
       // (PASSIVE_LEVEL)提升 IRQL 等级为DISPATCH_LEVEL,并返回旧的 IRQL
       // 需要一个高的IRQL才能修改
       irQl = KeRaiseIrqlToDpcLevel();
       ULONG_PTR cr0 = __readcr0(); // 内联函数:读取Cr0寄存器的值, 相当于: mov eax,   cr0;
       // 将第16位(WP位)清0,消除写保护
       cr0 &= ~0x10000; // ~ 按位取反
       _disable(); // 清除中断标记, 相当于 cli 指令,修改 IF标志位
       __writecr0(cr0); // 将cr0变量数据重新写入Cr0寄存器中,相当于: mov cr0, eax
       return irQl;
}
// 复原Cr0寄存器
KIRQL UndoWP()
{
       DbgPrint("UndoWP\n");
       ULONG_PTR cr0 = __readcr0();
       cr0 |= 0x10000; // WP复原为1
       _disable(); // 清除中断标记, 相当于 cli 指令,清空 IF标志位
       __writecr0(cr0); // 将cr0变量数据重新写入Cr0寄存器中,相当于: mov cr0, eax
       // 恢复IRQL等级
       KeLowerIrql(irQl);
       return irQl;
}
// 我们函数
NTSTATUS HookNtTerminateProcess(HANDLE ProcessHandle, NTSTATUS ExitStatus)
{
       DbgPrint("进入 HookNtTerminateProcess\n");
       PEPROCESS Process;
       NTSTATUS status;
       // 获取内核对象
       status = ObReferenceObjectByHandle(ProcessHandle, PROCESS_ALL_ACCESS,  *PsProcessType, KernelMode, &Process, NULL);
       if (NT_SUCCESS(status))
       {
              if (strcmp(PsGetProcessImageFileName(Process), "calc.exe") == 0) //  如果为保护进程
              {
                     if (PsGetCurrentProcess() != Process) // 如果不为自己进程
                     {
                           return STATUS_ACCESS_DENIED; // 返回删除成功(拦截)
                     }
              }
       }
       return OldNtTerminateProcess(ProcessHandle, ExitStatus); // 调用原来的函数
}
// HOOK KeBugCheckEx 函数, 让其通过调用 KeBugCheckEx 到我们的函数中
BOOL HOOKKeBugCheckEx()
{
       DbgPrint("进入 HOOKKeBugCheckEx\n");
       /*
              mov rax, 0xFFFFFFFF~FFFFFFFF;
              jmp rax;
       */
       char jmp_Code[] = { "\x48\xB8\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xE0" };  // 构建       ShellCode
       ULONGLONG pMyHookFun = (ULONGLONG)HookNtTerminateProcess;
       RtlMoveMemory(&jmp_Code[2], &pMyHookFun, sizeof(PVOID)); // 将我们的函数地址写入, 间接Call
       PVOID BugFunAddr = NULL;
       BugFunAddr = (PVOID)KeBugCheckEx;// 将函数地址给 BugFunAddr
       // 关闭写保护
       RemovWP();
       RtlMoveMemory(BugFunAddr, jmp_Code, sizeof(jmp_Code)); // inlineHook, 将我们的代码写入其中
       // 恢复写保护
       UndoWP();
       return TRUE;
}
VOID GetSsdtTableAddr()
{
       DbgPrint("进入 GetSsdtTableAddr\n");
       // 获取SSDT表地址
       LONG offset;
       // 获取 KiSystemCall64 地址
       PVOID pKiSystemCall64 = (PVOID)__readmsr(0xC0000082);
       UCHAR ulCode1, ulCode2, ulCode3;
       for (ULONG index = 0; index < 1024; index++)
       {
              // 获取内存数据
              ulCode1 = *(PUCHAR)((PUCHAR)pKiSystemCall64 + index);
              ulCode2 = *(PUCHAR)((PUCHAR)pKiSystemCall64 + index + 1);
              ulCode3 = *(PUCHAR)((PUCHAR)pKiSystemCall64 + index + 2);
              // 判断特征码
              if (ulCode1 == (UCHAR)0x4c &&
                     ulCode2 == (UCHAR)0x8d &&
                     ulCode3 == (UCHAR)0x15)
              {
                     // 获取 SSDT 表偏移
                     offset = *(PLONG)((PUCHAR)pKiSystemCall64 + index + 3);
                     // 根据偏移计算地址
                     KeServiceDescriptorTable =  (PSERVICETABLE)((PUCHAR)pKiSystemCall64 + index + 7 + offset);
                     break;
              }
       }
}
BOOL HookSSDTCall(ULONG SsdtIndex)
{
       DbgPrint("进入 HookSSDTCall\n");
       PCHAR FunAddr = NULL;
       LONG offset;
       HOOKKeBugCheckEx(); // Hook KeBugCheckEx 函数
       GetSsdtTableAddr(); // 获取 KeServiceDescriptorTable 地址
       RemovWP();
       FunAddr = (PCHAR)KeBugCheckEx;
       offset = (LONG)((PCHAR)FunAddr -  (PCHAR)KeServiceDescriptorTable->ServiceTableAddr); // 计算 KeBugCheckEx 地址 -  ServiceTableAddr
       offset <<= 4; // ( KeBugCheckEx地址 - ServiceTableAddr) << 4
       // 获取原函数地址
       OldNtTerminateProcess =  (pNtTerminateProcess)((PCHAR)KeServiceDescriptorTable->ServiceTableAddr +  ((KeServiceDescriptorTable->ServiceTableAddr[41]) >> 4));
       // 填写 KeBugCheckEx 偏移
       KeServiceDescriptorTable->ServiceTableAddr[41] = offset;
       UndoWP();
       return TRUE;
}
VOID Unload(IN PDRIVER_OBJECT DriverObject)
{
       DbgPrint("卸载驱动\n");
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING   RegeditPath)
{
       DbgPrint("加载驱动\n");
       //KdBreakPoint();
       NTSTATUS status = STATUS_SUCCESS;
       DriverObject->DriverUnload = Unload;
       HookSSDTCall(41);
       return STATUS_SUCCESS;
}

HOOK 指定函数让其无法关闭计算器进程


原创文章,转载请注明: 转载自Windows内核安全驱动编程

本文链接地址: 64 位 SSDT HOOK

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注