一、回顾
在前面的课程里,我们逆向分析了 KiSystemService / KiFastCallEntry 的部分代码,我们发现这两个函数找系统服务表 SystemServiceTable 的方法是通过 _KTHREAD + 0xE0 来找。
今天我们来学习另一种方法,即通过 SSDT 表找系统服务表。
二、SSDT
SSDT是内核模块的导出变量,可通过 dd KeServiceDescriptorTable 查看:
kd> dd KeServiceDescriptorTable
80553fa0 80502b8c 00000000 0000011c 80503000
80553fb0 00000000 00000000 00000000 00000000
80553fc0 00000000 00000000 00000000 00000000
80553fd0 00000000 00000000 00000000 00000000
SSDT表由四部分组成,可以理解为4张系统服务表,其中只有第一张是有效的,它表示内核函数的系统服务表。
还有一张叫做 KeServiceDescriptorTableShadow 的未导出表,它和 KeServiceDescriptorTable 的区别是它第二项里是有值的,是win32k.sys的系统服务表:
kd> dd KeServiceDescriptorTableShadow
80553f60 80502b8c 00000000 0000011c 80503000
80553f70 bf999b80 00000000 0000029b bf99a890
80553f80 00000000 00000000 00000000 00000000
80553f90 00000000 00000000 00000000 00000000
KeServiceDescriptorTableShadow 是未导出的,我们通过windbg能看到是因为我们导入了PDB文件。在驱动程序里想要找到它,可以通过特征码的方式,先找到某个使用了 KeServiceDescriptorTableShadow 的函数,然后再通过偏移找到 KeServiceDescriptorTableShadow,具体做法本文就不演示了。
而要想找到 KeServiceDescriptorTable 就简单得多,因为它是导出的,所以我们只需要在驱动里声明一下就能用了:
// 系统服务表
typedef struct _KSYSTEM_SERVICE_TABLE
{
PULONG ServiceTableBase; // 函数地址表
PULONG ServiceCounterTableBase; // SSDT 函数被调用的次数
ULONG NumberOfService; // 函数个数
PULONG ParamTableBase; // 函数参数表
} KSYSTEM_SERVICE_TABLE, *PKSYSTEM_SERVICE_TABLE;
// SSDT表
typedef struct _KSERVICE_TABLE_DESCRIPTOR
{
KSYSTEM_SERVICE_TABLE ntoskrnl; // 内核函数
KSYSTEM_SERVICE_TABLE win32k; // win32k.sys 函数
KSYSTEM_SERVICE_TABLE unUsed1;
KSYSTEM_SERVICE_TABLE unUsed2;
} KSERVICE_TABLE_DESCRIPTOR, *PKSERVICE_TABLE_DESCRIPTOR;
extern PKSERVICE_TABLE_DESCRIPTOR KeServiceDescriptorTable; // 内核导出的全局变量
三、win32k.sys 系统服务表
0环代码执行时,KPCR+0x124是当前线程 _ETHREAD,所以不要觉得驱动不是进程,0环代码就不属于任何线程,只要0环代码执行,它总归是属于某个进程的。如果这个进程没有给 win32k.sys 系统服务表挂物理页,那么你访问它就会蓝屏。准确的说,只有进程使用了GDI函数,win32k.sys 系统服务表才会挂物理页。
要想访问 win32k.sys 系统服务表,做法是创建一个GUI进程,通过 IoControl 函数和驱动通信,驱动执行代码时,它所属的进程就是GUI进程,win32k.sys 系统服务表也就能访问了。
四、SSDT HOOK 实现保护记事本进程
题目要求:
将系统服务表中某个函数改成自己的函数,使任务管理器右键无法关闭自己,只有点击自己的关闭按钮才可以正常关闭。
补充内容:
方法是SSDT HOOK NtTerminateProcess 函数,SSDT HOOK 的模板我在之前的博客里写过,那个模板是 hook 了 NtOpenProcess 函数:
相关API:
NTSTATUS
ZwTerminateProcess(
IN HANDLE ProcessHandle,
IN NTSTATUS ExitStatus
);
NTSTATUS
ObReferenceObjectByHandle(
IN HANDLE Handle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_TYPE ObjectType OPTIONAL,
IN KPROCESSOR_MODE AccessMode,
OUT PVOID *Object,
OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL
);
运行结果:
用关闭按钮可以正常关闭 notepad 1,用任务管理器无法关闭 notepad 2
代码:
#include <ntddk.h>
#include <ntstatus.h>
/************************************************************************/
/* 宏定义 */
/************************************************************************/
// NtTerminateProcess 系统调用号
#define NTTERMINATEPROCESS_EAX 0x101
/************************************************************************/
/* 类型声明 */
/************************************************************************/
// 系统服务表
typedef struct _KSYSTEM_SERVICE_TABLE
{
PULONG ServiceTableBase; // 函数地址表
PULONG ServiceCounterTableBase; // SSDT 函数被调用的次数
ULONG NumberOfService; // 函数个数
PULONG ParamTableBase; // 函数参数表
} KSYSTEM_SERVICE_TABLE, *PKSYSTEM_SERVICE_TABLE;
// SSDT表
typedef struct _KSERVICE_TABLE_DESCRIPTOR
{
KSYSTEM_SERVICE_TABLE ntoskrnl; // 内核函数
KSYSTEM_SERVICE_TABLE win32k; // win32k.sys 函数
KSYSTEM_SERVICE_TABLE unUsed1;
KSYSTEM_SERVICE_TABLE unUsed2;
} KSERVICE_TABLE_DESCRIPTOR, *PKSERVICE_TABLE_DESCRIPTOR;
typedef NTSTATUS (*NTTERMINATEPROCESS) (HANDLE ProcessHandle, NTSTATUS ExitStatus);
/************************************************************************/
/* 函数声明 */
/************************************************************************/
VOID DriverUnload(PDRIVER_OBJECT pDriver);
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING reg_path);
VOID PageProtectOff();
VOID PageProtectOn();
VOID HookNtTerminateProcess();
VOID UnHookNtTerminateProcess();
NTSTATUS HbgNtTerminateProcess(HANDLE ProcessHandle, NTSTATUS ExitStatus);
/************************************************************************/
/* 全局变量 */
/************************************************************************/
extern PKSERVICE_TABLE_DESCRIPTOR KeServiceDescriptorTable; // 内核导出的全局变量
ULONG uOldNtTerminateProcess; // 旧的函数地址
/************************************************************************/
/* 函数定义 */
/************************************************************************/
// 驱动入口
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING reg_path)
{
// HOOK
HookNtTerminateProcess();
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
// 卸载驱动
VOID DriverUnload(PDRIVER_OBJECT pDriver)
{
UnHookNtTerminateProcess();
DbgPrint("Driver unloaded.\n");
}
// 关闭页保护
VOID PageProtectOff()
{
__asm
{
cli; // 关闭中断
mov eax, cr0;
and eax, not 0x10000; // WP位置0
mov cr0, eax;
}
}
// 开启页保护
VOID PageProtectOn()
{
__asm
{
mov eax, cr0;
or eax, 0x10000; // WP位置1
mov cr0, eax;
sti; // 恢复中断
}
}
// HOOK NtTerminateProcess
VOID HookNtTerminateProcess()
{
PageProtectOff();
uOldNtTerminateProcess = KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[NTTERMINATEPROCESS_EAX];
KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[NTTERMINATEPROCESS_EAX] = (ULONG)HbgNtTerminateProcess;
PageProtectOn();
}
// UnHOOK NtTerminateProcess
VOID UnHookNtTerminateProcess()
{
PageProtectOff();
KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[NTTERMINATEPROCESS_EAX] = uOldNtTerminateProcess;
PageProtectOn();
}
// 被修改的 NtTerminateProcess 函数,简单打印参数
NTSTATUS HbgNtTerminateProcess(HANDLE ProcessHandle, NTSTATUS ExitStatus)
{
PEPROCESS pEprocess;
NTSTATUS status;
PCHAR ImageFileName;
// 此API用法请看文档
status = ObReferenceObjectByHandle(ProcessHandle,FILE_ANY_ACCESS,*PsProcessType,KernelMode,&pEprocess,NULL);
if (!NT_SUCCESS(status))
{
return status;
}
// 根据镜像文件名判断是不是要保护的进程,字符串最大长度是16,超过就会截断,所以不用担心越界
ImageFileName = (PCHAR)pEprocess + 0x174;
if (strcmp(ImageFileName, "notepad.exe") == 0)
{
if (ProcessHandle == (HANDLE)0xFFFFFFFF)
{
// 通过关闭按钮关闭
return ((NTTERMINATEPROCESS)uOldNtTerminateProcess)(ProcessHandle, ExitStatus);
}
else
{
// 通过任务管理器关闭
DbgPrint("Terminate denied. %s: NtTerminateProcess(%x, %x)\n", ImageFileName, ProcessHandle, ExitStatus);
return STATUS_ACCESS_DENIED;
}
}
return ((NTTERMINATEPROCESS)uOldNtTerminateProcess)(ProcessHandle, ExitStatus);
}
---------------------
作者:hambaga
来源:CSDN
原文:https://blog.csdn.net/Kwansy/article/details/109490657
版权声明:本文为作者原创文章,转载请附上博文链接!
内容解析By:CSDN,CNBLOG博客文章一键转载插件