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是一种强大而灵活的技术,可以用于实现各种高级功能,但需要谨慎使用,并且要对目标程序的内部结构有深入的了解。