发一个支持任意地点hook的类(包含驱动hook和应用层hook)

这是以前练习写驱动类的一个产物,  有点早了,很简单.
写这个类也是方便自己绕过某些驱动的保护钩子.  当然这个也只支持x86, 没有做x64的拓展. 因为只是方便自己不需要每次都copy一大堆代码;
如果需要x64的拓展的,可以参考detours 或者是 EasyHook.   比较好的是EasyHook提供了驱动的hook;

貌似关于驱动写类论坛上比较少, 对于驱动,大家更倾向于直接用c写, 而不喜欢用c++写;  其实遇到大的工程,用c++有很多好处,而且微软也是有c++写驱动类的例子的.


废话不多说: 
关于驱动的,啰嗦一句, 不能定义全局的驱动对象, 具体为什么不能大家就自己baidu或者google;


直接上代码,代码里提供了例子;hook NtOpenProcess 和 NtReadVirtualMemory
驱动部分Hook.zip.


应用层的:
应用层hook.zip.

比较简单, 高手飘过. 这里只是给一个例子


代码:
//KernelDetours.h
#pragma once
#include 
#include "ldasm.h"
//保存5字节代码的结构
#pragma pack(1)
typedef struct _TOP5CODE
{
  UCHAR  instruction;  //指令
  ULONG  address;    //地址
}TOP5CODE,*PTOP5CODE;
#pragma pack()



#ifdef __cplusplus
extern "C" {
#endif
class CKernelDetours
{
public:
  CKernelDetours();
  ~CKernelDetours();

  BOOLEAN Hook(ULONG HookAddr,PULONG NakedFunc);
  void UnHook();
  void __stdcall CallJmpBack();

protected:
  VOID WPOFF();
  VOID WPON();

private:
  UCHAR m_Bak[5];
  ULONG m_hookAddr;
  ULONG m_HookCodeLen;//被hook指令的长度

  ULONG m_OldProtect;
  BOOLEAN  m_bHookSuccess;
  KIRQL  Irql;

  PVOID m_detoursFunc;//这个地方呢,是动态生成的. 他是保存hook前的指令, 可以执行到这里然后跳转到原函数下面继续执行

public:
  static void __cdecl operator delete(void* pointer) { ASSERT(NULL != pointer); if (NULL != pointer) ExFreePool(pointer); }
  static void * __cdecl operator new(size_t iSize,POOL_TYPE PoolType,unsigned int tag) {
    KdPrint(("global operator new --  Allocate size :%d \n",iSize));
    PVOID result; // [sp+0h] [bp-4h]@1
    result = ExAllocatePoolWithTag(PoolType, iSize, tag);
    if ( result )
      memset(result, 0, iSize);
    return result;
  }


};



#ifdef __cplusplus
}; // extern "C"
#endif


//KernelDetours.cpp
#include "KernelDetours.h"

#define  TAG 'liuq'


CKernelDetours::CKernelDetours(void)
{
  RtlZeroMemory(m_Bak,0,6);
  m_hookAddr =0;
  m_OldProtect = 0;
  m_bHookSuccess = FALSE;
  m_HookCodeLen = 0;
  m_detoursFunc = NULL;
  m_detoursFunc = ExAllocatePoolWithQuotaTag(NonPagedPool,0x50,TAG);
  memset(m_detoursFunc,0x90,0x50);
  KdPrint(("Enter CKernelDetours::CKernelDetours(void)   m_detoursFunc: %x",(ULONG)m_detoursFunc));
}

CKernelDetours::~CKernelDetours(void)
{
// if (m_bHookSuccess)
// {
//   UnHook();
// }
KdPrint(("Enter CKernelDetours::~CKernelDetours(void)"));
}

void CKernelDetours::WPOFF()
{    //清除页面保护
  __asm
  {
      cli
      mov eax,cr0
      and eax,not 10000h
      mov cr0,eax
  }
}

void CKernelDetours::WPON()
{  //恢复页面保护
  __asm
  {
      mov eax,cr0
      or eax,10000h
      mov cr0,eax
      sti
  }
}



void CKernelDetours::UnHook()
{
  if (m_bHookSuccess)
  {
    ULONG a = m_hookAddr;
    WPOFF();
    Irql = KeRaiseIrqlToDpcLevel();
    RtlCopyMemory((void*)a,m_Bak,5);
    KeLowerIrql(Irql);
    WPON();

    m_bHookSuccess =FALSE;
  }
  if (m_detoursFunc != NULL)
  {
    KdPrint(("ExFreePoolWithTag(m_detoursFunc,TAG);"));
    ExFreePoolWithTag(m_detoursFunc,TAG);
    m_detoursFunc  =  NULL;
  }
}

BOOLEAN CKernelDetours::Hook(ULONG HookAddr,PULONG NakedFunc)
{
  if(m_bHookSuccess || NakedFunc==NULL || HookAddr == NULL)
  {
    return FALSE;
  }
  m_hookAddr = HookAddr; //保存被hook的地址
  unsigned char jmp[6]  ={0xe9};
  jmp[5] = 0x90;


  PUCHAR pcode = NULL;
  ULONG codelen =0;
  ULONG uSumCodeLen = 0;
  BOOLEAN bFind = FALSE;
  for (int j =0; j<0x30; j+=codelen)
  {
    codelen =   SizeOfCode((void*)(HookAddr+j),&pcode);
    uSumCodeLen+=codelen;//计算总长度
    if (uSumCodeLen>=5)
    {
      bFind =TRUE; 
      break;
    }
  }

  if (!bFind)
  {
    KdPrint((" I'm sorry, Can Not Find Right Place to Hook\n"));
    return FALSE;
  }
  
  m_HookCodeLen = uSumCodeLen; //保存这个长度

  ULONG  JmpBack= HookAddr + uSumCodeLen;
  ULONG jmpDetoursAddr = ((ULONG)m_detoursFunc + uSumCodeLen);//在detours函数跳转到hook的地方
  ULONG b = JmpBack - jmpDetoursAddr  - 5;
  *(ULONG *)(jmp+1) = b;

  WPOFF();
  Irql = KeRaiseIrqlToDpcLevel();
  RtlCopyMemory((void*)m_detoursFunc,(void*)HookAddr,uSumCodeLen); //把这些数据保存到detours 函数里面,//然后在后面写上 jmp (HookAddr+uSumCodeLen)
  RtlCopyMemory((void*)jmpDetoursAddr,jmp,5);
  KeLowerIrql(Irql);
  WPON();

  ULONG a = HookAddr;  //hook的地址
   b = (ULONG)NakedFunc - a - 5;
  *(ULONG *)(jmp+1) = b;
  
  
  RtlCopyMemory((void*)m_Bak,(void*)a,5);//保存hook的地方

  WPOFF();
  Irql = KeRaiseIrqlToDpcLevel();
  RtlCopyMemory((void*)a,jmp,5);
  KeLowerIrql(Irql);
  WPON();
  m_bHookSuccess = TRUE;
  return TRUE;
}



void CKernelDetours::CallJmpBack()
{
/*
__asm
{
  push ebp
  mov ebp,esp
  sub,esp,8
}*/

ULONG ebp1 = (ULONG)m_detoursFunc;
__asm
{
  mov eax,ebp1
  mov esp,ebp
  pop ebp
  add esp,8  ;//这里会有一个变量的堆栈空间 + 一个this指针的参数
  jmp eax
}

/*
__asm 
{
  mov esp,ebp
  pop ebp
  retn 4
}*/

}

至于用法就比较简单了;

CKernelDetours *g_p= NULL; //hook NtOpenProcess 定义一个全局的指针,是为了方便还原, 不需要还原hook的可以直接在函数内部定义对象


代码:
//NtOpenProcess的中继函数
__declspec(naked) void MyOpenProcess()
{
  __asm
  {
      pushad
      pushfd
  }

  ANSI_STRING  p_str1;
  RtlInitAnsiString(&p_str1,(PSTR)((ULONG)IoGetCurrentProcess()+0x174));
  //将我们要比对的进程名放入str2

  KdPrint(("访问OPenProcess的进程名为 %s\n", p_str1.Buffer));

  __asm
  {
      popfd
      popad
  }

  // call detours
  g_p->CallJmpBack();

}
CKernelDetours* Hook_NtOpenProcess()
{
  CKernelDetours *p =  new(NonPagedPool, 'RysI') CKernelDetours;
  ULONG addr= GetSsdtFuncAddr(0x7A) ;
  KdPrint(("NtOpenProcess Addr 0x%x\n",addr));
  p->Hook(addr,(PULONG)MyOpenProcess);//这里只需要填入要hook的地址,和中继函数的地址即可.很简单; 在函数头,函数尾都行;有了这个功能,大家就可以专心写中继函数了.不必考虑其他的事情;
  return p;
}

void UnHookNtOpenProcess(CKernelDetours* p)
{
  CKernelDetours *pthis = p;
  if (pthis != NULL)
  {
    pthis->UnHook();
    delete pthis;
  }
}


这里就是演示其用法. 作用就是可以在任意函数内部进行Hook, 专心写中继函数,也不需要你人为的去调用被覆盖的代码. 你只需要在中继函数的最后面调用一下  g_p->CallJmpBack();就是实现跳转的功能,即可跳转会原函数执行; 这个功能对绕过某些驱动保护应该足够了.而且用法也很简单.

鱼台论坛http://bbs.370827.org/forum-75-1.html

你可能感兴趣的:(发一个支持任意地点hook的类(包含驱动hook和应用层hook))