Inline hook ObReferenceObjectByHandle,附加问题笔记做记录

 

// 禁止记事本结束 inline hook成功,原来的抄袭代码似乎有问题,不知道是不是环境问题:xp sp3+VM+双核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                

}

}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到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;

}

代码

//禁止记事本结束 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;

图一:

Inline hook ObReferenceObjectByHandle,附加问题笔记做记录_第1张图片

以下为别处原来代码的

代码
[转]详谈内核三步走Inline Hook实现
(一)Inline hook原理
Inline hook通俗的说就是对函数执行流程进行修改,达到控制函数过滤操作的目的。理论上我们可以在函数任何地方把原来指令替换成我们的跳转指令,也确实有些人在inline
的时候做的很深,来躲避inline 的检测,前提是必须对函数的流程和指令非常熟悉,且这种深层次的inlline 不具有通用性,稳定性也是问题。本文讨论的是具有通用性的两类inline的实现。
Inline hook原理:解析函数开头的几条指令,把他们Copy到数组保存起来,然后用一个调用我们的函数的几条指令来替换,如果要执行原函数,则在我们函数处理完毕,再执行我们保存起来的开头几条指令,然后调回我们取指令之后的地址执行。
整个Inline hook的过程就大体这样,中间牵扯到对函数的检查,地址的获取就直接调用函数即可。
本文所要讨论的两类Inline hook都是基于上面原理。 

说明三点:
1 、堆栈平衡是重中之重,参数压栈也需要格外注意
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;

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;
  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
     �
  }
 �
}

_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               �
     �
  }
 �
}

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 ((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
     �
  }
}

驱动加载后,结束记事本程序如下:

    (图 一)

详细分析:
1 、ObReferenceObjectByHandle分析
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                无效句柄         

调用NtTerminateProcess需要调用ObReferenceObjectByHandle,因此我们通过对函数返回值进程修改来达到保护进程。但是NtCreateProcess(最终调用的PspCreateProcess)同样调用这个函数,如果不加区分的话,创建进程同样被禁止了,那么如何区分到底是谁在调用呢。参考WRK,我发现可以通过第二个参数DesiredAccess来判别,创建进程和结束进程第二个参数明显不同,PROCESS_CREATE_PROCESS和PROCESS_TERMINATE,问题就解决了。
PspCreateProcess位于 WRK
- v1. 2 \ base \ntos\ps\create.c
调用ObReferenceObjectByHandle代码:
Status 
=  ObReferenceObjectByHandle (ParentProcess,
                                            PROCESS_CREATE_PROCESS,
                                            PsProcessType,
                                            PreviousMode,
                                            
& Parent,
                                            NULL);
NtTerminateProcess位于 WRK
- v1. 2 \ base \ntos\ps\psdelete.c
调用ObReferenceObjectByHandle代码:
st 
=  ObReferenceObjectByHandle (ProcessHandle,
                                    PROCESS_TERMINATE,
                                    PsProcessType,
                                    KeGetPreviousModeByThread(
& Self -> Tcb),
                                    
& Process,
                                    NULL);
DesiredAccess参数说明:
#define  PROCESS_TERMINATE         (0x0001)  //  winnt
#define  PROCESS_CREATE_THREAD     (0x0002)  //  winnt
#define  PROCESS_SET_SESSIONID     (0x0004)  //  winnt
#define  PROCESS_VM_OPERATION      (0x0008)  //  winnt
#define  PROCESS_VM_READ           (0x0010)  //  winnt
#define  PROCESS_VM_WRITE          (0x0020)  //  winnt
//  begin_ntddk begin_wdm begin_ntifs
#define  PROCESS_DUP_HANDLE        (0x0040)  //  winnt
//  end_ntddk end_wdm end_ntifs
#define  PROCESS_CREATE_PROCESS    (0x0080)  //  winnt
#define  PROCESS_SET_QUOTA         (0x0100)  //  winnt
#define  PROCESS_SET_INFORMATION   (0x0200)  //  winnt
#define  PROCESS_QUERY_INFORMATION (0x0400)  //  winnt
#define  PROCESS_SET_PORT          (0x0800)
#define  PROCESS_SUSPEND_RESUME    (0x0800)  //  winnt

2 、函数调用说明
C语言中我们调用一个函数就直接写函数名就可以,但是实际是进行了下面的操作:
把函数参数压入堆栈,压入函数返回地址,调用函数,为新函数开辟堆栈空间申请局部变量,
恢复堆栈保持堆栈平衡
(_stdcall调用方式)汇编代码就是:
Push 参数4
Push 参数3
Push 参数2
Push 参数1
Call  函数  ;call指令同时完成2个操作,一是把返回地址压入堆栈,二跳转到调用函数入口地址

Push  ebp
Mov ebp,esp
Sub  esp, XX  ;开辟栈帧空间
……
Add  esp ,XX
Pop ebp
Retn          ;恢复堆栈平衡
堆栈详细情况:
ESP
局部变量

EBP
返回地址
参数1
参数2
参数3
参数4
堆栈是由高地址到低地址。
参数就通过EBP来去,四字节对齐的

参数4
---------------------- EBP + 0x14
参数3
---------------------- EBP + 0x10
参数2
---------------------- EBP + 0xc
参数1
---------------------  EBP + 0x8
局部变量则通过Ebp
- XX来获取

因此inline的时候要时刻考虑堆栈平衡,破坏了堆栈平衡就会导致函数崩溃。
我通常inline hook的思路就是三步走:
HOOK函数
----- DetourMy处理函数 ---------- UnHook函数
处理函数中对返回结果或者中间数据进行修改处理,然后调用原始函数。由于在我们处理的时候原始函数已经被hook了,所以我自己构造了一个原始函数,但是由于参数在我们hook前已经压人堆栈了,所以这里我们不用重新开辟栈帧,因此声名函数类型为_declspec (naked)
。有人就会问那么你调用处理函数的时候,参数不是重复压栈了,这里请注意,我们是通过JMP方式跳转到我们处理函数入口地址的,而不是Call的形式,所以并没有执行上面所说的函数调用过程,参数仍然是原始函数的。也就是说在真个inline hook过程中我们不能破坏原始栈帧的EBP。

关于函数调用很栈帧的相关联系可能比较难理解,我也在尽肯能的用通俗的话来解释清楚,有什么不理解的地方或者个人见解欢迎大家跟我交流。

2 、inline hook KiInsertQueueApc对抗插APC杀进程
KiInsertQueueAPc为内核未导出函数,我下面提供的代码可以作为未导出函数inline的通用模板来使用,大家根据自己需要进行修改,基于inline ObReferenceObjectByHandle已经把原理分析了,这部分我就不详加分析,仍然采用的但不走,Hook函数
--- DetourMy函数 --- UnHook函数
直接看核心代码:
// ===================inline hook KiInsertQueueApc====================
// KiInsertQueueApc为内核未导出函数,可以从导出函数KeInsertQueueApc定位
// 修改KiInsertQueueApc开头5字节
// 处理函数思路:apc-->kthread---apc_state--eprocess--进程名字
// HookKiInsertQueueApc---DetourMyKiInsertQueueApc---UnHookKiInsertQueueApc
ULONG CR0VALUE;
ULONG g_KiInsertQueueApc;
          �
BYTE JmpAddress[
5 ] = { 0xE9 , 0 , 0 , 0 , 0 };        // 跳转到HOOK函数的地址
BYTE  OriginalBytes[ 5 ] = { 0 };              // 保存原始函数前五个字

VOID FASTCALL DetourMyKiInsertQueueApc(IN PKAPC Apc,IN KPRIORITY Increment);

VOID WPOFF()
{
  _asm
   �
  {
   �
    push eax
     �
      mov eax, cr0�
     �
      mov CR0VALUE, eax�
     �
      and eax, 0fffeffffh �
     �
      mov cr0, eax
     �
      pop eax
      cli
     �
  };
 �
}

VOID WPON()
{
    __asm
   �
  {      �
    sti
    push eax
     �
      mov eax, CR0VALUE�
     �
      mov cr0, eax
     �
      pop eax
     �
  };
}
// 1、获取KiInsertQueueApc地址
ULONG GetFunctionAddr( IN PCWSTR FunctionName)      // PCWSTR常量指针,指向16位UNICODE
{
  UNICODE_STRING UniCodeFunctionName;
  RtlInitUnicodeString( 
& UniCodeFunctionName, FunctionName );
  
return  (ULONG)MmGetSystemRoutineAddress(  & UniCodeFunctionName );  �
}

ULONG GetKiInsertQueueApcAddr()
{
  ULONG sp_code1
= 0x28 ,sp_code2 = 0xe8 ,sp_code3 = 0xd88a ;   // 特征码,sp_code3 windbg显示错误,应该为d88a
  ULONG address = 0 ;
  PUCHAR addr;
  PUCHAR p;
  addr
= (PUCHAR)GetFunctionAddr(L " KeInsertQueueApc " );
  
for (p = addr;p < p + PAGE_SIZE;p ++ )
  {
    
if (( * (p - 1 ) == sp_code1) && ( * p == sp_code2) && ( * (PUSHORT)(p + 5 ) == sp_code3))
    {
      address
=* (PULONG)(p + 1 ) + (ULONG)(p + 5 );
      
break ;
    }
  }
  KdPrint((
" [KeInsertQueueApc] addr %x\n " ,(ULONG)addr));
    KdPrint((
" [KiInsertQueueApc] address %x\n " ,address));
    
return  address;
}

VOID HookKiInsertQueueApc()
{  �
  KIRQL Irql;
  g_KiInsertQueueApc
= GetKiInsertQueueApcAddr();
  KdPrint((
" [KiInsertQueueApc] KiInsertQueueApc %x\n " ,g_KiInsertQueueApc));
    
//  保存原函数的前字节内容
    RtlCopyMemory (OriginalBytes, (BYTE * )g_KiInsertQueueApc,  5 );
  
// 新函数对原函数的偏移地址
     * ( (ULONG * )(JmpAddress  +   1 ) )  =  (ULONG)DetourMyKiInsertQueueApc  -  (ULONG)g_KiInsertQueueApc  -   5 ;
    
//  禁止系统写保护,提升IRQL到DPC
    WPOFF();
    Irql 
=  KeRaiseIrqlToDpcLevel();
    
// inline hook函数
  RtlCopyMemory ( (BYTE * )g_KiInsertQueueApc, JmpAddress,  5  );
    
//  恢复写保护,降低IRQL
    KeLowerIrql(Irql);
    WPON(); �
}
// 原函数
_declspec (naked) VOID FASTCALL OriginalKiInsertQueueApc(IN PKAPC Apc,IN KPRIORITY Increment)
{
  _asm
  {
    
// 前五个字节
    mov edi,edi
      push ebp
      mov ebp,esp
     �
      mov eax,g_KiInsertQueueApc
      add eax,
5
      jmp eax
  }
}
// 处理函数
// apc--kthread--apc_state--eprocess
VOID FASTCALL DetourMyKiInsertQueueApc(IN PKAPC Apc,IN KPRIORITY Increment)
{
  ULONG thread;
  ULONG process;
  
if (MmIsAddressValid((PULONG)((ULONG)Apc + 0x008 )))     // 地址验证 KAPC结构+008--->kthread
    thread =* ((PULONG)((ULONG)Apc + 0x008 ));
  
else
    
return  ;
  
if (MmIsAddressValid((PULONG)((ULONG)thread + 0x044 )))  // kthread+30-->KAPC_STATE+10-->eprocess
    process =* ((PULONG)((ULONG)thread + 0x044 ));
  
else
    
return  ;
    
if (MmIsAddressValid((PULONG)((ULONG)process + 0x174 )))   // eprocess+174---->进程名字
  {
    
if ((_stricmp(( char   * )((ULONG)process + 0x174 ), " notepad.exe " ) == 0 ) && (Increment == 2 ))
    {
      
return  ;

    }
    
else
      OriginalKiInsertQueueApc(Apc,Increment);

  }
  
else
    
return ;
}

// 卸载函数
VOID UnHookKiInsertQueueApc()
{
  KIRQL Irql;
    WPOFF();
    Irql 
=  KeRaiseIrqlToDpcLevel();
    
// inline hook函数
    RtlCopyMemory ( (BYTE * )g_KiInsertQueueApc, OriginalBytes,  5 );
    
//  恢复写保护,降低IRQL
    KeLowerIrql(Irql);
    WPON(); �
}
 考虑到大家水平不一,对一些问题我详细如下:
1 、特征码的寻找
利用windbg的kernel debug来查找:
uf  KeInsertQueueApc
nt
! KeInsertQueueApc + 0x3b :
804e6d0a 8b450c          mov     eax,dword ptr [ebp
+ 0Ch]
804e6d0d 8b5514          mov     edx,dword ptr [ebp
+ 14h]
804e6d10 
894724           mov     dword ptr [edi + 24h],eax
804e6d13 8b4510          mov     eax,dword ptr [ebp
+ 10h]
804e6d16 8bcf            mov     ecx,edi
804e6d18 
894728           mov     dword ptr [edi + 28h],eax
804e6d1b e8523fffff        call    nt
! KiInsertQueueApc (804dac72)
804e6d20 8ad8 (错误)  mov     bl,al
特征码就是sp_code1
= 0x28  sp_code2 = 0xe8  sp_code3 = 0xd88a (windbg显示有误,应该是d88a

这种方法就是通过已导出函数定位未导出函数通常使用的方法,具有通用性。详细见代码。

2 、取EPRocess的过程
Apc
----- kthread ----- apc_state—eprocess
dt  _KAPC             偏移0x008指向KTHREAD
dt  _KTHREAD         偏移0x034指向KAPC_STATE
dt  _KAPC_STATE      偏移0x10指向EPROCESS
dt  _EPROCESS         偏移0x174指向进程名

(三)总结
 很多人觉得inline hook比较难,处理起来很麻烦。但是我相信看完我这篇文章,你一定不会这么认为了,inline hook其实只要细心,注意细节跟别的hook没什么两样。本人采用的三步走inline hook做到了把inline简单化,同时有保证了堆栈的平衡。
 由于代码采用的硬编码,编译环境是sp3
+ VMware,请根据自己操作系统自行修改。欢迎读者跟我交流。

=======================  

 

 

你可能感兴趣的:(reference)