HOOK SSDT

在 WIN64 上 HOOK SSDT 和 UNHOOK SSDT 在原理上跟 WIN32 没什么不同,甚至说 HOOK 和 UNHOOK 在本质上也没有不同,都是在指定的地址上填写一串数字而已

(填写代理函数的地址时叫做 HOOK,填写原始函数的地址时叫做 UNHOOK)。不过实现起来还是很大不同的。

一 、 HOOK SSDT

要挂钩 SSDT,必然要先得到 ServiceTableBase 的地址。和 SSDT 相关的两个结构体 SYSTEM_SERVICE_TABLE 以及 SERVICE_DESCRIPTOR_TABLE 并没有发生

什么的变化(除了整个结构体的长度胖了一倍):

typedef struct _SYSTEM_SERVICE_TABLE{
PVOID ServiceTableBase;
PVOID ServiceCounterTableBase;
ULONGLONG NumberOfServices;
PVOID ParamTableBase;
} SYSTEM_SERVICE_TABLE, *PSYSTEM_SERVICE_TABLE; typedef struct _SERVICE_DESCRIPTOR_TABLE{
SYSTEM_SERVICE_TABLE ntoskrnl; // ntoskrnl.exe (native api)
SYSTEM_SERVICE_TABLE win32k; // win32k.sys (gdi/user)
SYSTEM_SERVICE_TABLE Table3; // not used
SYSTEM_SERVICE_TABLE Table4; // not used
}SERVICE_DESCRIPTOR_TABLE,*PSERVICE_DESCRIPTOR_TABLE;

得到 ServiceTableBase 的地址后,就能得到每个服务函数的地址了。但和WIN32 不一样,这个表存放的并不是 SSDT 函数的完整地址,而是其相对于ServiceTableBase[Index]>>4 的数据(我称它为偏移地址),每个数据占四个字节,所以计算指定 Index 函数完整地址的公式是:ServiceTableBase[Index]>>4+ ServiceTableBase。代码如下(这个上一个博客已经说过):

ULONGLONG GetSSDTFuncCurAddr(ULONG id)
{
LONG dwtmp=0;
PULONG ServiceTableBase=NULL;
ServiceTableBase=(PULONG)KeServiceDescriptorTable->ServiceTableBase;
dwtmp=ServiceTableBase[id];
dwtmp=dwtmp>>4;
return (LONGLONG)dwtmp + (ULONGLONG)ServiceTableBase;
}

反之,从函数的完整地址获得函数偏移地址的代码也就出来了:

ULONG GetOffsetAddress(ULONGLONG FuncAddr)
{
ULONG dwtmp=0;
PULONG ServiceTableBase=NULL;
ServiceTableBase=(PULONG)KeServiceDescriptorTable->ServiceTableBase;
dwtmp=(ULONG)(FuncAddr-(ULONGLONG)ServiceTableBase);
return dwtmp<<4;
}

以下是我目前正在看的资料的作者 胡文亮 的吐槽:

知道了这一套机制,HOOK SSDT 就很简单了,首先获得待 HOOK 函数的序号Index,然后通过公式把自己的代理函数的地址转化为偏移地址,然后把偏移地址的数据填入 ServiceTableBase[Index]。也许有些读者看到这里,已经觉得胜利在望了,我当时也是如此。但实际上我在这里栽了个大跟头,整整郁闷了很长时间!因为我低估了设计这套算法的工程师的智商,我没有考虑一个问题,为什么 WIN64 的 SSDT 表存放地址的形式这么奇怪?只存放偏移地址,而不存放完整地址?难道是为了节省内存?这肯定是不可能的,要知道现在内存白菜价。那么不是为了节省内存,唯一的可能性就是要给试图挂钩 SSDT 的人制造麻烦!要知道,WIN64 内核里每个驱动都 不在同一个 B 4GB  里,而 4 字节的整数只能表示 4GB的范围!所以无论你怎么修改这个值,都跳不出 ntoskrnl 的手掌心。如果你想通过修改这个值来跳转到你的代理函数,那是绝对不可能的。 因为你的驱动的地址不 可能跟 l ntoskrnl  在同一个 B 4GB  里。然而,这位工程师也低估了我们中国人的智商,在中国有两句成语,这位工程师一定没听过,叫“明修栈道,暗渡陈仓”以及“上有政策,下有对策”。虽然不能直接用 4 字节来表示自己的代理函数所在的地址,但是还是可以修改这个值的。要知道,ntoskrnl 虽然有很多地方的代码通常是不会被执行的,比如 KeBugCheckEx。所以我的办法是: 修改这个偏移地址的值,使之跳转到  KeBugCheckEx ,然后在 x KeBugCheckEx  的头部写一个 2 12  字节的  mov - - jmp ,这是一个可以跨越  4GB
 ! 的跳转,跳到我们的函数里!代码如下:

VOID FuckKeBugCheckEx()
{
KIRQL irql;
ULONGLONG myfun;
UCHAR jmp_code[]="\x48\xB8\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xE0";
myfun=(ULONGLONG)Fake_NtTerminateProcess;
memcpy(jmp_code+2,&myfun,8);
irql=WPOFFx64();
memset(KeBugCheckEx,0x90,15);
memcpy(KeBugCheckEx,jmp_code,12);
WPONx64(irql);
} VOID HookSSDT()
{
KIRQL irql;
ULONGLONG dwtmp=0;
PULONG ServiceTableBase=NULL;
//get old address
NtTerminateProcess=(NTTERMINATEPROCESS)GetSSDTFuncCurAddr(41);
dprintf("Old_NtTerminateProcess: %llx",(ULONGLONG)NtTerminateProcess);
//set kebugcheckex
FuckKeBugCheckEx();
//show new address
ServiceTableBase=(PULONG)KeServiceDescriptorTable->ServiceTableBase;
OldTpVal=ServiceTableBase[41]; //record old offset value
irql=WPOFFx64();
ServiceTableBase[41]=GetOffsetAddress((ULONGLONG)KeBugCheckEx);
WPONx64(irql);
dprintf("KeBugCheckEx: %llx",(ULONGLONG)KeBugCheckEx);
dprintf("New_NtTerminateProcess: %llx",GetSSDTFuncCurAddr(41));
}

回调函数保护计算器不被结束:

NTSTATUS __fastcall Fake_NtTerminateProcess(IN HANDLE ProcessHandle, IN NTSTATUS ExitStatus)
{
PEPROCESS Process;
NTSTATUS st = ObReferenceObjectByHandle (ProcessHandle, 0, *PsProcessType, KernelMode, &Process, NULL);
DbgPrint("Fake_NtTerminateProcess called!");
if(NT_SUCCESS(st))
{
if(!_stricmp(PsGetProcessImageFileName(Process),"calc.exe"))
return STATUS_ACCESS_DENIED;
else
return NtTerminateProcess(ProcessHandle,ExitStatus);
}
else
return STATUS_ACCESS_DENIED;
}

完整测试代码:

#include <ntddk.h>
typedef struct _SYSTEM_SERVICE_TABLE{
PVOID ServiceTableBase;
PVOID ServiceCounterTableBase;
ULONGLONG NumberOfServices;
PVOID ParamTableBase;
} SYSTEM_SERVICE_TABLE, *PSYSTEM_SERVICE_TABLE; typedef struct _SERVICE_DESCRIPTOR_TABLE{
SYSTEM_SERVICE_TABLE ntoskrnl; // ntoskrnl.exe (native api)
SYSTEM_SERVICE_TABLE win32k; // win32k.sys (gdi/user)
SYSTEM_SERVICE_TABLE Table3; // not used
SYSTEM_SERVICE_TABLE Table4; // not used
}SERVICE_DESCRIPTOR_TABLE,*PSERVICE_DESCRIPTOR_TABLE; typedef NTSTATUS (__fastcall *NTTERMINATEPROCESS)(IN HANDLE ProcessHandle, IN NTSTATUS ExitStatus); NTKERNELAPI
UCHAR *
PsGetProcessImageFileName(PEPROCESS Process); PSYSTEM_SERVICE_TABLE KeServiceDescriptorTable;
NTTERMINATEPROCESS NtTerminateProcess=NULL;
ULONG OldTpVal; NTSTATUS __fastcall Fake_NtTerminateProcess(IN HANDLE ProcessHandle, IN NTSTATUS ExitStatus)
{
PEPROCESS Process;
NTSTATUS st = ObReferenceObjectByHandle (ProcessHandle, 0, *PsProcessType, KernelMode, &Process, NULL);
DbgPrint("Fake_NtTerminateProcess called!");
if(NT_SUCCESS(st))
{
if(!_stricmp(PsGetProcessImageFileName(Process),"calc.exe"))
return STATUS_ACCESS_DENIED;
else
return NtTerminateProcess(ProcessHandle,ExitStatus);
}
else
return STATUS_ACCESS_DENIED;
} KIRQL WPOFFx64()
{
KIRQL irql=KeRaiseIrqlToDpcLevel();
UINT64 cr0=__readcr0();
cr0 &= 0xfffffffffffeffff;
__writecr0(cr0);
_disable();
return irql;
} void WPONx64(KIRQL irql)
{
UINT64 cr0=__readcr0();
cr0 |= 0x10000;
_enable();
__writecr0(cr0);
KeLowerIrql(irql);
} ULONGLONG GetKeServiceDescriptorTable64()
{
PUCHAR StartSearchAddress = (PUCHAR)__readmsr(0xC0000082);
PUCHAR EndSearchAddress = StartSearchAddress + 0x500;
PUCHAR i = NULL;
UCHAR b1 = 0, b2 = 0, b3 = 0;
ULONG templong = 0;
ULONGLONG addr = 0;
for (i = StartSearchAddress; i<EndSearchAddress; i++)
{
if (MmIsAddressValid(i) && MmIsAddressValid(i + 1) && MmIsAddressValid(i + 2))
{
b1 = *i;
b2 = *(i + 1);
b3 = *(i + 2);
if (b1 == 0x4c && b2 == 0x8d && b3 == 0x15) //4c8d15
{
memcpy(&templong, i + 3, 4);
addr = (ULONGLONG)templong + (ULONGLONG)i + 7;
return addr;
}
}
}
return 0;
} ULONGLONG GetSSDTFuncCurAddr(ULONG id)
{
LONG dwtmp=0;
PULONG ServiceTableBase=NULL;
ServiceTableBase=(PULONG)KeServiceDescriptorTable->ServiceTableBase;
dwtmp=ServiceTableBase[id];
dwtmp=dwtmp>>4;
return (LONGLONG)dwtmp + (ULONGLONG)ServiceTableBase;
} ULONG GetOffsetAddress(ULONGLONG FuncAddr)
{
ULONG dwtmp=0;
PULONG ServiceTableBase=NULL;
ServiceTableBase=(PULONG)KeServiceDescriptorTable->ServiceTableBase;
dwtmp=(ULONG)(FuncAddr-(ULONGLONG)ServiceTableBase);
return dwtmp<<4;
} VOID FuckKeBugCheckEx()
{
KIRQL irql;
ULONGLONG myfun;
UCHAR jmp_code[]="\x48\xB8\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xE0";
myfun=(ULONGLONG)Fake_NtTerminateProcess;
memcpy(jmp_code+2,&myfun,8);
irql=WPOFFx64();
memset(KeBugCheckEx,0x90,15);
memcpy(KeBugCheckEx,jmp_code,12);
WPONx64(irql);
} /*
填写KeBugCheckEx的地址
在KeBugCheckEx填写jmp,跳到Fake_NtTerminateProcess
不能直接填写Fake_NtTerminateProcess的地址,因为它们不再同一个4GB
*/
VOID HookSSDT()
{
KIRQL irql;
ULONGLONG dwtmp=0;
PULONG ServiceTableBase=NULL;
//get old address
NtTerminateProcess=(NTTERMINATEPROCESS)GetSSDTFuncCurAddr(41);
dprintf("Old_NtTerminateProcess: %llx",(ULONGLONG)NtTerminateProcess);
//set kebugcheckex
FuckKeBugCheckEx();
//show new address
ServiceTableBase=(PULONG)KeServiceDescriptorTable->ServiceTableBase;
OldTpVal=ServiceTableBase[41]; //record old offset value
irql=WPOFFx64();
ServiceTableBase[41]=GetOffsetAddress((ULONGLONG)KeBugCheckEx);
WPONx64(irql);
dprintf("KeBugCheckEx: %llx",(ULONGLONG)KeBugCheckEx);
dprintf("New_NtTerminateProcess: %llx",GetSSDTFuncCurAddr(41));
} VOID UnhookSSDT()
{
KIRQL irql;
PULONG ServiceTableBase=NULL;
ServiceTableBase=(PULONG)KeServiceDescriptorTable->ServiceTableBase;
//set value
irql=WPOFFx64();
ServiceTableBase[41]=GetOffsetAddress((ULONGLONG)NtTerminateProcess); //OldTpVal;//直接填写这个旧值也行
WPONx64(irql);
//没必要恢复KeBugCheckEx的内容了,反正执行到KeBugCheckEx时已经完蛋了。
dprintf("NtTerminateProcess: %llx",GetSSDTFuncCurAddr(41));
} 调用:
DriverEntry里
KeServiceDescriptorTable = (PSYSTEM_SERVICE_TABLE)GetKeServiceDescriptorTable64();
HookSSDT();
DriverUnload里
UnhookSSDT();

执行结果:

下一节是UNHOOK SSDT 取消掉机器上别人的HOOK 这个资源的作者其实是写在一起的,我是分两次学习这个,所以总结两次笔记。声明一下,我这里所有的思路和笔记都来源于资料,我只是理解,把所有的例子都做了一遍,学习整理,有一些没理解了的我会去别的资料里面找,然后把所有相关整理在同一个资料里。大家一起学习,一起进步。

Win64 驱动内核编程-19.HOOK-SSDT的更多相关文章

  1. Win64 驱动内核编程-20.UnHook SSDT

    UNHOOK SSDT 要恢复 SSDT,首先要获得 SSDT 各个函数的原始地址,而 SSDT 各个函数的原始地址,自然是存储在内核文件里的.于是,有了以下思路: 1.获得内核里 KiService ...

  2. Win64 驱动内核编程-22.SHADOW SSDT HOOK(宋孖健)

    SHADOW SSDT HOOK HOOK 和 UNHOOK SHADOW SSDT 跟之前的 HOOK/UNHOOK SSDT 类似,区别是查找SSSDT的特征码,以及根据索引计算函数地址的公式,还 ...

  3. Win64 驱动内核编程-18.SSDT

    SSDT 学习资料:http://blog.csdn.net/zfdyq0/article/details/26515019 学习资料:WIN64内核编程基础 胡文亮 SSDT(系统服务描述表),刚开 ...

  4. Win64 驱动内核编程-23.Ring0 InLineHook 和UnHook

    Ring0 InLineHook 和UnHook 如果是要在R0里hook,作者的建议是InLine HOOK,毕竟SSDT HOOK 和 SHADOW SSDT HOOK比较麻烦,不好修改.目前R3 ...

  5. Win64 驱动内核编程-7.内核里操作进程

    在内核里操作进程 在内核里操作进程,相信是很多对 WINDOWS 内核编程感兴趣的朋友第一个学习的知识点.但在这里,我要让大家失望了,在内核里操作进程没什么特别的,就标准方法而言,还是调用那几个和进程 ...

  6. Win64 驱动内核编程-3.内核里使用内存

    内核里使用内存 内存使用,无非就是申请.复制.设置.释放.在 C 语言里,它们对应的函数是:malloc.memcpy.memset.free:在内核编程里,他们分别对应 ExAllocatePool ...

  7. Win64 驱动内核编程-8.内核里的其他常用

    内核里的其他常用 1.遍历链表.内核里有很多数据结构,但它们并不是孤立的,内核使用双向链表把它们像糖 葫芦一样给串了起来.所以遍历双向链表能获得很多重要的内核数据.举个简单的例子,驱 动对象 Driv ...

  8. Win64 驱动内核编程-2.基本框架(安装.通讯.HelloWorld)

    驱动安装,通讯,Hello World 开发驱动的简单流程是这样,开发驱动安装程序,开发驱动程序,然后安装程序(或者其他程序)通过通讯给驱动传命令,驱动接到之后进行解析并且执行,然后把执行结果返回. ...

  9. Win64 驱动内核编程-11.回调监控进线程句柄操作

    无HOOK监控进线程句柄操作 在 NT5 平台下,要监控进线程句柄的操作. 通常要挂钩三个API:NtOpenProcess.NtOpenThread.NtDuplicateObject.但是在 VI ...

随机推荐

  1. 通达OA 前台任意用户登录漏洞复现

    漏洞描述 通达OA是一套办公系统.通达OA官方于4月17日发布安全更新.经分析,在该次安全更新中修复了包括任意用户登录在内的高危漏洞.攻击者通过构造恶意请求,可以直接绕过登录验证逻辑,伪装为系统管理身 ...

  2. 过多if - else 的问题, 以及策略模式 + 反射解决方法

    策略模式解决if - else 的代码 业务场景: 外包企业的审批人需要审批打卡的场景: 审批人分为多种不同的级别,多种级别中具有方式相同但是内容不同的操作:审批. 原来场景: 有前端传来审批人参数, ...

  3. WBX24T2X CPEX国产化万兆交换板

      WBX24T2X是基于盛科CTC5160设计的国产化6U三层万兆CPEX交换板,提供24路千兆电口和2路万兆光口,采用龙芯 2K1000处理器.支持常规的L2/L3协议,支持Telnet.SNMP ...

  4. 09、集合set

    集合(set) 集合是一个无序.可变.不允许数据重复的容器 s = {11,22,33,'ccc'} 无序,无法通过索引取值 可变,可以添加和删除元素 s = {11,22,33,44} s.add( ...

  5. 一键获取linux内存、cpu、磁盘IO等信息脚本编写,及其原理详解

    更多linux知识,请关注公众号:一口Linux 一.脚本 今天主要分享一个shell脚本,用来获取linux系统CPU.内存.磁盘IO等信息. #!/bin/bash # 获取要监控的本地服务器IP ...

  6. 让你弄懂js中的闭包

    目录 闭包 闭包如何产生 闭包是什么 常见的闭包 闭包的作用 闭包的生命周期 闭包的应用 闭包的缺点 内存泄露 内存溢出 闭包面试题 闭包 之前在我执行上下文执行上下文栈这篇文章中,出现了这样一个题目 ...

  7. ImportError: No module named site

    cmd中执行python提示:ImportError: No module named site 运行python.exe Fatal Python error: initfsencoding: un ...

  8. 【Azure 应用程序见解】 Application Insights 对App Service的支持问题

    问题描述 Web App 发布后, Application Insights 收集不到数据了 问题分析 在应用服务(App Service)中收集应用的监控数据(如Request,Exception, ...

  9. python中gzip模块的使用

    gzip模块能够直接压缩和解压缩bytes-like类型的数据,同时也能实现对应格式文件的压缩与解压缩 一.数据压缩与解压缩 压缩 函数-gzip.compress(data, compresslev ...

  10. [深搜]C. 【例题3】虫食算

    C . [ 例 题 3 ] 虫 食 算 题目解析 正解 : Dfs + 剪枝 依题意,把样例以加法的形式展现出来. 根据加法的性质,可以得出有两种情况:有进位和没有进位的. 而从百位到最高位的结果,又 ...