Windows驱动开发学习笔记(五)—— SSDT HOOK
前言
一、学习自
滴水编程达人
中级班课程,官网:https://bcdaren.com
二、海东老师牛逼!
系统服务表
描述:
- 全称:SystemServiceTable(系统服务表)
- 可以通过系统服务描述符表访问系统服务表
系统服务描述符表
描述:
- 全称:System Services Descriptor Table(SSDT)
- 位于内核文件中,可以通过其导出表找到该表
在WinDbg中查看:
函数地址表(ServiceTable):
参数个数表(ArgmentTable):
实验一:通过代码获取SSDT表地址
#include <ntddk.h>
#include <ntstatus.h>
typedef struct _KSYSTEM_SERVICE_TABLE
{
PULONG ServiceTableBase; // 服务函数地址表基址
PULONG ServiceCounterTableBase; // SSDT函数被调用的次数
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;
// KeServiceDescriptorTable 是 ntoskrnl.exe 所导出的全局变量 申明一下就可以直接使用了
extern PKSERVICE_TABLE_DESCRIPTOR KeServiceDescriptorTable;
//卸载函数
VOID DriverUnload(PDRIVER_OBJECT driver)
{
DbgPrint("驱动程序已停止.\r\n");
}
//驱动程序入口函数,相当于控制台的main函数
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject,PUNICODE_STRING RegistryPath)
{
DbgPrint("驱动程序已运行.\r\n");
KdPrint(("--> %x \n", KeServiceDescriptorTable->ntoskrnl.ServiceTableBase));
//设置一个卸载函数 便于退出
DriverObject->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))));
}
方法2:修改CR0寄存器
描述:CR0寄存器的第16位叫做保护属性位,控制着页的读/写属性
VOID PageProtectOn()
{
__asm{
mov eax,cr0
or eax,10000h
mov cr0,eax
sti
}
}
VOID PageProtectOff()
{
__asm{
cli
mov eax,cr0
and eax,not 10000h
mov cr0,eax
}
}
实验二:SSDT HOOK
第一步:编译如下代码
#include <ntddk.h>
#include <ntstatus.h>
ULONG uOldNtOpenProcess; //存储原来的NtOpenProcess地址
typedef NTSTATUS (*NTOPENPROCESS)(
PHANDLE ProcessHandle,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes,
PCLIENT_ID ClientId);
VOID PageProtectOn()
{
__asm{//开启内存保护
mov eax,cr0
or eax,10000h
mov cr0,eax
sti
}
}
VOID PageProtectOff()
{
__asm{//关闭内存保护
cli
mov eax,cr0
and eax,not 10000h
mov cr0,eax
}
}
//1. 找到系统服务表:函数地址表
typedef struct _KSYSTEM_SERVICE_TABLE
{
PULONG ServiceTableBase; // 服务函数地址表基址
PULONG ServiceCounterTableBase; // SSDT函数被调用的次数
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;
// KeServiceDescriptorTable 是 ntoskrnl.exe 所导出的全局变量 申明一下就可以直接使用了
extern PKSERVICE_TABLE_DESCRIPTOR KeServiceDescriptorTable;
//2. 准备用于替换的函数
NTSTATUS MyNtOpenProcess(
PHANDLE ProcessHandle,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes,
PCLIENT_ID ClientId
){
//自己的业务..各种过滤,或者修改返回结构
NTSTATUS status;
status = STATUS_SUCCESS;
KdPrint(("%x %x %x %x \n", ProcessHandle, DesiredAccess, ObjectAttributes, ClientId));
return ((NTOPENPROCESS)uOldNtOpenProcess)(ProcessHandle, DesiredAccess, ObjectAttributes, ClientId);
}
//3. Hook
NTSTATUS HookNtOpenPRocess()
{
NTSTATUS status;
status = STATUS_SUCCESS;
PageProtectOff();
uOldNtOpenProcess = KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[0x7A];
KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[0x7A] = (ULONG)MyNtOpenProcess;
PageProtectOn();
return status;
}
//4. 恢复
NTSTATUS UnHookNtOpenPRocess()
{
NTSTATUS status;
status = STATUS_SUCCESS;
PageProtectOff();
KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[0x7A] = (ULONG)uOldNtOpenProcess;
PageProtectOn();
return status;
}
//卸载函数
VOID DriverUnload(PDRIVER_OBJECT driver)
{
UnHookNtOpenPRocess();
DbgPrint("驱动程序已停止.\r\n");
}
//驱动程序入口函数,相当于控制台的main函数
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject,PUNICODE_STRING RegistryPath)
{
DbgPrint("驱动程序已运行.\r\n");
HookNtOpenPRocess();
//设置一个卸载函数 便于退出
DriverObject->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
第二步:查看PCHunter(未HOOK)
第三步:运行驱动程序