欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Hook技术:虚表hook及虚函数的调用过程详解(附源码)

程序员文章站 2022-07-13 16:11:17
...

偶然间看到一篇帖子讲述的是虚表Hook,之前没有接触过,兴趣多致,进行学习和实践,将个人感悟与代码分享出来。

什么是虚表hook?

首先我们应该要先了解什么是虚表
虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的。简称为V-Table。
在这个表中,主是要一个类的虚函数的地址表,这张表解决了继承、覆盖的问题,保证其容真实反应实际的函数。

Hook技术:虚表hook及虚函数的调用过程详解(附源码)
我们调用虚函数的时候,会通过虚表指针,找到虚表,而后找到虚函数地址。
如果是这样的话,hook虚表就应当和IAT Hook想类似了。

为了验证我们的想法,我们是看一下虚函数是如何被调用的。
在我之前的帖子中已经详细分析:https://blog.csdn.net/weixin_43742894/article/details/105999347

那么起始我们就可以将虚函数的地址改为我们的函数地址就可以进行hook了,这么一想还是很简单的。

实现原理

一句话,将虚表中的虚函数地址更改成我们的即可

实现步骤

1.获得虚表指针
2.修改虚表的内存保护属性为可读可写
3.修改虚表中的虚函数地址为我们的函数地址.
4.还原HOOK,也就是将原函数地址,写回到虚表中
5.恢复原保护属性

未修改前的虚表中的函数地址:
Hook技术:虚表hook及虚函数的调用过程详解(附源码)
1.获得虚表指针

int pVftableAddress = *(int *)&test;        //第一步,获取自己的虚表指针

2.修改虚表的内存保护属性为可读可写

DWORD OldProtect = 0;                       //第二步修改虚表指针的内存保护属性,下方更改虚表
	VirtualProtect((void *)pVftableAddress, 0x1000, PAGE_EXECUTE_READWRITE, &OldProtect);//修改内存保护属性,其地址是虚表指针地址

3.修改虚表中的虚函数地址为我们的函数地址.

(*(int *)pVftableAddress) = (int)MyVirtual;//第三步,HOOK,也就是将我们的函数地址,写入到虚表中.

修改后的虚表中的函数地址:
Hook技术:虚表hook及虚函数的调用过程详解(附源码)

第四步,还原HOOK,也就是将原函数地址,写回到虚表中

(*(int *)pVftableAddress) = (int)__pOldFunction;//第四步,还原HOOK,也就是将原函数地址,写回到虚表中.

第五步:恢复原保护属性

VirtualProtect((void *)pVftableAddress, 0x1000, OldProtect, &NewProtect);   // 第五步:恢复原保护属性

hook结果:
Hook技术:虚表hook及虚函数的调用过程详解(附源码)

优缺点

优点:相对其他hook来说比较简单
缺点:比较明显,只能用于虚函数,使用范围小。

代码示例

#include<iostream>
#include <windows.h>


ULONG_PTR __pOldFunction = NULL;
ULONG_PTR __pOldFunctionAddress = NULL;
class MyTest
{
public:
	MyTest()
	{
		printf("MyTest::MyTest()\r\n");
	}
	~MyTest()
	{
		printf("MyTest::~MyTest()\r\n");
	}

	void print();
	virtual void Vritual();
	int m_Number;
};



void MyTest::Vritual()
{
	printf("原虚函数Vritual,未被hook\r\n\r\n");
}

void MyVirtual()                                //我们将虚表中的函数地址换为我们的函数地址
{
	printf("已被hook,我们自己的函数\r\n\r\n");

}

void MyTest::print()
{
	printf("void MyTest::print()\r\n");
}


int main(int argc, char* argv[])
{
	MyTest test;
	MyTest &obj = test;    
	obj.Vritual();   //虚函数调用,测试作用

	int pVftableAddress = *(int *)&test;        //第一步,获取自己的虚表指针

	__pOldFunction = (*(int *)pVftableAddress);  //保存原函数地址.因为我这里比较简单只有一个虚函数,多个虚函数还需要考虑索引位置的问题
	

	DWORD OldProtect = 0;                       //第二步修改虚表指针的内存保护属性,下方更改虚表
	DWORD NewProtect = PAGE_EXECUTE_READWRITE;
	VirtualProtect((void *)pVftableAddress, 0x1000, NewProtect, &OldProtect);//修改内存保护属性,其地址是虚表指针地址

	(*(int *)pVftableAddress) = (int)MyVirtual;//第三步,HOOK,也就是将我们的函数地址,写入到虚表中.

	
	obj.Vritual();   //重新调用,看看是否被HOOK

	(*(int *)pVftableAddress) = (int)__pOldFunction;//第四步,还原HOOK,也就是将原函数地址,写回到虚表中.

	VirtualProtect((void *)pVftableAddress, 0x1000, OldProtect, &NewProtect);   // 第五步:恢复原保护属性

	obj.Vritual();   //恢复地址后,重新调用。
	return 0;
}

相关标签: HOOK