标签:ssdt hook hook过滤函数 windows内核hook ssdthook示例 windows内核安全
讲解如何写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
#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; }
#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
# $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讲课资料的修改和整理。
版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:ssdt hook hook过滤函数 windows内核hook ssdthook示例 windows内核安全
原文地址:http://blog.csdn.net/qq1084283172/article/details/46754961