解说怎样写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

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

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

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

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

t=176477">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]\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=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讲课资料的改动和整理。

SSDTHook实例--编写稳定的Hook过滤函数的更多相关文章

  1. 4.5 HOOK分发函数

    4.5 HOOK分发函数 本节开始深入的探讨键盘的过滤与反过滤.有趣的是,无论是过滤还是反过 滤,其原理都是进行过滤.取胜的关键在于:谁将第一个得到信息. 黑客可能会通过修改一个已经存在的驱动对象(比 ...

  2. [内核编程] 4.5 HOOK分发函数

    4.5 HOOK分发函数 本节开始深入的探讨键盘的过滤与反过滤.有趣的是,无论是过滤还是反过 滤,其原理都是进行过滤.取胜的关键在于:谁将第一个得到信息. 黑客可能会通过修改一个已经存在的驱动对象(比 ...

  3. 为代码编写稳定的单元测试 [Go]

    为代码编写稳定的单元测试 本文档配套代码仓库地址: https://github.com/liweiforeveryoung/curd_demo 配合 git checkout 出指定 commit ...

  4. 如何编写高质量的 JS 函数(3) --函数式编程[理论篇]

    本文首发于 vivo互联网技术 微信公众号 链接:https://mp.weixin.qq.com/s/EWSqZuujHIRyx8Eb2SSidQ作者:杨昆 [编写高质量函数系列]中, <如何 ...

  5. PHP反序列化中过滤函数使用不当导致的对象注入

    1.漏洞产生的原因 ####  正常的反序列化语句是这样的 $a='a:2:{s:8:"username";s:7:"dimpl3s";s:8:"pa ...

  6. JS练习实例--编写经典小游戏俄罗斯方块

    最近在学习JavaScript,想编一些实例练练手,之前编了个贪吃蛇,但是实现时没有注意使用面向对象的思想,实现起来也比较简单所以就不总结了,今天就总结下俄罗斯方块小游戏的思路和实现吧(需要下载代码也 ...

  7. Filter 数组过滤函数精解示例

    '************************************************************************* '**模 块 名:Filter 数组过滤函数精解示 ...

  8. php 安全过滤函数代码

    php 安全过滤函数代码,防止用户恶意输入内容. //安全过滤输入[jb] function check_str($string, $isurl = false) { $string = preg_r ...

  9. php数据过滤函数与方法示例【转载】

    1.php提交数据过滤的基本原则 1)提交变量进数据库时,我们必须使用addslashes()进行过滤,像我们的注入问题,一个addslashes()也就搞定了.其实在涉及到变量取值时,intval( ...

随机推荐

  1. Java学习笔记九(泛型)

    1.介绍 所谓的泛型就是将类型作为一种參数来传递.有了泛型后类型不再是一成不变的.能够通过泛型參数来指定. 能够提供程序开发的灵活性. 2.泛型类或接口的使用 泛型类声明时.与普通类没有太大的差别,仅 ...

  2. MSSQL - 因为数据库正在使用,所以无法获得对数据库的独占访问权。

    关于“因为数据库正在使用,所以无法获得对数据库的独占访问权”的最终解决方案   今天在使用SQL Server2005对某个数据库进行还原操作的时候,出现了如上问题,“因为数据库正在使用,所以无法获得 ...

  3. linux脚本:shell, 判断输入参数的个数(命令行)

    if [ $# != 3 ] ; thenecho "USAGE: $0 from to"echo " e.g.: $0 ~/oucaijun/from ~/oucaij ...

  4. Java面试题精选(二)线程编程、数据库理论和Jdbc部分

    —— 线程编程.数据库理论和Jdbc部分内容 ——     数据库的开发应用想必是我们日常所碰到最多的知识点了,大致可分为:oracle.MySQL.SQL Server.Hadoop. NoSQL. ...

  5. try catch finally的执行顺序到底是怎样的?

    首先执行try,如果有异常执行catch,无论如何都会执行finally 一个函数中肯定会执行finally中的部分. 关于一个函数的执行过程是,当有return以后,函数就会把这个数据存储在某个位置 ...

  6. Android 界面滑动实现---Scroller类 从源码和开发文档中学习(让你的布局动起来)

    在android学习中,动作交互是软件中重要的一部分,其中的Scroller就是提供了拖动效果的类,在网上,比如说一些Launcher实现滑屏都可以通过这个类去实现..   例子相关博文:Androi ...

  7. boa-0.94.13:Hello CGI

    CGI是什么 CGI全称是CommonGateway Interface,简称CGI,中文名叫做通用网关接口. CGI程序就是符合CGI接口规范的程序,相对于WebServer来说也叫外部程序. CG ...

  8. Delphi图像处理 -- 最大值

    阅读提示:     <Delphi图像处理>系列以效率为侧重点,一般代码为PASCAL,核心代码采用BASM.     <C++图像处理>系列以代码清晰,可读性为主,全部使用C ...

  9. BAPI总的数据库提交和回滚

    BAPI事物中的数据提交和回滚必须通过调用SAP标准业务对象BAPI SERVICE(对象类型SAP0001)的BAPI方法bapiservic.transactioncommit和bapiservi ...

  10. Network Panel说明

    Chrome Developer Tools:Network Panel说明   官方资料:Chrome Developer Tools: Network Panel 一.chrome Develop ...