zoukankan      html  css  js  c++  java
  • SSDTHook实例--编写稳定的Hook过滤函数

    解说怎样写Hook过滤函数,比方NewZwOpenProcess。打开进程。

    非常多游戏保护都会对这个函数进行Hook

    因为我们没有游戏保护的代码,无法得知游戏公司是怎样编写这个过滤函数。

    我看到非常多奇形怪状的Hook过滤函数的写法。看得蛋痛无比。

    比方:

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

    http://bbs.pediy.com/showthread.php?

    t=126077

    第一个bug

    调用这个函数

    status = PsLookupProcessByProcessId(

              ClientId->UniqueProcess,

              &process

               );

    这个函数我们要注意的地方。没有看到清除调用引数ObDereferenceObject(process);

    第二个bug

    參数 PCLIENT_ID ClientId

    假设这个參数是NULL。那么 ClientId-->UniqueProcess

    就会产生蓝屏即直接对一个NULL变量进行读取。

    http://bbs.pediy.com/showthread.php?

    t=105418

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

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

    :假设你用naked(裸函数)这样的形式,就不应该有參数,假设你用__stdcall这样的调用规则。就应该要有參数。

    我认为这个还算是标准(至少可读性非常高):

    http://bbs.pediy.com/showthread.php?

    t=176477

    要注重细节,这就是代码稳定性的编写过程。

    随便找一个之前的hook过滤函数的代码。对比WRK,对使用的各种參数进行效验,写一个稳定的过滤函数

    稳定的SSDT Hook代码演示样例

    SSDT.h 头文件的编码:

    #ifndef _SSDT_H_
    #define _SSDT_H_
    
    #include <ntifs.h>
    
    //内核导出的SSDT表的结构
    typedef struct _SERVICE_DESCRIPTOR_TABLE {
    	/*
    	* Table containing cServices elements of pointers to service handler
    	* functions, indexed by service ID.
    	*/
    	PULONG   ServiceTable;
    	/*
    	* Table that counts how many times each service is used. This table
    	* is only updated in checked builds.
    	*/
    	PULONG  CounterTable;
    	/*
    	* Number of services contained in this table.
    	*/
    	ULONG   TableSize;
    	/*
    	* Table containing the number of bytes of parameters the handler
    	* function takes.
    	*/
    	PUCHAR  ArgumentTable;
    } SERVICE_DESCRIPTOR_TABLE, *PSERVICE_DESCRIPTOR_TABLE;
    
    //全局变量
    PMDL  pmdl_system_call;
    PVOID *pdword_mapped_table;
    
    
    //******************** SSDT Hook宏(固定的) ****************************************************************
    //获取Hook函数的Index
    #define SYSCALL_INDEX(_Function) *(PULONG)((PUCHAR)_Function+1)
    //实现SSDT表的Hook
    #define HOOK_SYSCALL(_Function, _Hook, _Orig )  
    	_Orig = (PVOID) InterlockedExchange( (PLONG) &pdword_mapped_table[SYSCALL_INDEX(_Function)], (LONG) _Hook)
    //恢复SSDT表的Hook
    #define UNHOOK_SYSCALL(_Function, _Hook, _Orig )  
    	InterlockedExchange( (PLONG) &pdword_mapped_table[SYSCALL_INDEX(_Function)], (LONG) _Hook)
    
    //声明SSDT的导出 
    extern PSERVICE_DESCRIPTOR_TABLE KeServiceDescriptorTable;
    
    #endif

    SSDTHook.h 头文件的编码:

    #ifndef _SSDT_HOOK_H_
    #define _SSDT_HOOK_H_
    
    #include <ntifs.h>
    #include "SSDT.h"  
    
    //声明没有文档化的函数PsGetProcessImageFileName
    UCHAR* PsGetProcessImageFileName(
    		__in PEPROCESS Process
    		); 
    //*************************************************************************************************************************
    
    //定义原函数的指针类型
    typedef NTSTATUS (__stdcall *REALZWOPENPROCESS)(OUT PHANDLE ProcessHandle,
    	IN ACCESS_MASK DesiredAccess,
    	IN POBJECT_ATTRIBUTES ObjectAttributes,
    	IN PCLIENT_ID ClientId
    	);
    //定义该函数指针
    REALZWOPENPROCESS RealZwOpenProcess;
    
    //保存要Hook函数的真实地址
    ULONG_PTR ul_ZwOpenProcess;
    //保存要Hook函数的名称
    UNICODE_STRING unicode_string;
    
    
    //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);
    
    #endif


    SSDT.c 文件的编写

    #include "SSDT.h"
    
    //******************************************************************************************
    //採用比較安全的方法改动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)
    {
    	//构建内存描写叙述符MDL
    	pmdl_system_call = MmCreateMdl(NULL, KeServiceDescriptorTable->ServiceTable, KeServiceDescriptorTable->TableSize*sizeof(ULONG_PTR));
    	if(!pmdl_system_call)
    	{
    		return STATUS_UNSUCCESSFUL;
    	}
    	
    	//依据MDL申请分配内存
    	MmBuildMdlForNonPagedPool(pmdl_system_call);
    
    	//设置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)
    	{
    		//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 文件的编写

    #include "SSDTHook.h"
    
    
    //深度的字符串效验
    BOOLEAN ValidateUnicodeString(PUNICODE_STRING usStr)
    {
    	ULONG i;
    
    	__try
    	{
    		//推断字符串的内存是否可訪问
    		if (!MmIsAddressValid(usStr))
    		{
    			return FALSE;
    		}
    
    		//推断是否为NULL
    		if (usStr->Buffer == NULL || usStr->Length == 0)
    		{
    			return FALSE;
    		}
    
    		//每个字节都要检查
    		for (i = 0; i < usStr->Length; i++)
    		{
    			if (!MmIsAddressValid((PUCHAR)usStr->Buffer + i))
    			{
    				return FALSE;
    			}
    		}
    
    	}
    	__except(EXCEPTION_EXECUTE_HANDLER)
    	{
    		//触发异常  
            return FALSE;  
    	}
    
    	return TRUE;
    }
    
    //自己定义的ZwOpenProcess函数(NewZwOpenProcess)
    NTSTATUS __stdcall NewZwOpenProcess(OUT PHANDLE ProcessHandle,
    	IN ACCESS_MASK DesiredAccess,
    	IN POBJECT_ATTRIBUTES ObjectAttributes,
    	IN PCLIENT_ID ClientId)
    {
    	NTSTATUS status;
    	ULONG PID;
    	HANDLE handle_process_handle;
    	PEPROCESS eprocess_process_object;
    	KPROCESSOR_MODE PreMode;
    
    	//获取当前的系统模式MODE
    	PreMode = ExGetPreviousMode();
    
    	//*******************************假设非内核模式,就要開始检查IN的这些參数都否可读****************************
    
    	//每Hook一个函数之前,你都要先对比WRK:
    	if(PreMode != KernelMode)
    	{
    		__try
    		{
    			//这里用ProbeForRead来对參数ClientId进行測试,然后加try捕获
    			//检查用户模式地址的可读性必须在ring0调用
    			ProbeForRead(ClientId, sizeof(CLIENT_ID), sizeof(ULONG));
    		}
    		__except(EXCEPTION_EXECUTE_HANDLER)
    		{
    			//返回异常代码
    			return GetExceptionCode();
    		}
    	}
    
    	//运行到这里说明改參数是能够訪问,那我们还要效验ClientId是否为NULL
    	if(ClientId != NULL && MmIsAddressValid(ClientId))
    	{
    		//更安全的訪问
    		PID = (ULONG)ClientId->UniqueProcess;
    
    		DbgPrint("OpenProcess %d by %s[0x%08X]
    ", PID, PsGetProcessImageFileName(PsGetCurrentProcess()), PsGetCurrentProcess());
    	}
    
    	/*
    	typedef struct _OBJECT_ATTRIBUTES {
    	ULONG           Length;
    	HANDLE          RootDirectory;
    	PUNICODE_STRING ObjectName; //Buffer
    	ULONG           Attributes;
    	PVOID           SecurityDescriptor;
    	PVOID           SecurityQualityOfService;
    	}  OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;
    	*/
    	if (ObjectAttributes != NULL && MmIsAddressValid(ObjectAttributes))
    	{
    		//这个成员是一个指针 ObjectName。效验指针是否为空,接着能否够訪问。这是第二层的效验
    		if (ObjectAttributes->ObjectName != NULL && MmIsAddressValid(ObjectAttributes->ObjectName))
    		{
    			//深度校验检查
    			if (ObjectAttributes->ObjectName->Buffer != NULL && ValidateUnicodeString(ObjectAttributes->ObjectName->Buffer))
    			{
    				//如今才干够安全不蓝屏的訪问这个Buffer
    				DbgPrint("OpenObjectName %ws
    ", ObjectAttributes->ObjectName->Buffer);
    			}
    		}
    	}
    
    
    	//假设我们要取ProcessHandle怎么办?
    	status = RealZwOpenProcess(ProcessHandle, 
    		DesiredAccess, 
    		ObjectAttributes, 
    		ClientId
    		);
    	if (NT_SUCCESS(status))
    	{
    		//为什么我们这里不用效验ProcessHandle?
    		//由于函数调用成功了
    		handle_process_handle = *ProcessHandle;
    
    		//然后我们还能够通过handle,得到eprocess。即 handle->eprocess。另一堆的转换,比方eprocess->handle, handle->fileobject
    		status = ObReferenceObjectByHandle(handle_process_handle, 
    			GENERIC_READ,
    			*PsProcessType, 
    			KernelMode, 
    			(PVOID*)&eprocess_process_object, 
    			0); 
    		if(NT_SUCCESS(status))
    		{
    			DbgPrint("@@ OpenProcess %s by %s
    ", PsGetProcessImageFileName(eprocess_process_object), PsGetProcessImageFileName(PsGetCurrentProcess()));
    
    			//这里非常重要,消除引用计数
    			ObDereferenceObject(eprocess_process_object);
    		}
    
    		//仅仅要RealZwOpenProcess调用成功,不管怎样一定要返回成功
    		status = STATUS_SUCCESS;
    	}
    
    	return status;
    }
    
    //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    
    //驱动卸载的例程函数
    VOID DriverUnload(IN PDRIVER_OBJECT DriverObject)
    {
    	//卸载Hook
    	if (ul_ZwOpenProcess)
    	{
    		//移除SSDT表的Hook
    		if (MdlRemoveSSDTHook((ULONG_PTR)ul_ZwOpenProcess, NewZwOpenProcess, &RealZwOpenProcess) == STATUS_SUCCESS)
    		{
    			DbgPrint("ZwOpenProcess Remove hook success
    ");
    		}
    	}
    
    	DbgPrint("DriverUnload
    ");
    }
    
    
    //驱动入口例程函数
    NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,PUNICODE_STRING RegistryPath)
    {
    	//设置驱动的卸载例程函数
    	DriverObject->DriverUnload = DriverUnload;
    
    
    	RtlInitUnicodeString(&unicode_string, L"ZwOpenProcess");
    
    	//获取要Hook函数的真实函数地址
    	ul_ZwOpenProcess = (ULONG_PTR)MmGetSystemRoutineAddress(&unicode_string);
    	if (ul_ZwOpenProcess)
    	{
    		if (MdlSSDTHook((ULONG_PTR)ul_ZwOpenProcess, NewZwOpenProcess, &RealZwOpenProcess) == STATUS_SUCCESS)
    		{
    			DbgPrint("ZwZwOpenProcess hook 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文件的编写:

    # $Id$
    TARGETNAME=SSDTHook
    TARGETPATH=obj
    TARGETTYPE=DRIVER
    
    
    # Create browse info
    #BROWSER_INFO=1
    #BROWSERFILE=<some path>
    
    # Additional defines for the C/C++ preprocessor
    C_DEFINES=$(C_DEFINES)
    
    SOURCES=SSDTHook.c
    	SSDT.c
    
    



    參考资料:

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




  • 相关阅读:
    2020蓝桥杯模拟赛(一)
    自己整理的瀑布流+滚动加载图片的例子
    .NET如何发送格式化的文本内容
    Bootstrap学习笔记(3)--表格表单图片
    BootStap学习笔记(2)
    BootStap学习笔记(1)
    Oracle性能优化
    Maven+spring+springMVC+mybatis+Junit+Log4j配置个人总结
    C#指针和寻址运算
    Linq to XML
  • 原文地址:https://www.cnblogs.com/lcchuguo/p/5095018.html
Copyright © 2011-2022 走看看