C++:虚函数表Hook

发布于:2024-05-12 ⋅ 阅读:(176) ⋅ 点赞:(0)

Hook

在计算机编程中,"Hook"(钩子)是一种技术,用于拦截并修改特定事件或函数的执行流程。它允许程序员在特定的代码点插入自定义的代码,以实现对程序行为的修改、监视或增强。

虚函数表Hook

虚函数表(Virtual Function Table,简称VTable)Hook是一种利用C++的动态多态性机制来修改类行为的技术。在C++中,虚函数表是用于实现动态多态性的重要机制,每个对象都有一个指向虚函数表的指针,该表中存储了虚函数的地址,通过修改这个虚函数表(即替换虚函数表中存储的函数指针),我们可以改变类的行为,使其调用不同的函数。

虚函数表Hook使用

由于在内存中虚函数表具有保护属性,是不可写的,所以在Windows环境中我们要进行虚函数表Hook需要通过调用Windows API中的一个函数VirtualProtect来进行,这个函数可以帮助我们修改内存页面的保护属性,让我们可以对其进行写入操作。函数原型:

BOOL VirtualProtect(
  LPVOID lpAddress,
  SIZE_T dwSize,
  DWORD  flNewProtect,
  PDWORD lpflOldProtect
);
  • lpAddress:指向要修改保护属性的内存地址的指针。

  • dwSize:要修改的内存区域的大小(以字节为单位)。

  • flNewProtect

    :新的保护属性,可以是下列常量之一:

    • PAGE_READONLY:页面为只读。

    • PAGE_READWRITE:页面为可读写。

    • PAGE_EXECUTE:页面为可执行。

    • PAGE_EXECUTE_READ:页面为可执行和可读。

    • PAGE_EXECUTE_READWRITE:页面为可执行和可读写。

  • lpflOldProtect:指向保存原始保护属性的变量的指针。如果不需要原始保护属性,则可以将其设置为 nullptr

接着我们来看一个简单的示例:首先,定义了一个父类 Animal 和一个派生类 Cat。在 Animal 类中,定义了一个虚函数 eat(),并在派生类 Cat 中进行了重写。然后,定义了一个自定义的函数 MyFunc(),用于替换虚函数的实现。

//父类
class Animal
{
public:
    Animal(){};
    
    ~Animal(){};
    
    //虚函数
    virtual void eat(){
        std::cout << "Animal Eat 函数" << std::endl;
    };
};
​
//派生类
class Cat : public Animal
{
public:
    Cat(){};
    
    ~Cat(){};
    
    void eat(){
        std::cout << "cat Eat 函数" << std::endl;
    };
}
​
//Hook函数用于替换虚函数
void MyFunc() {
    std::cout << "this is My Hook" << std::endl;
}

接着在main函数中进行虚函数表Hook操作:

int main() {
​
    Animal * catObj = new Cat;
​
    int  nVfptr = *(int*)catObj;    //虚函数表地址
    
    DWORD oldFunAddress = 0;  //定义变量接收保存原始保护属性的变量的指针
    BOOL bFlag = VirtualProtect((void*)nVfptr,0x1000, PAGE_EXECUTE_READWRITE, &oldFunAddress);
​
    if (bFlag) {
        *(int*)nVfptr = (int)MyFunc;
        catObj->run();
    }
    
    delete catObj;
    system("pause");
    return 0;
}
​

先创建了一个指向 Cat 对象的 Animal 类指针 catObj

int nVfptr = *(int*)catObj;获取指向虚函数表的指针 nVfptr的地址,

VirtualProtect((void*)nVfptr,0x1000, PAGE_EXECUTE_READWRITE, &oldFunAddress);使用 VirtualProtect 将其对应的内存页属性设置为可读写。

`(void*)nVfptr 是将整数 nVfptr 转换为指针类型的操作。在这里,nVfptr 存储着指向虚函数表的指针的地址,但它的类型是 int。为了能够将其传递给 VirtualProtect 函数,我们需要将其转换为指针类型。

if (bFlag) {
    *(int*)nVfptr = (int)MyFunc;
    catObj->eat();
}

如果成功内存页属性设置为可读写,则将虚函数表中第一个函数指针(对应于 eat() 函数)修改为 MyFunc() 函数的地址。最后,调用 catObj->eat(),实际上会执行 MyFunc() 函数,而不是原本的 Cat 类中的 eat() 函数,最后执行结果如下:

虚函数表Hook是一种强大而灵活的技术,可以用于实现各种高级功能,但需要谨慎使用,并且要对目标程序的内部结构有深入的了解。


网站公告

今日签到

点亮在社区的每一天
去签到