SSDT表
SSDT表示系统服务表,我们ring3调用的很多函数都会调用这个SSDT表。这个表就是一个把 Ring3 的 Win32 API 和 Ring0 的内核 API 联系起来。SSDT 并不仅仅只包含一个庞大的地址索引表,它还包含着一些其它有用的信息,诸如地址索引的基地址、服务函数个数等。通过修改此表的函数地址可以对常用 Windows 函数及 API 进行 Hook,从而实现对一些关心的系统动作进行过滤、监控的目的。一些 HIPS、防毒软件、系统监控、注册表监控软件往往会采用此接口来实现自己的监控模块。
1 | typedef struct _SERVICE_DESCRIPTOR_TABLE |
用本地内核调试查看SSDT表,第一个参数指向的地址存储的是全部内核函数
第二个参数代表ssdt表里面有多少个内核函数
第三个参数是一个指针指向一个地址,这里表示的是与上面的内核函数相对应的参数个数,例如第一个为10,参数个数就为0x10/4 = 4
系统中一共存在两个系统服务描述表,KeServiceDescriptorTable
和 KeServiceDescriptorTableShadow
,其中 KeServiceDescriptorTable
主要是处理来自 Ring3 层 Kernel32.dll 中的系统调用,而 KeServiceDescriptorTableShadow
则主要处理来自 User32.dll 和 GDI32.dll 中的系统调用,并且KeServiceDescriptorTable
在ntoskrnl.exe(Windows 操作系统内核文件,包括内核和执行体层)是导出的,而 KeServiceDescriptorTableShadow
则是没有被 Windows 操作系统所导出。
CR0寄存器
SSDT表所在的内存页属性是只读,没有写入的权限,所以需要把该地址设置为可写入,这样才能写入自己的函数,使用的是CR0寄存器关闭只读属性,开关在CR0寄存器的第16位。
可以看到这里使用32位寄存器,而在CR0寄存器中,我们重点关注的是3个标志位:
PE 是否启用保护模式,置1则启用。
PG 是否使用分页模式, 置1则开启分页模式, 此标志置1时, PE 标志也必须置1,否则CPU报异常。
WP WP为1时, 不能修改只读的内存页 , WP为0时, 可以修改只读的内存页。
驱动
原作者写了个简单驱动,其主要代码部分如下,就是替换SSDT表中相应函数地址。自己写的函数地址就是将相应进程设为拒绝访问,最后调用原函数。
不过,x64需要绕过PatchGuard保护,困难较大。