SSDT—Hook和MDL

SSDT—Hook和MDLSSDTHOOK 首先要明白 SSDTHOOK 就是自己再写一份函数 替换了 SSDT 表中的地址替换掉如何访问系统服务表呢 SSDT 的全称是 SystemServic 系统服务描述符表下面这个是个全局变量 kd gt ddKeServiceD SSDT 这个导出的声明一下就可以使用了 kd amp g

SSDT HOOK

如何访问系统服务表呢?

SSDT 的全称是 System Services Descriptor Table,系统服务描述符表

这个导出的 声明一下就可以使用了

kd> dd KeServiceDescriptorTableShadow(SSDT Shadow)

这个未导出 需要用其他的方式来查找

kd> dd KeServiceDescriptorTable 83fac9c0 83ec0d9c 00000000 00000191 83ec13e4 83fac9d0 00000000 00000000 00000000 00000000 83fac9e0 83f1f6af 00000000 025355a9 000000bb 83fac9f0 00000011 00000100 5385d2ba df 83faca00 83ec0d9c 00000000 00000191 83ec13e4 83faca10 956d6000 00000000 00000339 956d702c 83faca20 00000000 00000000 83faca24 00000340 83faca30 00000340 865fab00 00000007 00000000 

其中83ec0d9c是函数地址,00000191是函数个数,83ec13e4是函数参数个数,单位是一个字节,除以4就是函数地址表上对应函数的个数,第一个函数对第一个个数。

下面就是函数地址表

kd> dd 83ec0d9c 83ec0d9c 840bcc28 83f0340d 8404cb68 83e6788a 83ec0dac 840be4ff 83f403fa 8412eb05 8412eb4e 83ec0dbc bd  c1 84037b95 83ec0dcc 840c8b35  84074a56 cc 83ec0ddc 83fda928  8402b14e 8406da62 83ec0dec 840b9df1 8401b238 840b91fe 84038c0c 83ec0dfc 840ca5bc 8403b28f 840ca39c 840c2afc 83ec0e0c 8404d0f0 8410e657 840bfec9 840ca7ee 根据这个定义一个结构体 //下面这个是系统服务表的结构体 typedef struct _KSYSTEM_SERVICE_TABLE { PULONG ServiceTableBase; // 服务函数地址表基址  PULONG ServiceCounterTableBase; ULONG NumberOfService; // 服务函数的个数  PULONG ParamTableBase; // 服务函数参数表基址  } KSYSTEM_SERVICE_TABLE, *PKSYSTEM_SERVICE_TABLE; 

下面就是函数参数个数表除四就是真正个数

 kd> db 83ec13e4 83ec13e4 18 20 2c 2c 40 2c 40 44-0c 08 08 18 18 08 04 04 . ,,@,@D........ 83ec13f4 0c 0c 10 18 24 0c 2c 0c-18 10 0c 0c 0c 0c 0c 0c ....$.,......... 83ec1404 08 0c 18 18 14 18 0c 20-10 08 08 08 0c 08 0c 0c ....... ........ 83ec1414 08 04 04 0c 08 08 08 08-0c 04 04 20 08 10 0c 20 ........... ... 83ec1424 14 0c 2c 10 0c 0c 1c 10-20 20 10 38 10 14 10 20 ..,..... .8... 83ec1434 24 24 28 1c 1c 14 10 20-2c 10 34 28 18 2c 14 28 $$(.... ,.4(.,.( 83ec1444 08 0c 08 04 04 04 04 04-0c 04 08 28 00 04 04 1c ...........(.... 83ec1454 18 00 08 08 18 0c 14 18-08 18 0c 08 08 0c 04 00 ................ 

如何得到函数表地址

  //这个就是SSDT的结构体,即系统服务描述表,里面就是多个系统服务表 typedef struct _KSERVICE_TABLE_DESCRIPTOR { KSYSTEM_SERVICE_TABLE ntoskrnl; // ntoskrnl.exe 的服务函数  KSYSTEM_SERVICE_TABLE win32k; // win32k.sys 的服务函数(GDI32.dll/User32.dll 的内核支持)  KSYSTEM_SERVICE_TABLE notUsed1; KSYSTEM_SERVICE_TABLE notUsed2; }KSERVICE_TABLE_DESCRIPTOR, *PKSERVICE_TABLE_DESCRIPTOR; 

//这个是导出的,要到内核文件找,所以名字不能瞎起

下面的代码是查看到SSDT表地址的代码,可以打印显示

#include 
    #include 
    typedef struct _KSYSTEM_SERVICE_TABLE { PULONG ServiceTableBase; // 服务函数地址表基址  PULONG ServiceCounterTableBase; ULONG NumberOfService; // 服务函数的个数  PULONG ParamTableBase; // 服务函数参数表基址  } KSYSTEM_SERVICE_TABLE, *PKSYSTEM_SERVICE_TABLE; typedef struct _KSERVICE_TABLE_DESCRIPTOR { KSYSTEM_SERVICE_TABLE ntoskrnl; // ntoskrnl.exe 的服务函数  KSYSTEM_SERVICE_TABLE win32k; // win32k.sys 的服务函数(GDI32.dll/User32.dll 的内核支持)  KSYSTEM_SERVICE_TABLE notUsed1; KSYSTEM_SERVICE_TABLE notUsed2; }KSERVICE_TABLE_DESCRIPTOR, *PKSERVICE_TABLE_DESCRIPTOR; //导出由 ntoskrnl所导出的 SSDT extern PKSERVICE_TABLE_DESCRIPTOR KeServiceDescriptorTable;//这个是导出的,要到内核文件找,所以名字不能瞎起 VOID DriverUnload(PDRIVER_OBJECT pDriver) { UNREFERENCED_PARAMETER(pDriver); KdPrint(("My Dirver is unloading...")); } NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pPath) { UNREFERENCED_PARAMETER(pPath); KdPrint(("->%x \n", KeServiceDescriptorTable)); pDriver->DriverUnload = DriverUnload; return STATUS_SUCCESS; }

通过页表基址修改页属性

SSDT所在的物理页是只读的,如果要修改,先要修改页属性为可写:

1、第一种办法,用我们学过的知识,通过页表基址直接修改

 if(RCR4 & 0x00000020) { 
   //说明是2-9-9-12分页 KdPrint(("2-9-9-12分页 %p\n",RCR4)); KdPrint(("PTE1 %p\n",*(DWORD*)(0xC0000000 + ((HookFunAddr >> 9) & 0x007FFFF8)))); *(DWORD64*)(0xC0000000 + ((HookFunAddr >> 9) & 0x007FFFF8)) |= 0x02; KdPrint(("PTE1 %p\n",*(DWORD*)(0xC0000000 + ((HookFunAddr >> 9) & 0x007FFFF8)))); } else { 
   //说明是10-10-12分页 KdPrint(("10-10-12分页\n")); KdPrint(("PTE1 %p\n",*(DWORD*)(0xC0000000 + ((HookFunAddr >> 10) & 0x003FFFFC)))); *(DWORD*)(0xC0000000 + ((HookFunAddr >> 10) & 0x003FFFFC)) |= 0x02; KdPrint(("PTE2 %p\n",*(DWORD*)(0xC0000000 + ((HookFunAddr >> 10) & 0x003FFFFC)))); } 

通过修改CR0寄存器(WP位置改成0)

CR0寄存器

比如我们要SSDTHOOKNtOpenProcess,就要先通过IDA找到kernelBase的OpenProcess,在经过一系列查看最后在ntdll里找到ZWOpenProcess,所在位置看到了NtOpenProcess

.text:77F05D88 mov eax, 0BEh ; NtOpenProcess .text:77F05D8D mov edx, 7FFE0300h .text:77F05D92 call dword ptr [edx] .text:77F05D94 retn 10h
kd> dd KeServiceDescriptorTable 83f789c0 83e8cd9c 00000000 00000191 83e8d3e4 83f789d0 00000000 00000000 00000000 00000000 83f789e0 83eeb6af 00000000 025355a9 000000bb 83f789f0 00000011 00000100 5385d2ba df 83f78a00 83e8cd9c 00000000 00000191 83e8d3e4 83f78a10 95d46000 00000000 00000339 95d4702c 83f78a20 00000000 00000000 83f78a24 00000340 83f78a30 00000340 865fab00 00000007 00000000 kd> dd 83e8cd9c+4*BE 83e8d094 dc 84073fff 84061b37 83f8d0c7 83e8d0a4  83ff50c6  8405db6f 83e8d0b4 8406dd87 e4 84061c4e 84119e0f 83e8d0c4 f1  83ff3506  83e8d0d4 a2 84101fc2 a a 83e8d0e4 f 83fd5f60 83ff0a51 e4 83e8d0f4 aa  840a35a7 a1 83e8d104 84114a3e 84114e83 83ed2d34 84086b8c kd> u dc nt!NtOpenProcess: dc 8bff mov edi,edi de 55 push ebp df 8bec mov ebp,esp e1 51 push ecx e2 51 push ecx e3 64a mov eax,dword ptr fs:[00000124h] e9 8a803a010000 mov al,byte ptr [eax+13Ah] ef 8b4d14 mov ecx,dword ptr [ebp+14h] 
 PMDL MDLSystemCall;

定义了MDL的指针以后,我们要通过MAPPED系列的参数来使内存拥有可写性,然后锁定内存中的MDL,那么我们就要定义一个PVOID的指针,来供MmMap操作。

PVOID *MappedSCT;
MDLSystemCall = MmCreateMdl( NULL, //内存描述符列表 KeServiceDescriptorTable.ServiceTableBase,// 内存地址 KeServiceDescriptorTable.NumberOfServices*4);//内存长度 if(!MDLSystemCall) return STATUS_UNSUCCESSFUL;

建立了MDL,填充一下页数组

MmBuildMdlForNonPagedPool(MDLSystemCall);//建立内存页的MDL描述  MDLSystemCall->MdlFlags = MDLSystemCall->MdlFlags | MDL_MAPPED_TO_SYSTEM_VA; //设置MDL标记为可写 MappedSCT = MmMapLockedPages(MDLSystemCall, KernelMode); 

然后做自己的操作,比如这里的Hook

然后另外还有释放MDL的代码

if(MDSystemCall) { MmUnmapLockedPages(MappedSCT, MDSystemCall); IoFreeMdl(MDSystemCall); }

下面是SSDT-Hook代码

#include 
    #include 
    //1.找到系统服务表的函数地址表 //定义一个全局变量用来存放之前的NtOpenProcess地址 ULONG uOldNtOpenProcess; //有了地址还需要一个函数NtOpenProcess指针,用于调用原来的NtOpenProcess //定义修复和恢复页属性的函数 PMDL MDSystemCall; PVOID *MappedSCT; typedef NTSTATUS(*NTOPENPROCESS)( __out PHANDLE ProcessHandle, __in ACCESS_MASK DesiredAccess, __in POBJECT_ATTRIBUTES ObjectAttributes, __in_opt PCLIENT_ID ClientId ); typedef struct _KSYSTEM_SERVICE_TABLE { PULONG ServiceTableBase; // 服务函数地址表基址  PULONG ServiceCounterTableBase; ULONG NumberOfService; // 服务函数的个数  PULONG ParamTableBase; // 服务函数参数表基址  } KSYSTEM_SERVICE_TABLE, *PKSYSTEM_SERVICE_TABLE; typedef struct _KSERVICE_TABLE_DESCRIPTOR { KSYSTEM_SERVICE_TABLE ntoskrnl; // ntoskrnl.exe 的服务函数  KSYSTEM_SERVICE_TABLE win32k; // win32k.sys 的服务函数(GDI32.dll/User32.dll 的内核支持)  KSYSTEM_SERVICE_TABLE notUsed1; KSYSTEM_SERVICE_TABLE notUsed2; }KSERVICE_TABLE_DESCRIPTOR, *PKSERVICE_TABLE_DESCRIPTOR; //导出由 ntoskrnl所导出的 SSDT extern PKSERVICE_TABLE_DESCRIPTOR KeServiceDescriptorTable;//这个是导出的,要到内核文件找,所以名字不能瞎起 //准备用于替换的函数 NTSTATUS NTAPI MyNtOpenProcess(__out PHANDLE ProcessHandle, __in ACCESS_MASK DesiredAccess, __in POBJECT_ATTRIBUTES ObjectAttributes, __in_opt PCLIENT_ID ClientId ) { NTSTATUS Status; Status = STATUS_SUCCESS; //这里填自己的业务。。。各种过滤,修改返回结构等 KdPrint(("MyNtOpenProcess %x %x %x %x \n", ProcessHandle, DesiredAccess, ObjectAttributes, ClientId)); //后面这里填的是打开原来的函数,因为这个函数也要实现原来的功能,不然就乱套了,除非你自己在自己业务里实现了 return ((NTOPENPROCESS)uOldNtOpenProcess)(ProcessHandle, DesiredAccess, ObjectAttributes, ClientId); } void PageProtectOff() { //MDSystemCall = MmCreateMdl(NULL, KeServiceDescriptorTable->ntoskrnl.ServiceTableBase, KeServiceDescriptorTable->ntoskrnl.NumberOfService * 4); //if (!MDSystemCall) // //return STATUS_UNSUCCESSFUL; // return; //MmBuildMdlForNonPagedPool(MDSystemCall); //MDSystemCall->MdlFlags = MDSystemCall->MdlFlags | MDL_MAPPED_TO_SYSTEM_VA; //MappedSCT = MmMapLockedPages(MDSystemCall, KernelMode); __asm { //关闭内存保护 push eax; mov eax, cr0; and eax, ~0x10000; mov cr0, eax; pop eax; } } void PageProtectOn() { 解锁、释放MDL //if (MDSystemCall) //{ 
    // MmUnmapLockedPages(MappedSCT, MDSystemCall); // IoFreeMdl(MDSystemCall); //} __asm { //恢复内存保护 push eax; mov eax, cr0; or eax, 0x10000; mov cr0, eax; pop eax; } } //3.修改函数地址,准备个函数用来修改函数地址 void HookNtOpenProcess() { NTSTATUS Status; Status = STATUS_SUCCESS; PageProtectOff(); uOldNtOpenProcess = KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[0xBE]; KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[0xBE] = (ULONG)MyNtOpenProcess; PageProtectOn(); } //4.恢复 void UnHookNtOpenProcess() { PageProtectOff(); KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[0xBE] = (ULONG)uOldNtOpenProcess; PageProtectOn(); } VOID DriverUnload(PDRIVER_OBJECT pDriver) { UNREFERENCED_PARAMETER(pDriver); UnHookNtOpenProcess(); KdPrint(("My Dirver is unloading...")); } NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pPath) { UNREFERENCED_PARAMETER(pPath); KdPrint(("->%x \n", KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[0xBE]));//得到函数地址表 HookNtOpenProcess(); pDriver->DriverUnload = DriverUnload; return STATUS_SUCCESS; }

然后操作时可以用PChunt观察HOOk前后这个函数地址变化

这是hook前image

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/228291.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月16日 下午7:22
下一篇 2026年3月16日 下午7:22


相关推荐

  • matlab select函数,select函数

    matlab select函数,select函数阻塞方式 block 就是进程或是线程执行到这些函数时必须等待某个事件的发生 如果事件没有发生 进程或线程就被阻塞 函数不能立即返回 使用 Select 就可以完成非阻塞 non block 就是进程或线程执行此函数时不必非要等待事件的发生 一旦执行肯定返回 以返回值的不同来反映函数的执行情况 如果事件发生则与阻塞方式相同 若事件没有发生则返回一个代码来告知事件未发生 而进程或线程继续执行 所以效率较高

    2026年3月19日
    3
  • java 激活码【最新永久激活】

    (java 激活码)这是一篇idea技术相关文章,由全栈君为大家提供,主要知识点是关于2021JetBrains全家桶永久激活码的内容IntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,下面是详细链接哦~https://javaforall.net/100143.html1PA3AFINM4-eyJsa…

    2022年3月31日
    79
  • SessionId详解

    SessionId详解1 客户端用 cookie 保存了 sessionId 客户端用 cookie 保存了 sessionId 当我们请求服务器的时候 会把这个 sessionId 一起发给服务器 服务器会到内存中搜索对应的 sessionId 如果找到了对应的 sessionId 说明我们处于登录状态 有相应的权限 如果没有找到对应的 sessionId 这说明 要么是我们把浏览器关掉了 后面会说明为什么 要么 session 超时了

    2026年3月18日
    2
  • 一款漂亮的网址永久发布页源码

    一款漂亮的网址永久发布页源码介绍:好看导航页面可自动测域名延迟html页面,可以做个网址导航引导页面!网盘下载地址:http://www.zijiepan2.xyz/XR7VmgADZXm正文:公众号投票系统,暂不支持小程序注:只支持认证服务号和认证订阅号借权支付,非认证公众号无法使用礼物功能1,域名自动切换,随机域名,炮灰域名(启用随机域名时,简介:没有后台直接html修改自己的资料就行网盘地址:http://zijiepan2.xyz/vJMdhdMSBuE图片:…

    2022年10月21日
    5
  • 文件名太长无法删除怎么办

    文件名太长无法删除怎么办

    2021年10月24日
    49
  • 2020 CSP-S第二轮认证一等奖获奖名单

    2020 CSP-S第二轮认证一等奖获奖名单CCFCSP J S2020 第二轮提高级一等名单证书编号 省份 准考证号 姓名 性别 总分 学校 年级 CCF CSP JS2020 00001 安徽 AH 00120 洪朝阳 男 365 合肥市第一中学 高二 CCF CSP JS2020 00002 安徽 AH 00451 杨珖 男 355 安徽师范大学附属中学 高二 CCF CSP JS2020 00003 安徽 AH 00098 葛子越 男 340 安徽师范大学附属中学 高一 CCF CSP JS2020 00004 安徽 AH 00549 朱剑

    2026年3月17日
    2

发表回复

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

关注全栈程序员社区公众号