为了探究虚表的今生前世,先来一段测试代码
虚函数类:
class CTest { public: int m_nData; virtual void PrintData() { printf("Data = 0x%x\n", m_nData); } }; class CBase1 { public: int m_nData; virtual void PrintData1() = 0; }; class CBase2 { public: int m_nData; virtual void PrintData2() = 0; }; class CBaseTest : public CBase1, public CBase2 { public: void PrintData1() { printf("Data = 0x%x\n", CBase1::m_nData); } void PrintData2() { printf("Data = 0x%x\n", CBase2::m_nData); } };测试代码:
void Test() { CTest oCTest; CTest* pCTest = new CTest(); pCTest->m_nData = 0x8888; pCTest->PrintData(); oCTest.m_nData = 888; oCTest.PrintData(); delete pCTest; } void BaseTest() { CBaseTest oCBaseTest; oCBaseTest.PrintData1(); }
1、虚表位于何处?
WinDbg显示虚表的地址的属性:Usage RegionUsageImage(代表此地址区域被映射到二进制文件的镜像),为只读属性。
2、同一个类对象的虚表位置相同吗?
同一个类对象的虚表位置相同。
加载模块的内存位置:0x00380000
虚表的VA = 0x003FDF2C - 0x00380000 = 0x0007DF2C3、虚表需要在加载后进行初始化吗?
否,虚表的位置在PE文件的 .rdata 节中,.rdata 是存放程序常量的地方,属性为只读
4、多父类继承的虚表如何存放?
多重继承的子类对象实际上是将每个父类的完整数据按顺序依次排布,所以拥有每个父类的虚表,父类每个虚表的位置同样在每个父类的起始位置。
oCBaseTest对象内存分布
5、何为虚表Hook?5.3 Hook测试(内存Hook的方式)
测试代码:
class CVFHook { public: typedef void (CVFHook::* MemberFunctionPtr)(); public: static BOOL Hook(void* pVirtrulFunctionVA); private: void PrintData(); };
BOOL CVFHook::Hook(void* pVirtrulFunctionVA) { HMODULE hHookedModule = ::GetModuleHandle(NULL); if (NULL == hHookedModule) { _tprintf(_T("Get module handle fail\n")); return FALSE; } //取重定位后的虚表地址 MemberFunctionPtr* pMemberFunctionPtr = (MemberFunctionPtr*)((INT64)pVirtrulFunctionVA + (INT64)hHookedModule); //去除读写保护 DWORD dwOldProtect = 0; ::VirtualProtect(pMemberFunctionPtr, sizeof(pMemberFunctionPtr), PAGE_READWRITE, &dwOldProtect); *pMemberFunctionPtr = &CVFHook::PrintData; return TRUE; } void CVFHook::PrintData() { printf("PrintData is facked!!!\n"); }然后再dll的加载位置传入虚表的VA参数调用即可:
BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: { CVFHook::Hook((void*)0x0007DF2C); break; } case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; }
注意:这里我们伪造的虚函数 CVFHook::PrintData() 我直接用的是与要 Hook 的虚函数相同的成员函数形式,由于Release版的代码可能会被编译器优化掉函数调用形式,如果如此,需要我们手动编写汇编形式的虚函数代码。
启动测试程序 Console.exe ,然后用 APIMonitor 工具将 VirtrualFunctionHook.dll 注入,然后按任意键观察结果如下:
我们来对前面的测试代码进行反汇编后观察:
版权声明:本文为博主原创文章,如需转载请说明转至http://blog.csdn.net/gufeng99
原文地址:http://blog.csdn.net/gufeng99/article/details/47355085