zoukankan      html  css  js  c++  java
  • SSDT表函数Hook原理

    其实 SSDT Hook 的原理是很简单的,我们可以知道在 SSDT 这个数组中呢,保存了系统服务的地址,比如对于 Ring0 下的 NtQuerySystemInformation 这个系统服务的地址,就保存在 KeServiceDescriptorTable[105h] (计算公式 address = SSDT首地址+服务号*4

    lkd> u 84647e3e

    nt!NtQuerySystemInformation:

    84647e3e 8bff            mov     edi,edi

    84647e40 55              push    ebp

    84647e41 8bec          mov     ebp,esp

    84647e43 8b5508      mov     edx,dword ptr [ebp+8]

    既然是 Hook 的话,我们就可以将这个 KeServiceDescriptorTable[105h] 下保存的服务地址替换掉,将我们自己的 Hook 处理函数的地址来替换掉原来的地址

    Mov eax,84496170h

    Mov [eax],MyHookFunction

    这样当每次调用 KeServiceDescriptorTable[105h]时就会调用我们自己的这个 Hook 处理函数了,就执行了我们的代码,达到hook原理。

    我给大家画个图便以理解:


    所有我们只要修改 KeServiceDescriptorTable[105h] 即可达到目的。

    所以这就是我们在平时所讲的SSDT Hook原理。

    大家都知道在SSDT Hook都时候都看到如下代码:


    修改内存属性

    __asm

    {

    cli

    mov eax, cr0

    and eax, not 10000h

    mov cr0, eax

    }


    恢复内存属性

    __asm

    {

    mov eax, cr0

    or eax, 10000h

    mov cr0, eax

    sti

    }


    这是为什么呢?原因在于Windows系统对部分内存起用了写保护,IAT 内存属性,来防止内存页被修改,SSDT表的内存属性只是只读。如果你试图对一个只读属性的表进行写入操作,那么等待你的就是无情的蓝屏。因此,要修改ssdt表的话,首先要让表变为可写属性

    注:

    修改系统分页内存属性的方法也可以参考:

    http://blog.csdn.net/qq1084283172/article/details/40987347这里提供三种修改分页内存的方法。

    有两种方法可以绕过写保护,一种是修改控制寄存器的CR0中的写保护位(WP)来绕过,另一种是利用MDL(Memory Descriptor List)来绕过写保护.

     

    第一种方法比较简单,也就是CR0重的WP(写保护)位设置为0,就可以禁止内存保护了。

    比如大家看到看雪上的大部分代码,都是通过修改cr0wp来禁止内存保护:

    http://bbs.pediy.com/showthread.php?t=168061

    http://bbs.pediy.com/showthread.php?t=148831

     

    第二种是根据MDL。我们可以根据Mdl分配一段虚拟地址映射到SSDT所在的物理地址同时因为我们映射的MDL内存属性却是可写,所以就可以修改SSDT,这样就替代了cr0方式。

    MDL有很多标志:

    //MDL Flags  

    #define MDL_MAPPED_TO_SYSTEM_VA     0x0001  

    #define MDL_PAGES_LOCKED                0x0002  

    #define MDL_SOURCE_IS_NONPAGED_POOL 0x0004  

    #define MDL_ALLOCATED_FIXED_SIZE        0x0008  

    #define MDL_PARTIAL                     0x0010  

    #define MDL_PARTIAL_HAS_BEEN_MAPPED 0x0020  

    #define MDL_IO_PAGE_READ                0x0040  

    #define MDL_WRITE_OPERATION             0x0080  

    10 #define MDL_PARENT_MAPPED_SYSTEM_VA 0x0100  

    11 #define MDL_LOCK_HELD                   0x0200  

    12 #define MDL_PHYSICAL_VIEW               0x0400  

    13 #define MDL_IO_SPACE                        0x0800  

    14 #define MDL_NETWORK_HEADER          0x1000  

    15 #define MDL_MAPPING_CAN_FAIL            0x2000  

    16 #define MDL_ALLOCATED_MUST_SUCCEED  0x4000  

    我们在SSDTHook过程中,只关心MDL_MAPPED_TO_SYSTEM_VA这个标志位,因为他表示我们申请的MDL内存页是可读可写的


    下面是采用Cr0和Mdl的方式进行SSDT表的函数Hook的示例:

    SSDT.h头文件

    //SSDT.h
    #ifndef _SSDT_H_
    #define _SSDT_H_
    
    #include <ntifs.h>
    
    //SSDT表的结构体
    #pragma pack(1)  
    typedef struct _SERVICE_DESCRIPTOR_TABLE
    {
    	//System Service Dispatch Table的基地址  
    	PULONG   ServiceTable;
    
    	//SSDT中每个服务被调用次数的计数器。
    	PULONG  CounterTable;
    
    	//由 ServiceTableBase 描述的服务数目。
    	ULONG   TableSize;
    	
    	//每个系统服务参数字节数表的基地址-系统服务参数表SSPT
    	PUCHAR  ArgumentTable;
    } SERVICE_DESCRIPTOR_TABLE, *PSERVICE_DESCRIPTOR_TABLE;
    #pragma pack()  
    
    //进行ssdt表KeServiceDescriptorTable的导出声明
    extern PSERVICE_DESCRIPTOR_TABLE KeServiceDescriptorTable;
    
    
    /*
    _Hook为自定义用于Hook SSDT中函数的函数地址
    _Function为SSDT表中被Hook函数原始地址
    pdword_mapped_table申请的非分页内存的地址
    */
    
    //内存描述符列表
    PMDL  pmdl_system_call;
    PVOID *pdword_mapped_table;
    
    //获取SSDT表中函数的调用序号Index
    #define SYSCALL_INDEX(_Function) *(PULONG)((PUCHAR)_Function+1)
    
    //Mdl方式的SSDT表函数Hook
    #define HOOK_SYSCALL(_Function, _Hook, _Orig )  
    	_Orig = (PVOID) InterlockedExchange( (PLONG) &pdword_mapped_table[SYSCALL_INDEX(_Function)], (LONG) _Hook)
    
    //移除Mdl方式的SSDT表函数Hook
    #define UNHOOK_SYSCALL(_Function, _Hook, _Orig )  
    	InterlockedExchange( (PLONG) &pdword_mapped_table[SYSCALL_INDEX(_Function)], (LONG) _Hook)
    
    
    //开启内存写保护
    VOID WPON();
    
    //关闭内存写保护
    VOID WPOFF();
    
    
    //采用Cr0方式SSDT表函数Hook
    VOID Cr0SSDTHook(int *Index, ULONG_PTR *ul_save_real_addrsss, ULONG_PTR ul_hook_address);
    
    //移除采用Cr0方式SSDT表函数Hook
    VOID Cr0RemoveSSDTHook(int *Index, ULONG_PTR ul_save_real_addrsss);
    
    
    //采用Mdl方式SSDT表函数Hook
    NTSTATUS MdlSSDTHook(ULONG_PTR ul_real_function, ULONG_PTR hook_function_addr, ULONG_PTR *ul_save_real_function_addr);
    
    //移除采用Mdl方式SSDT表函数Hook
    NTSTATUS MdlRemoveSSDTHook(ULONG_PTR ul_real_function, ULONG_PTR hook_function_addr, ULONG_PTR *ul_save_real_function_addr);
    
    
    /*
    驱动中输出提示信息推荐使用 KdPrint函数 :
    驱动编程学习中,往往需要通过DbgPrint或者KdPrint来输出调试信息,
    对于Check版本,KdPrint只是DbgPrint的一个宏定义,而对于Free版本,KdPrint将被优化掉。
    */
    
    /*
    LONG InterlockedExchange(  
         IN OUT PLONG  Target,  
         IN LONG  Value);  
    
    InterlockedExchange(a,b)能以原子操作的方式交换俩个参数a, b,并返回a以前的值;
    因为InterlockedExchange 是原子函数,不会要求中止中断,所以交换指针的方式是安全的。
    */
    
    /*
    #define SYSTEMSERVICE(_Function) 
         KeServiceDescriptorTable.ServiceTableBase[ *(PULONG)((PUCHAR)_Function+1)]
    
    #define SYSCALL_INDEX(_Function) *(PULONG)((PUCHAR)_Function+1)
    
    #define HOOK_SYSCALL(_Function, _Hook, _Orig ) 
              _Orig = (PVOID) InterlockedExchange( (PLONG) &MappedSystemCallTable
              [SYSCALL_INDEX(_Function)], (LONG) _Hook)
    
    #define UNHOOK_SYSCALL(_Function, _Hook, _Orig)  InterlockedExchange((PLONG)
             &MappedSystemCallTable[SYSCALL_INDEX(_Function)], (LONG) _Hook)
    
    而SYSTEMSERVICE宏是采用由ntoskrnl.exe导出的Zw*函数地址,并返回对应的Nt*函数在SSDT中的地址,SYSCALL_INDEX采用Zw*函数地址并返回它在SSDT中相应的索引号。
    由于ZwCreateThread没有被ntoskrnl.exe导出,所以这时我们就无法直接使用上述的宏.
    */
    
    /*
    MDL的全称是Memory Descriptor List,即内存描述符表。我们可以通过MDL描述一块内存区域,
    在MDL中包含了该内存区域的起始地址、拥有者进程、字节数量、标记等信息,如下所示:
    typedef struct _MDL 
    {
        struct _MDL  *Next;
        CSHORT       Size;
        CSHORT       MdlFlags;
        struct _EPROCESS *Process;
        PVOID          MappedSystemVa;
        PVOID          StartVa;
        ULONG         ByteCount;
        ULONG   ByteOffset;
    } MDL, *PMDL;
    */
    
    /*
    #define SYSTEMSERVICE(_function)  KeServiceDescriptorTable.ServiceTableBase[ *(PULONG)((PUCHAR)_function+1)] 
    */
    
    #endif

    SSDTHook.h头文件

    //SSDTHook.h
    #ifndef _SSDT_HOOK_H_
    #define _SSDT_HOOK_H_
    
    #include "SSDT.h"
    
    //自定义的ZwOpenProcess函数
    NTSTATUS NewZwOpenProcess(OUT PHANDLE ProcessHandle,
    	IN ACCESS_MASK DesiredAccess,
    	IN POBJECT_ATTRIBUTES ObjectAttributes,
    	IN PCLIENT_ID ClientId
    	);
    
    //声明ZwOpenProcess函数指针类型
    typedef NTSTATUS (__stdcall *ZWOPENPROCESS)(OUT PHANDLE ProcessHandle,
    	IN ACCESS_MASK DesiredAccess,
    	IN POBJECT_ATTRIBUTES ObjectAttributes,
    	IN PCLIENT_ID ClientId
    	);
    ZWOPENPROCESS RealZwOpenProcess;
    
    //决定我们使用哪种hook
    BOOLEAN bool_hook_type = TRUE;
    
    //设置SSDT表Hook函数的Index
    int int_hook_index = 190;
    
    //保存ZwOpenProcess函数的真实值
    ULONG_PTR ul_ZwOpenProcess;
    UNICODE_STRING unicode_string;
    
    #endif


    SSDT.c文件

    //SSDT.c
    #include "SSDT.h"
    
    //关闭内存写保护
    VOID WPOFF()
    {
    	__asm
    	{
    		cli                  //屏蔽中断
    		mov eax, cr0
    		and eax, not 10000h
    		mov cr0, eax
    	}
    }
    
    //开启内存写保护
    VOID WPON()
    {
    	__asm
    	{
    		mov eax, cr0
    		or eax, 10000h
    		mov cr0, eax
    		sti                   //回复中断
    	}
    }
    
    /*
    第一个参数Index就是服务号,
    第二个参数ul_save_real_addrsss就是保存函数原始地址。因为我们要在过滤函数中调用原始地址
    第三个参数ul_hook_address就是我们的hook代码的函数
    */
    
    //Cr0方式SSDT表的函数Hook
    VOID Cr0SSDTHook(int *Index, ULONG_PTR *ul_save_real_addrsss, ULONG_PTR ul_hook_address)
    {
    	ULONG_PTR ul_real_service_address;
    	ULONG_PTR num = *Index;
    
    	//设置断点
    	//__asm int 3
    
    	//根据计算公式
    	//ul_real_service_address = 0x84495d5c + 105*4;
    	//ul_real_service_address = 0x84496170;
    
    	//获取被Hook函数的原始地址
    	ul_real_service_address = (ULONG)KeServiceDescriptorTable->ServiceTable + num*4;
    	if (ul_real_service_address)
    	{
    		//0x84647e3e
    		/*
    		nt!NtQuerySystemInformation:
    		84647e3e 8bff            mov     edi,edi
    		84647e40 55              push    ebp
    		84647e41 8bec            mov     ebp,esp
    		84647e43 8b5508          mov     edx,dword ptr [ebp+8]
    		84647e46 83fa53          cmp     edx,53h
    		*/
    
    		//保存SSDT表中原始函数地址
    		*ul_save_real_addrsss = *((ULONG*)ul_real_service_address);
    
    		//关闭wp写保护
    		WPOFF();
    
    		//修改SSDT表中的Hook函数的地址为自定函数的地址
    		*((ULONG*)ul_real_service_address) = ul_hook_address;
    
    		//开启wp写保护
    		WPON();
    	}
    }
    
    //移除Cr0方式SSDT表的函数Hook即恢复SSDT中被Hook的函数
    VOID Cr0RemoveSSDTHook(int *Index, ULONG_PTR ul_save_real_addrsss)
    {
    	ULONG_PTR ul_real_service_address;
    	ULONG_PTR num = *Index;
    
    	//关闭wp写保护
    	WPOFF();
    
    	//根据计算公式
    	ul_real_service_address = (ULONG)KeServiceDescriptorTable->ServiceTable + num*4;
    	if (ul_real_service_address)
    	{
    		//恢复SSDT表中被Hook函数的地址
    		*((ULONG*)ul_real_service_address) = ul_save_real_addrsss;
    	}
    
    	//开启wp写保护
    	WPON();
    }
    
    
    //******************************************************************************************
    //采用比较安全的方法修改ssdt表
    //因为SSDT的虚拟地址分页属性是只读的,我们不能够直接修改它,否则会产生蓝屏
    //我们借助Mdl分配一段虚拟地址映射到SSDT所在的物理地址,
    //同时因为我们映射的MDL内存属性却可以是可写,所以就可以修改ssdt,这样就替代了cr0方式。
    //******************************************************************************************
    
    //采用Mdl方式的SSDT表函数Hook
    NTSTATUS MdlSSDTHook(ULONG_PTR ul_real_function, ULONG_PTR hook_function_addr, ULONG_PTR *ul_save_real_function_addr)
    {
    	//设置断点
    	//__asm int 3
    
    	//创建内存描述符列表Mdl,映射到SSDT表所在的内存物理地址
    	pmdl_system_call = MmCreateMdl(NULL, KeServiceDescriptorTable->ServiceTable, KeServiceDescriptorTable->TableSize*sizeof(ULONG_PTR));
    	//判断内存描述符列表Mdl 
    	if(!pmdl_system_call)
    	{
    		return STATUS_UNSUCCESSFUL;
    	}
    
    	//构建非分页内存,建立虚拟地址与物理地址的映射关系
    	MmBuildMdlForNonPagedPool(pmdl_system_call);
    
    	//改变MDL的标志,设置为MDL_MAPPED_TO_SYSTEM_VA标志,让这块内存变可写
    	pmdl_system_call->MdlFlags = pmdl_system_call->MdlFlags | MDL_MAPPED_TO_SYSTEM_VA;
    
    	//锁定非分页内存
    	pdword_mapped_table = MmMapLockedPages(pmdl_system_call, KernelMode);
    	if (pdword_mapped_table)
    	{
    		//开始Mdl方式的SSDT表函数Hook
    		HOOK_SYSCALL(ul_real_function, hook_function_addr, *ul_save_real_function_addr);
    	}
    
    	return STATUS_SUCCESS;
    }
    
    //移除采用Mdl方式的SSDT表函数Hook
    NTSTATUS MdlRemoveSSDTHook(ULONG_PTR ul_real_function, ULONG_PTR hook_function_addr, ULONG_PTR *ul_save_real_function_addr)
    {
    	//恢复SSDT表中被Hook的函数
    	UNHOOK_SYSCALL(ul_real_function, *ul_save_real_function_addr, hook_function_addr);
    
    	if(pmdl_system_call)
    	{
    		//解锁非分页内存
    		MmUnmapLockedPages(pdword_mapped_table, pmdl_system_call);
    
    		//释放申请的非分页内存
    		IoFreeMdl(pmdl_system_call);
    
    		return STATUS_SUCCESS;
    	}
    
    	return STATUS_UNSUCCESSFUL;
    }

    SSDTHook.c文件

    //SSDTHook.c
    #include "SSDTHook.h"
    
    //卸载函数
    VOID DriverUnload(IN PDRIVER_OBJECT DriverObject)
    {
    	if (bool_hook_type)
    	{
    		//移除采用Cr0方式的SSDT表函数Hook
    		Cr0RemoveSSDTHook(&int_hook_index, RealZwOpenProcess);
    
    		DbgPrint("ZwOpenProcess Remove Cr0RemoveSSDTHook success
    ");
    	}
    	else
    	{
    		//移除采用Mdl方式的SSDT表函数Hookook
    		if (MdlRemoveSSDTHook((ULONG_PTR)ul_ZwOpenProcess, NewZwOpenProcess, &RealZwOpenProcess) == STATUS_SUCCESS)
    		{
    			DbgPrint("ZwOpenProcess Remove MdlRemoveSSDTHook success
    ");
    		}
    	}
    
    	DbgPrint("卸载完成!
    ");
    }
    
    //因为这节只是阐述SSDTHOOK原理,所以只是简单的弄一个过滤函数
    /*
    lkd> u 8a114070 l 30
    8a114070 8bff            mov     edi,edi
    8a114072 55              push    ebp
    8a114073 8bec            mov     ebp,esp
    8a114075 8b4514          mov     eax,dword ptr [ebp+14h]
    8a114078 50              push    eax
    8a114079 8b4d10          mov     ecx,dword ptr [ebp+10h]
    8a11407c 51              push    ecx
    8a11407d 8b550c          mov     edx,dword ptr [ebp+0Ch]
    8a114080 52              push    edx
    8a114081 8b4508          mov     eax,dword ptr [ebp+8]
    8a114084 50              push    eax
    8a114085 ff151c60118a    call    dword ptr ds:[8A11601Ch]
    8a11408b 5d              pop     ebp
    8a11408c c21000          ret     10h
    */
    /*
    HOOK,SSDT表的内容:
    lkd> dd 84495d5c+be*4
    84496054  8a114070
    
    我们unhook:
    lkd> dd 84495d5c+be*4
    84496054  84629ad4
    */
    
    //自定义的ZwOpenProcess函数NewZwOpenProcess
    NTSTATUS NewZwOpenProcess(OUT PHANDLE ProcessHandle,
    	IN ACCESS_MASK DesiredAccess,
    	IN POBJECT_ATTRIBUTES ObjectAttributes,
    	IN PCLIENT_ID ClientId
    	)
    {
    	//Your OEM code
    	DbgPrint("You Have Hooked Me
    ");
    
    	//在过滤函数当中,要调用,所以我们在hook的时候要保存好
    	return RealZwOpenProcess(ProcessHandle,
    		DesiredAccess,
    		ObjectAttributes,
    		ClientId
    		);
    }
    
    //****************************************************************************************************************
    
    //驱动入口函数DriverEntry
    NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
    {
    	//设置卸载例程函数
    	DriverObject->DriverUnload = DriverUnload;
    
    	//定义一个BOOLEAN
    	if (bool_hook_type)
    	{
    		//Cr0方式SSDT表函数Hook
    		Cr0SSDTHook(&int_hook_index, &RealZwOpenProcess, (ULONG_PTR)NewZwOpenProcess);
    
    		DbgPrint("ZwOpenProcess start Cr0RemoveSSDTHook success
    ");
    	}
    	else
    	{
    		DbgPrint("ZwOpenProcess start MdlRemoveSSDTHook success
    ");
    
    		RtlInitUnicodeString(&unicode_string, L"ZwOpenProcess");
    		//获取函数ZwOpenProcess的原始调用地址
    		ul_ZwOpenProcess = (ULONG_PTR)MmGetSystemRoutineAddress(&unicode_string);
    
    		if (ul_ZwOpenProcess)
    		{
    			//Mdl方式SSDT表函数Hook
    			if (MdlSSDTHook(ul_ZwOpenProcess, (ULONG_PTR)NewZwOpenProcess, &RealZwOpenProcess) == STATUS_SUCCESS)
    			{
    				DbgPrint("ZwOpenProcess MdlSSDTHook success
    ");
    			}
    		}
    	}
    
    	return STATUS_SUCCESS;
    }



    驱动编译需要的配置文件的编写

    makefile文件

    #
    # DO NOT EDIT THIS FILE!!!  Edit .sources. if you want to add a new source
    # file to this component.  This file merely indirects to the real make file
    # that is shared by all the driver components of the Windows NT DDK
    #
    
    !INCLUDE $(NTMAKEENV)makefile.def

    sources文件

    TARGETNAME=ssdtHook
    TARGETTYPE=DRIVER
    TARGETPATH=obj
    
    INCLUDE=.
    
    SOURCES = SSDTHook.c
    	  SSDT.c 


    文档和代码的下载地址:http://download.csdn.net/detail/qq1084283172/8838535


    参考资料:

    AGP讲课资料的整理和修改。



  • 相关阅读:
    tp 30秒超时
    zend studio git 提示错误
    php连接数据库
    php点击排序
    表情符号解析
    js Object.assign 方法
    Web界面简繁体转换
    H5骨架屏实战
    JavaScript heap out of memory问题
    Vue bus插件封装
  • 原文地址:https://www.cnblogs.com/csnd/p/11800722.html
Copyright © 2011-2022 走看看