虚函数Hook
在编译后,虚函数表就是一个固定的表了,它位于PE的.rdata段。
对虚函数表进行Hook:查找原函数位置、修改页面属性、写入Detour函数的地址。
步骤:
1)确定虚函数表中的函数位置及函数原型:在C++类中如果有虚函数成员,那么第一个元素就是虚函数表,也就是地址表,重载就是用子类的方法去覆盖父类的方法,也就是地址替换(实际上Hook时也是这么做的)。
2)根据目标函数的原型定义类,每个类都有一个与TargetFun相同的虚函数成员。
3)修改虚函数表,实现Hook。
.h文件: 关联第1步
#include <iostream>
using namespace std;
class base
{
public:
virtual int AddNew(int a, int b);
virtual void g(){cout << "base::g" << endl;};
virtual void h(){cout << "base::h" << endl;};
void novirtual(){cout << "base::not virtual" << endl;};
};
class DetourClass
{
public:
virtual int DetourFun(int a, int b);
};
class TrampolineClass
{
public:
virtual int TrampolineFun(int a, int b){return 0;};
};
.cpp文件:关联第2步
#include "stdafx.h"
#include <windows.h>
#include "VirtualHook.h"
#include <iostream>
using namespace std;
int DetourClass::DetourFun(int a, int b)
{
printf("Hook good\n");
return a + b;
}
int base::AddNew(int a, int b)
{
return a - b;
}
.main函数:Hook的实现
#include "stdafx.h"
#include <windows.h>
#include "VirtualHook.h"
#include <iostream>
using namespace std;
DWORD GetClassVirtualFnAddress(DWORD * BaseAddr, int count)
{
DWORD dwOLD;
MEMORY_BASIC_INFORMATION mbi;
ULONG uAddr = 0;
DWORD dwReturn;
VirtualQuery(BaseAddr, &mbi, sizeof(mbi));
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &dwOLD);
dwReturn = BaseAddr[count];
//恢复内存页属性
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, dwOLD, 0);
return dwReturn;
}
//Hook实现
void HookClassMemberByAnotherClassMember()
{
base b;
base *pbase = &b;
TrampolineClass CTr;
TrampolineClass* pCTr = &CTr;
DetourClass DeC;
DetourClass* pDeC = &DeC;
DWORD dwOLD;
MEMORY_BASIC_INFORMATION mbi;
ULONG uAddr = 0;
DWORD *vfTableToHook = (DWORD *) *(DWORD*) pbase;
DWORD *vfTableTrampoline = (DWORD*) * (DWORD*) pCTr;
DWORD *vfDetourClass = (DWORD*) * (DWORD*) pDeC;
VirtualQuery(vfTableTrampoline, &mbi, sizeof(mbi));
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &dwOLD);
//在C++类中如果有虚函数成员,那么第一个元素就是虚函数表,也就是地址表,重载就是用子类的方法去覆盖父类的方法,也就是地址替换(实际上Hook时也是这么做的)。
//修改Trampoline的虚函数表,原函数位于第几个这里就是第几个,位置必须一样
vfTableTrampoline[0] = (DWORD)GetClassVirtualFnAddress(vfTableToHook, 0);
//恢复内存页属性
VirtualProtect(mbi.AllocationBase, mbi.RegionSize, dwOLD, 0);
VirtualQuery(vfTableToHook, &mbi, sizeof(mbi));
VirtualProtect(mbi.AllocationBase, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &dwOLD);
vfTableToHook[0] = (DWORD)GetClassVirtualFnAddress(vfDetourClass, 0);
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, dwOLD, 0);
int result = pbase->AddNew(1, 2); //会调用DetourFun函数
printf("result = %d \nafter call member fun.\n", result);
}
int _tmain(int argc, _TCHAR* argv[])
{
HookClassMemberByAnotherClassMember();
system("pause");
return 0;
}