详细过程】
这次主要说说核心层的hook。包括SSDT-hook,IDT-hook,sysenter-hook。欢迎讨论,指正!内核层需要驱动,有这方面的基础最好,如果不会,了解下其中的思路也可以的。
II. SSDT-hook,IDT-hook,sysenter-hook
一.SSDT-hook
(一)一般思路:
1.先来了解一下,什么是SSDT
SSDT既System Service Dispath Table。在了解他之前,我们先了解一下NT的基本组建。在 Windows NT 下,NT 的 executive(NTOSKRNL.EXE 的一部分)提供了核心系统服务。各种 Win32、OS/2 和 POSIX 的 APIs 都是以 DLL 的形式提供的。这些dll中的 APIs 转过来调用了 NT executive 提供的服务。尽管调用了相同的系统服务,但由于子系统不同,API 函数的函数名也不同。例如,要用Win32 API 打开一个文件,应用程序会调用 CreateFile(),而要用 POSIX API,则应用程序调用 open() 函数。这两种应用程序最终都会调用 NT executive 中的 NtCreateFile() 系统服务。
系统组建.jpg
用户模式(User mode)的所有调用,如Kernel32,User32.dll, Advapi32.dll等提供的API,最终都封装在Ntdll.dll中,然后通过Int 2E或SYSENTER进入到内核模式,通过服务ID,在System Service Dispatcher Table中分派系统函数,举个具体的例子,再如下图
从上可知,SSDT就是一个表,这个表中有内核调用的函数地址。从上图可见,当用户层调用FindNextFile函数时,最终会调用内核层的NtQueryDirectoryFile函数,而这个函数的地址就在SSDT表中,如果我们事先把这个地址改成我们特定函数的地址,那么,哈哈。。。。。。。下来详细了解一下,SSDT的结构,如下图:
KeServiceDescriptorTable:是由内核(Ntoskrnl.exe)导出的一个表,这个表是访问SSDT的关键,具体结构是 还有一种这样的表,叫KeServiceDescriptorTableShadow,它主要包含GDI服务,也就是我们常用的和窗口,桌面有关的,具体存在于Win32k.sys。在如图:
服务分发.jpg
右侧的服务分发就通过KeServiceDescriptorTableShadow。
那么下来该咋办呢?下来就是去改变SSDT所指向的函数,使之指向我们自己的函数。
2.Hook前的准备-改变SSDT内存的保护
系统对SSDT都是只读的,不能写。如果试图去写,等你的就是蓝脸。一般可以修改内存属性的方法有:通过cr0寄存器及Memory Descriptor List(MDL)。
(1)改变CR0寄存器的第1位
Windows对内存的分配,是采用的分页管理。其中有个CR0寄存器,如下图:
CR0.jpg
其中第1位叫做保护属性位,控制着页的读或写属性。如果为1,则可以读/写/执行;如果为0,则只可以读/执行。SSDT,IDT的页属性在默认下都是只读,可执行的,但不能写。所以现在要把这一位设置成1。
(2)通过Memory Descriptor List(MDL)
也就是把原来SSDT的区域映射到我们自己的MDL区域中,并把这个区域设置成可写。MDL的结构: 首先需要知道KeServiceDscriptorTable的基址和入口数,这样就可以用MmCreateMdl创建一个有起始地址和大小的内存区域。然后把这个MDL结构的flag改成
MDL_MAPPED_TO_SYSTEM_VA ,那么这个区域就可以写了。最后把这个内存区域调用MmMapLockedPages锁定在内存中。大体框架如下:
//先声明一个System Service Descriptor Table,我们知道SSDT及SSPT都从这个表中指向 现在遇到的第一个问题解决了,但接着面临另外一个问题,如何获得SSDT中函数的地址呢?
3.四个有用的宏 4.小试牛刀:利用SSDT Hook隐藏进程
我们所熟知的任务管理器,能察看系统中的所有进程及其他很多信息,这是由于调用了一个叫ZwQuerySystemInformation的内核函数,具体结构是:
NTSTATUS NewZwQuerySystemInformation(
IN ULONG SystemInformationClass, //如果这值是5,则代表系统中所有进程信息
IN PVOID SystemInformation, //这就是最终列举出的信息,和上面的值有关
IN ULONG SystemInformationLength, //后两个不重要
OUT PULONG ReturnLength)
如果用我们自己函数,这个函数可以把我们关心的进程过滤掉,再把它与原函数调换,则可达到隐藏的目的,大体思路如下:
(1) 突破SSDT的内存保护,如上所用的MDL方法
(2) 实现自己的NewZwQuerySystemInformation函数,过滤掉以某些字符开头的进程
(3) 用上面介绍的宏来交换ZwQuerySystemInformation与我们自己的New*函数
(4) 卸载New*函数,完成
具体实例:来自Rootkit.com,我做了注释,代码也很精小。
这次主要说说核心层的hook。包括SSDT-hook,IDT-hook,sysenter-hook。欢迎讨论,指正!内核层需要驱动,有这方面的基础最好,如果不会,了解下其中的思路也可以的。
II. SSDT-hook,IDT-hook,sysenter-hook
一.SSDT-hook
(一)一般思路:
1.先来了解一下,什么是SSDT
SSDT既System Service Dispath Table。在了解他之前,我们先了解一下NT的基本组建。在 Windows NT 下,NT 的 executive(NTOSKRNL.EXE 的一部分)提供了核心系统服务。各种 Win32、OS/2 和 POSIX 的 APIs 都是以 DLL 的形式提供的。这些dll中的 APIs 转过来调用了 NT executive 提供的服务。尽管调用了相同的系统服务,但由于子系统不同,API 函数的函数名也不同。例如,要用Win32 API 打开一个文件,应用程序会调用 CreateFile(),而要用 POSIX API,则应用程序调用 open() 函数。这两种应用程序最终都会调用 NT executive 中的 NtCreateFile() 系统服务。
系统组建.jpg
用户模式(User mode)的所有调用,如Kernel32,User32.dll, Advapi32.dll等提供的API,最终都封装在Ntdll.dll中,然后通过Int 2E或SYSENTER进入到内核模式,通过服务ID,在System Service Dispatcher Table中分派系统函数,举个具体的例子,再如下图
从上可知,SSDT就是一个表,这个表中有内核调用的函数地址。从上图可见,当用户层调用FindNextFile函数时,最终会调用内核层的NtQueryDirectoryFile函数,而这个函数的地址就在SSDT表中,如果我们事先把这个地址改成我们特定函数的地址,那么,哈哈。。。。。。。下来详细了解一下,SSDT的结构,如下图:
KeServiceDescriptorTable:是由内核(Ntoskrnl.exe)导出的一个表,这个表是访问SSDT的关键,具体结构是 还有一种这样的表,叫KeServiceDescriptorTableShadow,它主要包含GDI服务,也就是我们常用的和窗口,桌面有关的,具体存在于Win32k.sys。在如图:
服务分发.jpg
右侧的服务分发就通过KeServiceDescriptorTableShadow。
那么下来该咋办呢?下来就是去改变SSDT所指向的函数,使之指向我们自己的函数。
2.Hook前的准备-改变SSDT内存的保护
系统对SSDT都是只读的,不能写。如果试图去写,等你的就是蓝脸。一般可以修改内存属性的方法有:通过cr0寄存器及Memory Descriptor List(MDL)。
(1)改变CR0寄存器的第1位
Windows对内存的分配,是采用的分页管理。其中有个CR0寄存器,如下图:
CR0.jpg
其中第1位叫做保护属性位,控制着页的读或写属性。如果为1,则可以读/写/执行;如果为0,则只可以读/执行。SSDT,IDT的页属性在默认下都是只读,可执行的,但不能写。所以现在要把这一位设置成1。
(2)通过Memory Descriptor List(MDL)
也就是把原来SSDT的区域映射到我们自己的MDL区域中,并把这个区域设置成可写。MDL的结构: 首先需要知道KeServiceDscriptorTable的基址和入口数,这样就可以用MmCreateMdl创建一个有起始地址和大小的内存区域。然后把这个MDL结构的flag改成
MDL_MAPPED_TO_SYSTEM_VA ,那么这个区域就可以写了。最后把这个内存区域调用MmMapLockedPages锁定在内存中。大体框架如下:
//先声明一个System Service Descriptor Table,我们知道SSDT及SSPT都从这个表中指向 现在遇到的第一个问题解决了,但接着面临另外一个问题,如何获得SSDT中函数的地址呢?
3.四个有用的宏 4.小试牛刀:利用SSDT Hook隐藏进程
我们所熟知的任务管理器,能察看系统中的所有进程及其他很多信息,这是由于调用了一个叫ZwQuerySystemInformation的内核函数,具体结构是:
NTSTATUS NewZwQuerySystemInformation(
IN ULONG SystemInformationClass, //如果这值是5,则代表系统中所有进程信息
IN PVOID SystemInformation, //这就是最终列举出的信息,和上面的值有关
IN ULONG SystemInformationLength, //后两个不重要
OUT PULONG ReturnLength)
如果用我们自己函数,这个函数可以把我们关心的进程过滤掉,再把它与原函数调换,则可达到隐藏的目的,大体思路如下:
(1) 突破SSDT的内存保护,如上所用的MDL方法
(2) 实现自己的NewZwQuerySystemInformation函数,过滤掉以某些字符开头的进程
(3) 用上面介绍的宏来交换ZwQuerySystemInformation与我们自己的New*函数
(4) 卸载New*函数,完成
具体实例:来自Rootkit.com,我做了注释,代码也很精小。