SSDT的全称是System Services Descriptor Table(系统服务描述符表),在内核中的实际名称是KeServiceDescriptorTable。这个表已通过内核ntoskrnl.exe导出
SSDT用于处理应用层通过kernel32.dll下发的各个API操作请求
ntdll.dll中的API是一个简单的包装函数
当kernel32.dll中的API通过ntdll.dll会先完成对参数的检查,再调用一个中断(int 2Eh或者sysenter)实现从R3层进入R0层,并将要调用的服务号(也就是SSDT数组中的索引号index值)存放到寄存器EAX中
最后根据存放在EAX中的索引值在SSDT数组中调用指定的服务(Nt*系列函数)
SSDT表的结构定义如下
#pragma pack(1)
typedef struct ServiceDescriptorEntry
{
unsigned int *ServiceTableBase;//表的基地址
unsigned int *ServiceCounterTableBase;
unsigned int NumberOfServices;//表中服务函数的个数
unsigned char *ParamTableBase;
}ServiceDescriptorTableEntry_t,
*PServiceDescriptorTableEntry_t;
#pragma pack()
其中最重要的两个成员为ServiceTableBase(SSDT表的基地址)和NumberOfServices(表示系统中SSDT服务函数的个数)
SSDT表其实就是一个连续存放函数指针的数组
SSDT表的导入方法
__declspec(dllimport) ServiceDescriptorTableEntry_t KeServiceDescriptorTable;
SSDT表的基地址(数组的首地址)和SSDT函数的索引号(index)从而求出对应的服务服务函数地址
FuncAddr=KeServiceDescriptorable+4*index
(32位)
如果在x64平台上,SSDT中存放的是索引号所对应SSDT函数地址和SSDT表基地址的偏移量x16的值
因此计算公式为
FuncAddr=([KeServiceDescriptorable+index*4]>>4+KeServiceDescriptorable)
因此通用这个公式,只用知道SSDT表的首地址和对应函数的索引号,就可以将对应位置的服务函数替换为自己的函数,从而完成SSDT HOOK过程
Shadow SSDT的原理与此SSDT类似,它对应的表名为KeServiceDescriptorTableShadow是内核中未导出的另一张表
包含Ntoskrnel.exe和win32k.sys服务函数
主要处理来自User32.dll和GDI32.dll的系统调用
与SSDT不同,Shadow SSDT未导出,不能在自己的模块中导入和直接引用
- 挂钩该表中的
NtGdiBitBlt与NtGdiStretchBlt可以实现截屏保护 - 挂钩
NtUserSetWindowsHookEx可以防止或保护键盘钩子 - 挂钩与按键相关的函数
NtUserSendInput可以防止模拟按键 - 挂钩
NtUserFindWindowEx函数可以防止搜索函数 - 挂钩与窗口相关的函数
NtUserPostMessage,NtUserQueryWindow可以防止窗口被关闭
Shadow SSDT的Hook原理与SSDT HOOK原理一样,但是由于未导出
需要使用不同的方法来获得该表的地址以及服务函数的索引号。
例如,硬编码与KeServiceDescriptorTable在不同系统的位置偏移,搜索KeAddSystemServiceTable与KTHREAD.ServiceTable以及有效内存搜索等
KeServiceDescriptorTableShadow实际上也是一个SSDT结构数组
在XP中KeServiceDescriptorTableShadow表位于KeServiceDescriptorTable表上方偏移0x40处
KeServiceDescriptorTableShadow包含4个子结构
第一个子结构是ntoskrnl.exe,与KeServiceDescriptorTable指向相同
第二个子结构(比较重要)即win32k.sys(gdi/user support)的api