API钩取技术研究(四)——SSDT Hook

本文介绍了如何通过SSDTHook技术来实现进程保护,详细阐述了基本原理,包括系统服务描述符表的作用,以及如何找到并修改SSDT以阻止用户在任务管理器中终止特定进程。实验过程涉及驱动加载、关闭页只读保护和自定义钩取函数的实现,特别是利用CR0寄存器控制内存页的写保护属性。
摘要由CSDN通过智能技术生成

SSDT Hook

简单实现一个SSDT进程保护,使得用户无法在任务管理器中终止指定的进程。

实验环境

基本原理

  1. SSDT 的全称是 System Services Descriptor Table,系统服务描述符表。这个表就是一个把 Ring3 的 Win32 API 和 Ring0 的内核 API 联系起来。如图1所示,SSDT 并不仅仅只包含一个庞大的地址索引表,它还包含着一些其它有用的信息,诸如地址索引的基地址、服务函数个数等。通过修改此表的函数地址可以对常用 Windows 函数及 API 进行 Hook,从而实现对一些关心的系统动作进行过滤、监控的目的。一些 HIPS、防毒软件、系统监控、注册表监控软件往往会采用此接口来实现自己的监控模块。
    在这里插入图片描述

  2. 本文要简单实现一个SSDT进程保护,使得用户无法在任务管理器中终止指定的进程,需要关注OpenProcess()这个API。它在Ring3也就是用户层调用,它保存一些信息传入到Ring0内核层后系统实际调用的是ZwOpenProcess函数。在Kernel32模块里面查找OpenProcess函数,可以看到经过2个jmp后进入下一层,找到一个call进入其中便是ZwOpenProcess的调用。
    在这里插入图片描述
    ZwOpenProcess() API函数调用时在进入关键函数之前有一句汇编代码MOV EAX,0xBE,这是用EAX保存一个调用号0xBE。
    在这里插入图片描述
    在调用ZwOpenProcess()时调用号就是0xBE,那么我们如果通过调用号找到了函数地址,把这个地址替换成自定义的钩取函数,这样就能完成SSDT Hook,类似于Ring3的IAT Hook。
    在这里插入图片描述

  3. 在Windows内核中设计了两张系统服务描述符表,一张表是上述所说的SSDT,它只保存非用户界面相关的系统服务(例如创建文件、创建进程等);另一张表称为ShadowSSDT,它专门用于保存和用户界面相关的服务(例如创建窗口等),这两张表在内核中都使用了同一个结构体的表示:
    在这里插入图片描述
    但实际上系统共有4个系统服务描述符,其中2个就是上述的2张表,另外2个没有被使用,可能是留着将来备用的:
    在这里插入图片描述

  4. 进入Ring0时调用号是EAX传递的,但这个调用号并不只是一个普通的数字作为索引序号,系统会把他用32位数据表示,拆分成19:1:12的格式:
    在这里插入图片描述
    低12位组成一个真正的索引号,第12位表示服务表号,13-31位没有使用。
    而进入内核后调用哪一张表,就由调用号中的第12位决定,为0则调用SSDT,为1则调用Shadow SSDT。所以想要对SSDT进行Hook,首先要找到SSDT。如图6,SSDT存在于KTHREAD结构体的偏移为0xbc的一个字段中,可双机调试使用windbg查看,输入dt_kthread,然后再找到偏移为0xbc的字段。
    在这里插入图片描述

  5. 找到SSDT后,想要修改SSDT,还需要关闭所在的内存页只读保护属性。因为Windows XP及其以后的系统将一些重要的内存页设置为只读属性,这样就算有权力访问该表也不能随意对其修改,例如SSDT、IDT等。但这种方法很容易被绕过,我们只要修改其属性为可写属性就即可,不过当我们的事情做完后记得把它们恢复为只读属性,不将这些部分修改为然会造成一些很难预料到的后果(比如蓝屏)。
    使用CR0控制寄存器关闭只读属性。控制寄存器是一些特殊的寄存器,它们可以控制CPU的一些重要特性。 如图7,CR0寄存器在486的处理器版本之后被加入了“写保护”(Write Protect,WP)位,WP位控制是否允许处理器向标记为只读属性的内存页写入数据。WP位为0,禁用写保护的功能;WP位为1,则开启写保护的功能。因此,只要将cr0的第16位WP位置0就可以禁用写保护,置1则可将其恢复。
    在这里插入图片描述

实验过程

简单实现一个SSDT进程保护,使得用户无法在任务管理器中终止指定的进程,总体思路如下:
在这里插入图片描述

Ⅰ. 驱动加载DriverEntry()和驱动卸载OutLoad()

在DriverEntry()中如果系统成功加载SSDTHook.sys文件,会在系内核中输出调试信息“驱动启动成功!”。
在这里插入图片描述

Ⅱ. InstallHook()挂钩和UnistallHook脱钩()

想要对SSDT进行HOOK,首先要找到SSDT。SSDT存在于KTHREAD结构体的偏移为0xbc的一个字段中,使用PsGetCurrentThread()函数可获取当前KTHREAD的首地址。另外,需要注意的是SSDT所在的内存页属性是只读,没有写入的权限,所以需要把该地址设置为可写入,这样才能写入自己的函数。
在这里插入图片描述

Ⅲ. ShutPageProtect()关闭页只读保护

使用汇编语言控制CR0寄存器实现ShutPageProtect()关闭页只读保护。__declspec(naked)是用来告诉编译器函数代码的汇编语言为自己的所写,不需要编译器添加任何汇编代码注意,所以一定要记得在开始的时候保存上下文标志位(压栈),在结束的时候要记得恢复上下文(出栈),并且在结尾要加上ret命令。and eax, ~0x10000(and eax 0b01111111111111111)可以使EAX寄存器上的操作数第16位复0,相反or eax, 0x10000(or eax, 0b10000000000000000)可以使EAX寄存器上的操作数第16位置1。
在这里插入图片描述

Ⅳ. 自定义钩取函数MyZwOpenProcess()

简单起见,我将要保护的进程PID硬性编码为cmd.exe的PID。自定义钩取函数MyZwOpenProcess()的实现思路类似于IAT Hook中自定义钩取函数的实现思路,通过修改进程参数Desired Access的值为0,然后再将修改的参数传入正常调用的原ZwOpenProcess() API即可实现进程保护。
在这里插入图片描述

实现演示

SSDT-Hook演示

参考:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值