[转](51)SSDT HOOK 实现进程保护

 

一、回顾

 

在前面的课程里,我们逆向分析了 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 函数:

 

补充内容:SSDT HOOK 模板

 

相关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博客文章一键转载插件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值