Windows内核新手上路1——挂钩SSDT
这个系列记录学习我学习windows内核的点点滴滴,高手请直接无视。
文章核心内容:挂钩SSDT中函数列NtOpenProcess,NtDuplicateObject,NtCreateThread,NtOpenThread,NtWriteVirtualMemory,过滤进程操作来保护目标进程空间。
SSDT的全称是System Services Descriptor Table,系统服务描述符表。这个表把ring3的Win32 API和ring0的内核API联系起来。SSDT并不仅仅只包含一个庞大的地址索引表,它还包含着一些其它有用的信息,诸如地址索引的基地址、服务函数个数等。
SSDT管的是与系统相关的函数(kernel32.dll、ntdll.dll)。通过修改此表的函数地址可以对常用windows函数及API进行hook,从而实现对一些关心的系统动作进行过滤、监控的目的。一些HIPS、防毒软件、系统监控、注册表监控软件往往会采用此接口来实现自己的监控模块。
SSDT在Win32子系统的内核实现是NTOSKRNL.EXE,导出表名是 KeServiceDescriptorTable,对应系统进程一般有System
SSDT结构:
typedef struct tagSSDT {
PULONG pSSDTBase; //SSDT在内存中的基址
PVOID pServiceCounterTable;
ULONG uNumberOfServices; //SSDT项个数
PUCHAR pParamTableBase;
} SSDT, *PSSDT; //SSDT
1 如何挂钩SSDT
首先关闭CR0写保护(通过改变CR0寄存器的WP位),然后用新的函数地址替换原来SSDT中的函数地址,最后恢复CR0写保护。
挂钩SSDT中函数列NtOpenProcess,NtDuplicateObject,NtCreateThread,NtOpenThread,NtWriteVirtualMemory,过滤进程操作来保护目标进程空间。
1.1NtOpenProcess
在Windows操作系统中,kernel32!OpenProcess通过调用ntdll!NtOpenProcess最终转到内核态的nt!NtOpenProcess,通过挂钩NtOpenProcess,可以过滤和拦截进程打开其他进程的操作。
在自定义的NtOpenProcess函数中,首先获取要打开的进程PID,判断该PID是否是被保护的进程,如果是被保护的进程,则去掉打开权限中的相应权限,如果是打开本系统R3进程,还要去掉结束进程的权限,最终,再调用真实的NtOpenProcess函数,最后返回给R3的进程句柄就无法读写操作受保护的进程,恶意程序也无法结束目标进程。
通过进程句柄获取PID 判断是否受保护的PID 如果是 做一些权限的处理…. 调用真实NtOpenProcess 如果否 调用真实的NtCreateThread |
代码1.1 MyNtOpenProcess
1.2NtDuplicateObject
Windows系统提供了一个函数DuplicateHandle,用于从其他进程复制一个句柄到当前进程,而且csrss.exe进程会保存所有进程的句柄,通过调用DuplicateHandle从csrss中复制句柄,可以间接的达到调用OpenProcess获取目标进程句柄的目的。
在定义的NtDuplcateObject函数中,首先调用真实的NtDuplicateObject,如果调用成功,判断最后返回的句柄是不是受保护进程的句柄,如果是受保护的进程句柄,则关闭之。通过这样的过滤,恶意程序无法通过调用DuplicateHandle来打开受保护的进程。
1.3NtCreateThread
R3中在进程中和其他进程中创建线程(分别调用CreateThread和CreateRemoteThread)最终通过ntdll!NtCreateThread转到内核态的nt!CreateThread,通过过滤nt!CreateThread可以防止恶意程序在受保护的进程中创建远程线程(即调用CreateRemoteThread)。实现的MyNtCreateThread处理流程如代码1.2所示:
通过进程句柄获取PID 判断是否受保护的PID 如果是 返回STATUS_ACCESS_DENIED 如果否 调用真实的NtCreateThread |
代码1.2 MyNtCreateThread
1.4NtOpenThread
在windows操作系统中,通过调用OpenThread可以获取目标线程的句柄,进一步通过此句柄操作目标线程。防密码窃取系统挂钩了NtOpenThread来防止恶意程序操作受保护进程的线程。
在自定义的NtOpenThread函数中,首先获取线程所在进程的PID,然后判断该PID是否被保护,如果是被保护的PID,则直接返回STATUS_ACCESS_DENIED,否则调用真实的NtOpenThread。
1.5NtWriteVirtualMemory
在windows操作系统中,进程可以调用kernel32!WriteProcessMemory来写其他进程内存,kernel32!WriteProcessMemory通过ntdll!NtWriteVirtualMemory转到内核态的nt!NtWriteVirtualMemory。通过挂钩nt!NtWriteVirtualMemory来防目恶意程序写受保护的进程内存。伪代码如代码1.3所示。
通过进程句柄获取PID 判断是否受保护的PID 如果是 返回STATUS_ACCESS_DENIED 如果否 调用真实的NtWriteVirtualMemory |
代码1.3 MyNtWriteVirtualMemory