博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
SSDTHook实例--编写稳定的Hook过滤函数
阅读量:7187 次
发布时间:2019-06-29

本文共 8798 字,大约阅读时间需要 29 分钟。

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

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

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

 

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

比方:

t=126077

 

第一个bug

调用这个函数

status = PsLookupProcessByProcessId(

          ClientId->UniqueProcess,

          &process

           );

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

 

第二个bug

參数 PCLIENT_ID ClientId

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

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

 

t=105418

 

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

 

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

t=176477

 

 

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

 

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

 

稳定的SSDT Hook代码演示样例

SSDT.h 头文件的编码:

#ifndef _SSDT_H_#define _SSDT_H_#include 
//内核导出的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 
#include "SSDT.h" //声明没有文档化的函数PsGetProcessImageFileNameUCHAR* 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表的HookNTSTATUS MdlSSDTHook(ULONG_PTR ul_real_function, ULONG_PTR hook_function_addr ,ULONG_PTR *ul_save_real_function_addr);//移除Mdl方式的SSDT表的HookNTSTATUS 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表的HookNTSTATUS 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表的HookNTSTATUS 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]\r\n", 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\r\n", 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\r\n", 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\r\n");		}	}	DbgPrint("DriverUnload\r\n");}//驱动入口例程函数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\r\n");		}	}	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=SSDTHookTARGETPATH=objTARGETTYPE=DRIVER# Create browse info#BROWSER_INFO=1#BROWSERFILE=
# Additional defines for the C/C++ preprocessorC_DEFINES=$(C_DEFINES)SOURCES=SSDTHook.c\ SSDT.c\

參考资料:

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

转载地址:http://mdjkm.baihongyu.com/

你可能感兴趣的文章
Effective C++学习笔记
查看>>
Spring Cloud Stream消费失败后的处理策略(三):使用DLQ队列(RabbitMQ)
查看>>
JavaScript自定义事件
查看>>
Golang 入门系列(九) 如何读取YAML,JSON,INI等配置文件
查看>>
MPAndroidChart 教程:设置颜色 Setting Colors
查看>>
一文教会你数据库性能调优(附某大型医院真实案例)
查看>>
为啥说5G是“全村人的希望” 2018年5G产业大盘点
查看>>
在 kubectl 中使用 Service Account Token
查看>>
松禾资本厉伟:创新的思考
查看>>
ajax跨域请求数据
查看>>
Spring Cloud Config 规范
查看>>
Linux 查找大文件
查看>>
Kubernetes ConfigMap热更新测试 – 探究ConfigMap的创建和更新流程
查看>>
.NET启动CMD执行(转载)
查看>>
Java在HBase数据库创建表
查看>>
Activity的启动模式
查看>>
x:Static&nbsp;,&nbsp;StaticResource&nbsp;和DynamicResource等XAML&nbsp;扩展用法
查看>>
「镁客早报」罗永浩回应锤子被酷派起诉事件,会妥善处理;NASA宣布对SpaceX和波音公司进行审查...
查看>>
spring之jdbc配置到固定路径
查看>>
Brackets安装emmit
查看>>