天天看点

硬件断点hook

见字如面,我是东北码农。

上文我们介绍了硬件断点,大家可以回顾一下。本文将介绍使用硬件断点+veh,实现硬件断点hook。

关注后,聊天框回复“硬件断点hook”,可以获取本文源码。

1、不修改原函数hook

硬件断点这个手法,我还是分析外挂时学到的。开始做反外挂时候,只会用xuetr、pchunter等工具查看游戏进程的inlinehook,分析修改了游戏的哪些代码,然后再防御。然后有一天,我发现了一个外挂不按套路出牌,没有修改任何代码实现了透视功能,我就很纳闷这个外挂是怎么实现的呢,不改代码我怎么防啊?

后来经过不断学习,才知道hook的方式有很多种,硬件断点hook就是其中一种比较隐蔽的hook方式,可以在不修改源函数的前提实现hook,这一点比inlinehook强。

2、VEH(Vectored Exception Handling)

2.1、VEH介绍

我们先来介绍一下VEH,先放一段微软官方描述。

参考资料:https://docs.microsoft.com/en-us/windows/win32/debug/vectored-exception-handling

Vectored exception handlers are an extension to structured exception handling. An application can register a function to watch or handle all exceptions for the application. Vectored handlers are not frame-based, therefore, you can add a handler that will be called regardless of where you are in a call frame. Vectored handlers are called in the order that they were added, after the debugger gets a first chance notification, but before the system begins unwinding the stack.

大概意思是,程序可以注册异常回调函数链表。当程序触发异常时,回调函数就会按照用户指定的顺序依次调用。

2.2、VEH 使用

向VEH链注册一个异常处理函数,可以使用windows提供的API,

PVOID AddVectoredContinueHandler(
  ULONG                       First,
  PVECTORED_EXCEPTION_HANDLER Handler
);
           
  • First:是否插入VEH链头部。
  • Handler:异常处理函数。

接下来看一下异常处理函数,PVECTORED_EXCEPTION_HANDLER 的定义:

LONG PvectoredExceptionHandler(
  [in] _EXCEPTION_POINTERS *ExceptionInfo
)
           

唯一的参数_EXCEPTION_POINTERS指向异常信息,定义如下:

定义如下:

typedef struct _EXCEPTION_POINTERS {
  PEXCEPTION_RECORD ExceptionRecord;// 异常信息记录
  PCONTEXT          ContextRecord;// 寄存器信息
} EXCEPTION_POINTERS, *PEXCEPTION_POINTERS;
           

异常处理函数的返回值很重要,控制着异常处理的后续行为:

To return control to the point at which the exception occurred, return EXCEPTION_CONTINUE_EXECUTION (0xffffffff). To continue the handler search, return EXCEPTION_CONTINUE_SEARCH (0x0).

  • 返回-1:异常已处理,继续执行;
  • 返回0:继续调用VEH链的其它处理函数。

3、硬件断点 hook 实战代码

介绍完硬件断点和veh后,我们就可以实现硬件断点hook了。我们先介绍一下思路:

  1. 设置VEH链异常处理函数。
  2. 设置硬件断点,监控原函数的执行事件。
  3. 执行原函数时,会触发异常调用我们的异常处理函数。
  4. 异常处理函数判断,如果是原函数地址,则修改rip寄存器,跳转到hook函数。

3.1、设置VEH链异常处理函数

异常处理函数,xx_hw_bp_veh的实现一会再介绍。

static bool xx_init_hwbp_hook() {
  return xx_add_veh(CALL_FIRST, xx_hw_bp_veh);
}
           

3.2、设置硬件断点

class xx_hw_bp
{
public:
  struct bp_info {
    void* src_ = nullptr;
    void* dst_ = nullptr;
  };
public:
  void hook(void* src,void* dst,HANDLE thread,int idx)
{
    //记录原函数与hook函数关系。
    // TODO idx check
    info_[idx].src_ = src;
    info_[idx].dst_ = dst;
    // 设置硬件断点
    xx_set_hw_bp(thread, idx, src, RW_EXE);
  }
    // 获取原函数与hook函数映射
  void* get_dst(void* src) {
    for (int i = 0; i < 4; ++i) {
      if (src == info_[i].src_)
      {
        return info_[i].dst_;
      }
    }
    return nullptr;
  }
  bp_info info_[4];
};

xx_hw_bp g_hwbp_;// 全局类,方便异常处理函数访问
           

先记录一下原函数和hook函数的映射关系,再使用上文硬件断点封装好的xx_set_hw_bp设置硬件断点。

3.3、异常处理函数实现

LONG NTAPI xx_hw_bp_veh(
  struct _EXCEPTION_POINTERS* ExceptionInfo)
{
  // 获取异常地址
  void* src = ExceptionInfo->ExceptionRecord->ExceptionAddress;
  // 查找对应关系
  void* dst = g_hwbp_.get_dst(src);
  if (nullptr != dst) {
    // 修改rip 跳转
    memcpy(&ExceptionInfo->ContextRecord->Rip, &dst, sizeof(void*));
    return EXCEPTION_CONTINUE_EXECUTION;
  }
  // 其它异常,继续veh链处理
  return EXCEPTION_CONTINUE_SEARCH;
}
           

先通过异常地址,找到对应的hook函数地址。若找到,则修改Rip(指令寄存器)实现跳转;找不到则不是我们关心的异常,交给VEH链继续处理吧。

4、demo验证

int WINAPI My_MessageBoxA(
  _In_opt_ HWND hWnd,
  _In_opt_ LPCSTR lpText,
  _In_opt_ LPCSTR lpCaption,
  _In_ UINT uType)
{
  printf("[call %s]!\n", __FUNCTION__);

  auto ori = xx_trampoline_to_func(&MessageBoxA, trampoline);
  return (*ori)(hWnd, lpText, lpCaption, uType);
}

void test_hook() {
  // 制作跳板,只需要复制1条指令,跳过硬件断点即可
  xx_mem_unprotect(trampoline, 1024);
  xx_make_trampoline(&MessageBoxA, trampoline, 4);

  xx_init_hwbp_hook();
  g_hwbp_.hook(&MessageBoxA, &My_MessageBoxA,GetCurrentThread(), 0);
  ::MessageBoxA(0, "aaa", "bbb", 0);
}
           

使用时先制作跳板,再hook。制作跳板复用inlinehook的代码。由于不修改代码,所以跳板值需要复制一条指令即可。复制的代码少,所以也基本不用考虑重定位问题。

欢迎大家点赞、转发、再看、留言交流~

继续阅读