C++ 虚函数表 Hook

本文详细讲解了C++中如何通过修改虚函数表实现Hook,涉及x86和x64平台的调用规则差异,以及针对不同类型的函数(虚函数和普通函数)的Hook策略。重点介绍了模板函数VtlHook和VtlHookFun的使用方法。
摘要由CSDN通过智能技术生成

当C++类中函数虚函数时,为了实现多态,C++类最开始地方会包含一个指针,该指针指向虚函数表,当我们修改这虚函数表里地址就达到了实现虚函数表Hook的目的。

代码如下:

#include "VTblHook.h"
#include <iostream>
#include <Windows.h>

class MyBase
{
public:
   MyBase(int a,int b):m_ia(a),m_ib(b){ }

   virtual void __cdecl Test(int a){ }

   void Print()
   {
      std::cout << "a:" << m_ia<< std::endl;
   }

private:
   int m_ia;
   int m_ib;
};

class MyHook
{
public:
   virtual void __cdecl Hook(int a)
   {
   MyBase* pBase = (MyBase*)(this);
   pBase->Print();
   std::cout << "Hook:" << a << std::endl;
   }

   virtual void __cdecl Hook1(int b)
   {
      std::cout << "Hook1:" << b << std::endl;
   }
};

void __cdecl HookTest(void* p, int a)
{
std::cout << "HookTest sucess:"<< a << std::endl;
}

template<class T,class U>
void VtlHook(T* pT, int Tidx, U* pU, int Uidx)
{
   auto pTVtAddr = (size_t*)*(size_t*)pT;
   auto pUVtAddr = (size_t*)*(size_t*)pU;

   DWORD dwProct = 0;
   VirtualProtect(pTVtAddr, sizeof(size_t), PAGE_READWRITE, &dwProct);
   pTVtAddr[Tidx] = pUVtAddr[Uidx];
   VirtualProtect(pTVtAddr, sizeof(size_t), dwProct, nullptr);
};

template<class T>
void VtlHookFun(T* pT, int Tidx,size_t funcAdd)
{
   auto pTVtAddr = (size_t*)*(size_t*)pT;
   DWORD dwProct = 0;
   VirtualProtect(pTVtAddr, sizeof(size_t), PAGE_READWRITE, &dwProct);
   pTVtAddr[ Tidx ] = funcAdd;
   VirtualProtect(pTVtAddr, sizeof(size_t), dwProct, nullptr);
}

void VTblHook()
{
   MyBase* pb = new MyBase(1,2);
   MyHook* pHook = new MyHook();

   pb->Test(1);
   HookTest(NULL,1);

   //VtlHook(pb,0,pHook,0);
   VtlHookFun(pb,0, (size_t)HookTest);

   pb->Test(125);

   delete pb;
   delete pHook;

   return;
}

这里面需要注意的地方有两个:

对于x86,我们写入的函数地址的调用方式,参数类型一定要设置好。

如果我们填入的是一个虚函数的地址,就相对简单一点,只需要填入虚函数地址的函数声明与被替换函数的声明一致即可。因为同一个程序里,是采用同一种调用方式的。示例代码里可以把两个虚函数里的__cdecl去掉,也是可以正常执行的。

如果我们填入的是一个普通函数的地址,该普通函数比虚函数需要新增一个参数,就是第一个参数需要是一个指针来接收,同时该函数需要采用__cdecl调用方式,虚函数也要采用__cdecl进行调用。因为如果采用__stdcall,由于用于hook的函数多一个参数,此时普通函数被以虚函数的方式进行调用,平栈的时候会有问题。因此需要统一采用__cdecl进行调用,由主调函数进行平栈。

如果虚函数采用__stdcall进行调用,而我们也没有办法修改函数调用方式的话,只有参数为空的时候才能hook成功,虚函数参数为空的时候普通函数参数也需要为空(还是因为平栈的问题),但是这样子就无法获取到类的指针了,并且无法有参数的传递,局限较大,否则就无法进行hook。

对于X64,由于去除了x86那么多混乱的调用方式,而且也是由主调函数进行平栈,因此没有这个问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值