#include <ntddk.h> #define dprintf DbgPrint
#include
<windef.h>#include
<ntstatus.h> //字节型数据 unsigned charULONG CR0VALUE;
BYTE OriginalBytes[
5]={0}; //保存原始函数前五个字节BYTE JmpAddress[5]={0xE9,0,0,0,0}; //跳转到HOOK函数的地址
extern POBJECT_TYPE *PsProcessType;
NTKERNELAPI NTSTATUS ObReferenceObjectByHandle(
IN HANDLE Handle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_TYPE ObjectType OPTIONAL,
IN KPROCESSOR_MODE AccessMode,
OUT PVOID
*Object,OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL
);
//HOOK函数NTSTATUS DetourMyObReferenceObjectByHandle(
IN HANDLE Handle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_TYPE ObjectType OPTIONAL,
IN KPROCESSOR_MODE AccessMode,
OUT PVOID
*Object,OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL);
// //hook流程 HookObReferenceObjectByHandle---DetourMyObReferenceObjectByHandle---UnHookObReferenceObjectByHandlevoid HookObReferenceObjectByHandle()
{
//赋值前面定义的数组KIRQL Irql; // DbgPrint("[ObReferenceObjectByHandle] :%08x\n",ObReferenceObjectByHandle); //地址验证 //保存函数前五个字节内容
RtlCopyMemory(OriginalBytes,(BYTE *)ObReferenceObjectByHandle,5); //保存新函数五个字节之后偏移
*(ULONG *)(JmpAddress+1)=(ULONG)DetourMyObReferenceObjectByHandle-((ULONG)ObReferenceObjectByHandle+5); //开始inline hook
__asm{//去掉内存保护
cli
mov eax,cr0
and eax,not 10000h
mov cr0,eax
}
//提升IRQL中断级Irql=KeRaiseIrqlToDpcLevel(); //函数开头五个字节写JMP
RtlCopyMemory((BYTE *)ObReferenceObjectByHandle,JmpAddress,5); //恢复Irql
KeLowerIrql(Irql); //开启内存写保护
__asm{//恢复内存保护
mov eax,cr0
or eax,10000h
mov cr0,eax
sti
}
}
_declspec (naked) NTSTATUS OriginalObReferenceObjectByHandle(IN HANDLE Handle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_TYPE ObjectType OPTIONAL,
IN KPROCESSOR_MODE AccessMode,
OUT PVOID
*Object,OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL)
{
_asm
{
mov edi,edi
push ebp
mov ebp,esp
mov eax,ObReferenceObjectByHandle
add eax,
5jmp eax
}
}NTSTATUS status;
char* ProtectName = "notepad.exe";NTSTATUS DetourMyObReferenceObjectByHandle(
IN HANDLE Handle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_TYPE ObjectType OPTIONAL,
IN KPROCESSOR_MODE AccessMode,
OUT PVOID
*Object,OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL)
{
//调用原函数status=-1;
status
=OriginalObReferenceObjectByHandle(Handle,DesiredAccess,ObjectType,AccessMode,Object,HandleInformation); if((DesiredAccess==0x1)&&(ObjectType== *PsProcessType)){
if(( _stricmp((char *)((ULONG)(*Object)+0x174),ProtectName)==0))//&&(status=STATUS_SUCCESS))加入蓝屏见图一反汇编代码,原因不知,—。—,太菜,结束进程记事本,出现应用程序出错,进程没结束,但会耗死CPU。{
ObDereferenceObject(
*Object);//??? 去除也可以 不蓝屏或出错return STATUS_ACCESS_DENIED;
}
}
return status;}
void UnHookObReferenceObjectByHandle(){
//把五个字节再写回到原函数KIRQL Irql; //关闭写保护
__asm{//去掉内存保护
cli
mov eax,cr0
and eax,not 10000h
mov cr0,eax
}
//提升IRQL到DpcIrql=KeRaiseIrqlToDpcLevel();
RtlCopyMemory((BYTE
*)ObReferenceObjectByHandle,OriginalBytes,5);KeLowerIrql(Irql);
//开启写保护__asm{//恢复内存保护
mov eax,cr0
or eax,10000h
mov cr0,eax
sti
}
}
VOID OnUnload( IN PDRIVER_OBJECT DriverObject )
{
UnHookObReferenceObjectByHandle();
dprintf(
"[UnHookObReferenceObjectByHandle] Unloaded\n");}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObj,PUNICODE_STRING pRegistryString)
{
pDriverObj
->DriverUnload = OnUnload;HookObReferenceObjectByHandle();
return STATUS_SUCCESS;}
代码
//禁止记事本结束 inline hook成功,原来的抄袭代码似乎有问题,不知道是不是环境问题:xp sp3+VM+双核CPU //耗死CPU问题如何解决? //ObReferenceObjectByHandle是ntoskrnl.exe导出函数,采用HOOK前五个字节的方式 #include <ntddk.h> #define dprintf DbgPrint #include <windef.h> #include <ntstatus.h> //字节型数据 unsigned char ULONG CR0VALUE; BYTE OriginalBytes[5]={0}; //保存原始函数前五个字节 BYTE JmpAddress[5]={0xE9,0,0,0,0}; //跳转到HOOK函数的地址 extern POBJECT_TYPE *PsProcessType; NTKERNELAPI NTSTATUS ObReferenceObjectByHandle( IN HANDLE Handle, IN ACCESS_MASK DesiredAccess, IN POBJECT_TYPE ObjectType OPTIONAL, IN KPROCESSOR_MODE AccessMode, OUT PVOID *Object, OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL ); //HOOK函数 NTSTATUS DetourMyObReferenceObjectByHandle( IN HANDLE Handle, IN ACCESS_MASK DesiredAccess, IN POBJECT_TYPE ObjectType OPTIONAL, IN KPROCESSOR_MODE AccessMode, OUT PVOID *Object, OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL); // //hook流程 HookObReferenceObjectByHandle---DetourMyObReferenceObjectByHandle---UnHookObReferenceObjectByHandle void HookObReferenceObjectByHandle() { //赋值前面定义的数组 KIRQL Irql; // DbgPrint("[ObReferenceObjectByHandle] :%08x\n",ObReferenceObjectByHandle); //地址验证 //保存函数前五个字节内容 RtlCopyMemory(OriginalBytes,(BYTE *)ObReferenceObjectByHandle,5); //保存新函数五个字节之后偏移 *(ULONG *)(JmpAddress+1)=(ULONG)DetourMyObReferenceObjectByHandle-((ULONG)ObReferenceObjectByHandle+5); //开始inline hook __asm{//去掉内存保护 cli mov eax,cr0 and eax,not 10000h mov cr0,eax } //提升IRQL中断级 Irql=KeRaiseIrqlToDpcLevel(); //函数开头五个字节写JMP RtlCopyMemory((BYTE *)ObReferenceObjectByHandle,JmpAddress,5); //恢复Irql KeLowerIrql(Irql); //开启内存写保护 __asm{//恢复内存保护 mov eax,cr0 or eax,10000h mov cr0,eax sti } } _declspec (naked) NTSTATUS OriginalObReferenceObjectByHandle(IN HANDLE Handle, IN ACCESS_MASK DesiredAccess, IN POBJECT_TYPE ObjectType OPTIONAL, IN KPROCESSOR_MODE AccessMode, OUT PVOID *Object, OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL) { _asm { mov edi,edi push ebp mov ebp,esp mov eax,ObReferenceObjectByHandle add eax,5 jmp eax } } char* ProtectName = "notepad.exe"; NTSTATUS DetourMyObReferenceObjectByHandle( IN HANDLE Handle, IN ACCESS_MASK DesiredAccess, IN POBJECT_TYPE ObjectType OPTIONAL, IN KPROCESSOR_MODE AccessMode, OUT PVOID *Object, OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL) {
/调用原函数 NTSTATUS status; status=OriginalObReferenceObjectByHandle(Handle,DesiredAccess,ObjectType,AccessMode,Object,HandleInformation); if((NT_SUCCESS(status))&&(ObjectType== *PsProcessType))//NT_SUCCESS(status)一定跟在调用函数后面,不然也蓝。 //&&(status=STATUS_SUCCESS))使用蓝屏见图一反汇编代码,换成NT_SUCCESS(status)可以了,原因不知,—.—,太菜,结束进程记事本,出现应用程序出错,进程没结束,但会耗死CPU。 { if( _stricmp((char *)((ULONG)(*Object)+0x174),ProtectName)==0) {if(DesiredAccess==0x0002 )//0x2为创建,一开始使用#define PROCESS_CREATE_PROCESS (0x0080) // winnt怎么都没用,后来换成#define PROCESS_CREATE_THREAD (0x0002) // winnt就行 { ObDereferenceObject(*Object); return STATUS_INVALID_HANDLE; } } } return status; } void UnHookObReferenceObjectByHandle() { //把五个字节再写回到原函数 KIRQL Irql; //关闭写保护 __asm{//去掉内存保护 cli mov eax,cr0 and eax,not 10000h mov cr0,eax } //提升IRQL到Dpc Irql=KeRaiseIrqlToDpcLevel(); RtlCopyMemory((BYTE *)ObReferenceObjectByHandle,OriginalBytes,5); KeLowerIrql(Irql); //开启写保护 __asm{//恢复内存保护 mov eax,cr0 or eax,10000h mov cr0,eax sti } } VOID OnUnload( IN PDRIVER_OBJECT DriverObject ) { UnHookObReferenceObjectByHandle(); dprintf("[UnHookObReferenceObjectByHandle] Unloaded\n"); } NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObj,PUNICODE_STRING pRegistryString) { pDriverObj->DriverUnload = OnUnload; HookObReferenceObjectByHandle(); return STATUS_SUCCESS; } 图一: 以下为别处原来代码的 说明三点: NTKERNELAPI NTSTATUS ObReferenceObjectByHandle( { _declspec (naked) NTSTATUS OriginalObReferenceObjectByHandle(IN HANDLE Handle, NTSTATUS DetourMyObReferenceObjectByHandle( { 驱动加载后,结束记事本程序如下: 详细分析:
(一)Inline hook原理
Inline hook通俗的说就是对函数执行流程进行修改,达到控制函数过滤操作的目的。理论上我们可以在函数任何地方把原来指令替换成我们的跳转指令,也确实有些人在inline
的时候做的很深,来躲避inline 的检测,前提是必须对函数的流程和指令非常熟悉,且这种深层次的inlline 不具有通用性,稳定性也是问题。本文讨论的是具有通用性的两类inline的实现。
Inline hook原理:解析函数开头的几条指令,把他们Copy到数组保存起来,然后用一个调用我们的函数的几条指令来替换,如果要执行原函数,则在我们函数处理完毕,再执行我们保存起来的开头几条指令,然后调回我们取指令之后的地址执行。
整个Inline hook的过程就大体这样,中间牵扯到对函数的检查,地址的获取就直接调用函数即可。
本文所要讨论的两类Inline hook都是基于上面原理。
2、CR0寄存器中的WP位控制处理器是否允许往只读内存页写入,为0禁用保护机制。
3、提高中断级别到DISPATCH_LEVEL,禁止线程切换产生的中断
�
(二)inline hook应用
Inline hook可分为两类:
(1)inline 导出函数,选择ObReferenceObjectByHandle做例子。
(2)inline 未导出函数,选择KiInsertQueueApc做例子。
导出函数前几个字节可以利用windbg自己查看是什么内容,而未导出函数就需要自己解析指令确定需要hook几个字节,其间还有很多问题需要注意。当大家真正的弄懂了我这篇文章,回头再看inline hook就会觉得inline也不过如此。
下面通过2个例子来讲inline hook的使用(这部分知识网上也有很多,但都很零散不系统,本文部分思路及代码的确参考了网上资源,有抄袭之嫌,希望读者谅解。我一直强调“授人以鱼不如授人以渔”,代码并不重要,关键是思想。)
1、inline hook ObReferenceObjectByHandle保护进程
ObReferenceObjectByHandle属于ntoskrnl.exe导出函数,在内核中调用频繁。
NtCreateProcess创建进程需要调用ObReferenceObjectByHandle,NtTerminateProcess需要调用ObReferenceObjectByHandle,基于这我们就可以利用Hook来保护进程同时屏蔽进程的创建。
效果:已经运行的记事本任务管理器无法结束
流程:
HookObReferenceObjectByHandle------DetourMyObReferenceObjectByHa ndle----------UnHookObReferenceObjectByHandle
核心代码分析如下:
//=======================================inline HOOK ObReferenceObjectByHandle===========================
//ObReferenceObjectByHandle是ntoskrnl.exe导出函数,采用HOOK前五个字节的方式
//字节型数据 unsigned char
ULONG CR0VALUE;
BYTE OriginalBytes[5]={0}; //保存原始函数前五个字节 �
BYTE JmpAddress[5]={0xE9,0,0,0,0}; //跳转到HOOK函数的地址
extern POBJECT_TYPE *PsProcessType;
�
IN HANDLE Handle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_TYPE ObjectType OPTIONAL,
IN KPROCESSOR_MODE AccessMode,
OUT PVOID
OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL
�
);
//HOOK函数
NTSTATUS DetourMyObReferenceObjectByHandle(
�
IN HANDLE Handle, �
IN ACCESS_MASK DesiredAccess
IN POBJECT_TYPE ObjectType OPTIONAL,�
IN KPROCESSOR_MODE AccessMode,
OUT PVOID *Object,
OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL);
//
//hook流程 HookObReferenceObjectByHandle---DetourMyObReferenceObjectByHandle---UnHookObReferenceObjectByHandle
void HookObReferenceObjectByHandle()
�
KIRQL Irql;
KdPrint(("[ObReferenceObjectByHandle] :0x%x",ObReferenceObjectByHandle)); //地址验证
//保存函数前五个字节内容
RtlCopyMemory(OriginalBytes,(BYTE *)ObReferenceObjectByHandle,5);
//保存新函数五个字节之后偏移
*(ULONG *)(JmpAddress+1)=(ULONG)DetourMyObReferenceObjectByHandle-((ULONG)ObReferenceObjectByHandle+5);
//开始inline hook
//关闭内存写保护
_asm
�
{
push eax
�
mov eax, cr0�
mov CR0VALUE, eax�
and eax, 0fffeffffh �
mov cr0, eax
pop eax
}
�
//提升IRQL中断级
Irql=KeRaiseIrqlToDpcLevel();
//函数开头五个字节写JMP�
RtlCopyMemory((BYTE *)ObReferenceObjectByHandle,JmpAddress,5);
//恢复Irql
KeLowerIrql(Irql);
//开启内存写保护
�
__asm
�
{ �
�
push eax
�
mov eax, CR0VALUE�
�
mov cr0, eax
�
pop eax
�
}
�
}
�
IN ACCESS_MASK DesiredAccess,
�
IN POBJECT_TYPE ObjectType OPTIONAL,
�
IN KPROCESSOR_MODE AccessMode,
�
OUT PVOID
�
OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL)
�
{
�
_asm
�
{ �
�
mov edi,edi
push ebp
mov ebp,esp
mov eax,ObReferenceObjectByHandle
add eax,5
jmp eax �
�
}
�
}
�
IN HANDLE Handle,
�
IN ACCESS_MASK DesiredAccess,
�
IN POBJECT_TYPE ObjectType OPTIONAL,
�
IN KPROCESSOR_MODE AccessMode,
�
OUT PVOID
�
OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL)
�
{
�
NTSTATUS status;
�
//调用原函数
�
status=OriginalObReferenceObjectByHandle(Handle,DesiredAccess,ObjectType,AccessMode,Object,HandleInformation);
�
if((status==STATUS_SUCCESS)&&(DesiredAccess==1))//这样写在我机器出蓝屏,虚拟机也一样蓝,,看来要学的挺多的
�
{ �
�
if(ObjectType== *PsProcessType)
�
{
�
if( _stricmp((char *)((ULONG)(*Object)+0x174),"notepad.exe")==0)
�
{ �
�
ObDereferenceObject(*Object);
�
return STATUS_INVALID_HANDLE;
�
}
�
}
�
}
�
return status;
�
}
void UnHookObReferenceObjectByHandle()
�
�
KIRQL Irql;
�
//关闭写保护
�
_asm
�
{
�
push eax
�
mov eax, cr0�
�
mov CR0VALUE, eax�
�
and eax, 0fffeffffh �
�
mov cr0, eax
�
pop eax
�
}
�
//提升IRQL到Dpc
�
Irql=KeRaiseIrqlToDpcLevel();
�
RtlCopyMemory((BYTE *)ObReferenceObjectByHandle,OriginalBytes,5);
�
KeLowerIrql(Irql);
�
//开启写保护
�
__asm
�
{ �
�
push eax
mov eax, CR0VALUE�
mov cr0, eax
�
pop eax
�
}
}
�
(图 一)
NTSTATUS�
ObReferenceObjectByHandle(
IN HANDLE Handle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_TYPE ObjectType OPTIONAL,
IN KPROCESSOR_MODE AccessMode,
OUT PVOID *Object,
OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL
);
函数原型如上,由句柄获取对象指针,函数返回值:
STATUS_SUCCESS 调用成功
STATUS_OBJECT_TYPE_MISMATCH �
STATUS_ACCESS_DENIED 权限不够
STATUS_INVALID_HANDLE 无效句