SSDT:
SSDT全称: System Services Descriptor Table
即系统服务描述表
这个表将Ring3的Win32 API 和 Ring0 的内核 API联系起来。通过修改此表的函数地址可以对常用Windows函数及API进行Hook,从而实现对一些关心的系统动作进行过滤、监控等目的。
在NT4.0 以上的Windpws 操作系统中,默认存在两个系统服务描述表,两个调度表对应两类不同的系统服务。
分别为KeServiceDescriptorTable
和 KeServiceDescriptorTableShadow
其中KeServiceDescriptorTable
主要处理来自Ring3层Kernel32.dll中的系统调用。
而KeServiceDescriptorTableShadow
则主要处理来自User32.dll和GDI32.dll中的系统调用。
系统服务分发函数KiSystemService
场景:
例如进程保护(驱动)
端口隐藏
文件隐藏等
系统从Ring3 到 Ring0 的执行过程:
Hook前准备:
要Hook SSDT表,首选需要这个表是可写的,但是在XP之后的系统都是只读的,有三种方式修改内存保护机制。
- 更改注册表:
恢复页面保护:HKLM\SYSTEM\CurrentControlset\Control\Session Manger\Memory Management\EnforceWriteProtection=0
去掉页面保护:HKLM\SYSTEM\CurrentControlset\Control\Session Manger\Memory Management\DisablePagingExecutive=1
- 改变CR0寄存器的第1位
Windows对内存的分配,采用分页管理。其中有个CR0寄存器:
其中第1位叫做保护属性位,控制着页的读或写属性。如果为1,则可以读/写/执行;如果为0,则只能读/执行。
SSDT,IDT(中断描述符表)的页属性在默认情况下只读,可执行,但不能写。
//设置为不可写
void DisableWrite()
{
__try
{
_asm
{
mov eax, cr0
or eax, 10000h
mov cr0, eax
sti
}
}
__except(1)
{
DbgPrint("DisableWrite执行失败!");
}
}
// 设置为可写
void EnableWrite()
{
__try
{
_asm
{
cli
mov eax,cr0
and eax,not 10000h //and eax,0FFFEFFFFh
mov cr0,eax
}
}
__except(1)
{
DbgPrint("EnableWrite执行失败!");
}
}
- 通过Memory Descriptor List
如何获取SSDT中的函数地址
①获取指定服务的索引号:SYSCALL_INDEX
②获取指定服务的当前地址:SYSCALL_FUNCTION
SSDT hook 流程:
使用SSDT hook 实现进程保护:
#include <ntifs.h>
//内核之SSDT-HOOK
//系统服务表
typedef struct _KSYSTEM_SERVICE_TABLE
{
PULONG ServiceTableBase; //函数地址表的首地址
PULONG ServiceCounterTableBase;//函数表中每个函数被调用的次数
ULONG NumberOfService; //服务函数的个数
ULONG ParamTableBase; //参数个数表首地址
}KSYSTEM_SERVICE_TABLE;
//服务描述符
typedef struct _KSERVICE_TABLE_DESCRIPTOR
{
KSYSTEM_SERVICE_TABLE ntoskrnl;//ntoskrnl.exe的服务函数,SSDT
KSYSTEM_SERVICE_TABLE win32k; //win32k.sys的服务函数,ShadowSSDT
KSYSTEM_SERVICE_TABLE notUsed1;//暂时没用1
KSYSTEM_SERVICE_TABLE notUsed2;//暂时没用2
}KSERVICE_TABLE_DESCRIPTOR;
//定义HOOK的函数的类型
typedef NTSTATUS (NTAPI*FuZwOpenProcess)(
_Out_ PHANDLE ProcessHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_opt_ PCLIENT_ID ClientId
);
//自写的函数声明
NTSTATUS NTAPI MyZwOpenProcess(
_Out_ PHANDLE ProcessHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_opt_ PCLIENT_ID ClientId
);
//记录系统的该函数
FuZwOpenProcess g_OldZwOpenProcess;
//服务描述符表指针
KSERVICE_TABLE_DESCRIPTOR* g_pServiceTable = NULL;
//要保护进程的ID
ULONG g_Pid = 9527;
//安装钩子
void InstallHook();
//卸载钩子
void UninstallHook();
//关闭页写入保护
void ShutPageProtect();
//开启页写入保护
void OpenPageProtect();
//卸载驱动
void OutLoad(DRIVER_OBJECT* obj);
***驱动入口主函数***/
NTSTATUS DriverEntry(DRIVER_OBJECT* driver, UNICODE_STRING* path)
{
path;
KdPrint(("驱动启动成功!\n"));
//DbgBreakPoint();
//安装钩子
InstallHook();
driver->DriverUnload = OutLoad;
return STATUS_SUCCESS;
}
//卸载驱动
void OutLoad(DRIVER_OBJECT* obj)
{
obj;
//卸载钩子
UninstallHook();
}
//安装钩子
void InstallHook()
{
//1.获取KTHREAD
PETHREAD pNowThread = PsGetCurrentThread();
//2.获取ServiceTable表
g_pServiceTable = (KSERVICE_TABLE_DESCRIPTOR*)
(*(ULONG*)((ULONG)pNowThread + 0xbc));
//3.保存旧的函数
g_OldZwOpenProcess = (FuZwOpenProcess)
g_pServiceTable->ntoskrnl.ServiceTableBase[0xbe];
//4.关闭页只读保护
ShutPageProtect();
//5.写入自己的函数到SSDT表内
g_pServiceTable->ntoskrnl.ServiceTableBase[0xbe]
= (ULONG)MyZwOpenProcess;
//6.开启页只读保护
OpenPageProtect();
}
//卸载钩子
void UninstallHook()
{
//1.关闭页只读保护
ShutPageProtect();
//2.写入原来的函数到SSDT表内
g_pServiceTable->ntoskrnl.ServiceTableBase[0xbe]
= (ULONG)g_OldZwOpenProcess;
//3.开启页只读保护
OpenPageProtect();
}
//关闭页只读保护
void _declspec(naked) ShutPageProtect()
{
__asm
{
push eax;
mov eax, cr0;
and eax, ~0x10000;
mov cr0, eax;
pop eax;
ret;
}
}
//开启页只读保护
void _declspec(naked) OpenPageProtect()
{
__asm
{
push eax;
mov eax, cr0;
or eax, 0x10000;
mov cr0, eax;
pop eax;
ret;
}
}
//自写的函数
NTSTATUS NTAPI MyZwOpenProcess(
_Out_ PHANDLE ProcessHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_opt_ PCLIENT_ID ClientId
)
{
//当此进程为要保护的进程时
if (ClientId->UniqueProcess == (HANDLE)g_Pid &&
DesiredAccess == PROCESS_TERMINATE)
{
//设为拒绝访问
DesiredAccess = 0;
}
//调用原函数
return g_OldZwOpenProcess(
ProcessHandle,
DesiredAccess,
ObjectAttributes,
ClientId);
}
参考文章
https://blog.csdn.net/flydragonhero/article/details/61193692
https://blog.csdn.net/php_fly/article/details/9180591
https://blog.csdn.net/php_fly/article/details/9202379