SSDTHook实现解析(x86)

在说到SSDTHook实现之前,我们首先来了解一下Win32API的调用过程,我们以ReadFile()为例,来看看从Ring3层到Ring0层的调用过程:
在一个Ring3进程Test.exe中调用ReadFile()函数,此时我们调用的是kernel32.dll中的导出函数,接下来ReadFile()会调用到位于ntdll.dll中的NtReadFile()或者ZwReadFile(),在ntdll.dll中的这两个函数实现是一样的,通过Windbg调试器查看如下:
在这里插入图片描述
在这里插入图片描述

通过调用ntdll.dll中的NtReadFile()函数会将系统调用号放入EAX寄存器中,以便切入内核时在SSDT表中找到对应的内核函数;如果内核采用自陷指令int 0x2e时,将堆栈上的参数块起始地址放入EDX寄存器中,以便于在内核层拷贝用户层所传递参数;接下来会通过int 0x2E自陷指令,切入到内核中去,此处的0x2E是系统中断向量表中的中断向量,也是IDT表的数组下标,在IDT表的对应偏移处找到中断程序入口地址,执行中断处理函数KiSystemService()。如果采用快速调用指令sysenter时,将堆栈指针保存在EDX寄存器中,进入内核,系统的的处理函数就是KiFastCallEntry()。
切入内核之后,会执行内核层(也就是内核模块ntoskrnl.exe)的ZwReadFile()函数,取出系统调用号,在SSDT表中找到服务表基地址,根据调用号找到对应的函数,即为最终实现系统调用实现NtReadFile()。
所以我们想要进行SSDTHook,只需要根据索引找到SSDT表中对应Nt系列函数的地址,修改索引所对应的函数地址为我们的Fake函数,即可实现SSDTHook。
以上为原理分析,下面进入正题,看代码:
此处代码的重点在于构建MDL,而构建MDL的目的是为了修改内存属性,因为原本的属性是写保护,将目标虚拟内存对应的内容拷贝一份到新建的MDL虚拟内存块上,而这两块内存是映射到同一片物理内存上。最终使用InterlockedExchange原子锁操作方式用Fake函数地址替换了目标函数地址。

NTSTATUS SSDTHook(
	PUCHAR TargetFunctionAddress,
	PVOID  FakeFunctionAddress,
	PVOID* OriginalFunctionAddress)
{
	NTSTATUS Status = STATUS_SUCCESS;
	__Mdl = MmCreateMdl(NULL,
		KeServiceDescriptorTable->ServiceTableBase,
		KeServiceDescriptorTable->NumberOfService * sizeof(PVOID));
	if (!__Mdl)
	{
		return STATUS_UNSUCCESSFUL;
	}
	MmBuildMdlForNonPagedPool(__Mdl);
	//修改属性为可写
	__Mdl->MdlFlags = __Mdl->MdlFlags | MDL_MAPPED_TO_SYSTEM_VA;
	__ServiceTableBase = MmMapLockedPages(__Mdl, KernelMode);
	if (__ServiceTableBase)
	{
		*OriginalFunctionAddress = (PVOID)InterlockedExchange(
			(PLONG)(&__ServiceTableBase[GetSSDTIndex(TargetFunctionAddress)]),
			(LONG)FakeFunctionAddress);
	}
	return Status;
}
复制代码

也可以使用嵌入式汇编的方法,修改内存写保护属性,那就是修改CR0寄存器得第16位为0,则废掉写保护

	_asm
	{
		cli;                 //禁止任何中断发生
		push eax;
		mov  eax, cr0;
		mov  Attribute, eax;
		and  eax, 0xFFFEFFFF; // CR0 16 BIT = 0 
		mov  cr0, eax;
		pop  eax;
	};
复制代码

上面代码用到了宏GetSSDTIndex是得到系统服务号,也就是函数地址表的索引,这里对函数指针强转为PUCHAR类型再加1是有原因的,我觉得有必要解释以下

#define GetSSDTIndex(ServiceFunction)         (*(PULONG)((PUCHAR)ServiceFunction + 1))
复制代码
2: kd> u ZwReadFile
nt!ZwReadFile:
84841354 b811010000      mov     eax,111h
84841359 8d542404        lea     edx,[esp+4]
8484135d 9c              pushfd
8484135e 6a08            push    8
84841360 e859130000      call    nt!KiSystemService (848426be)
84841365 c22400          ret     24h
复制代码

我想大家看一下这段Windbg调试器的内容就能明白为何强转成PUCHAR类型的指针再加一了吧,就是为了越过初始的b8指令,取出函数索引0111h。
至此,Hook结束,其实也没什么难的,重要的是懂得其中Hook得原理。Hook完毕时候,等到驱动卸载时候,一定要注意恢复SSDT表嗷!!
在下才疏学浅,写帖子过程中难免存在疏忽和纰漏,还请各位大牛指出?。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享