class CTestVirtual
{
public:
virtual void FunA()
{
std::cout << "FuncA " << m_iNum << std::endl;
}
virtual void FunB()
{
std::cout << "FunB " << m_iNum << std::endl;
}
virtual void FunC()
{
std::cout << "FunC " << m_iNum << std::endl;
}
// 很多COM 接口使用的 __stdcall
virtual void STDMETHODCALLTYPE FunD()
{
std::cout << "CChildEx FunD " << m_iNum << std::endl;
}
CTestVirtual(int iNum) : m_iNum(iNum)
{
}
int m_iNum;
};
class CChild : public CTestVirtual
{
public:
virtual void FunA()
{
std::cout << "CChild FuncA " << m_iNum << std::endl;
}
virtual void FunB()
{
std::cout << "CChild FunB " << m_iNum << std::endl;
}
virtual void FunC()
{
std::cout << "CChild FunC " << m_iNum << std::endl;
}
CChild(int iNum) : CTestVirtual(iNum)
{
}
};
class CChildEx : public CChild
{
public:
virtual void STDMETHODCALLTYPE FunD()
{
std::cout << "CChildEx FunD " << m_iNum << std::endl;
}
CChildEx(int iNum) : CChild(iNum)
{
}
};
void STDMETHODCALLTYPE MyFunc(CTestVirtual* pthis)
{
std::cout << "MyFunc " << pthis->m_iNum << std::endl;
}
int main()
{
CTestVirtual* _ttt = new CChildEx(222);
int* pVirtualTable = (int*)(*(int*)_ttt);
std::cout << "+++++++++++++++++++++++++++++++" << std::endl;
_ttt->FunA();
_ttt->FunB();
_ttt->FunC();
_ttt->FunD();
std::cout << "+++++++++++++++++++++++++++++++" << std::endl;
if (1)
{
bool bChangeProtect = true;
HANDLE hdProcess = INVALID_HANDLE_VALUE;
MEMORY_BASIC_INFORMATION mbi = { 0 };
DWORD m_dwOldProtectFlag = 0;
if (bChangeProtect)
{
CTestVirtual* pObj = _ttt;
DWORD** m_pplVrtable = (DWORD**)(pObj);
hdProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ::GetCurrentProcessId());
if (VirtualQueryEx(hdProcess, (LPVOID)(*m_pplVrtable), &mbi, sizeof(mbi)) != sizeof(mbi))
{
return 0;
}
if (!VirtualProtectEx(hdProcess, mbi.BaseAddress, 16, PAGE_EXECUTE_READWRITE, &m_dwOldProtectFlag))
{
return 0;
}
}
// 交换第一第二个虚函数地址,其实就是 FunA FunB
// 扩展一下。可以继承CTestVirtual (比如要 Hook 或者改写地址的第三方类)。
//写一个同样签名的虚函数比如 FunE。用FunE 替代 FunA/B/C
int iTmp = pVirtualTable[0];
pVirtualTable[0] = pVirtualTable[1];
pVirtualTable[1] = iTmp;
// stdcall 或者说非 thiscall 的约定可以用一个普通函数替换掉,这里没有覆盖测试其他约定
// 函数的替换必须使用一样的调用约定的函数替换,比如这里 MyFunc 不能替换pVirtualTable[0..2]
// 直接替换会有内存保护,具体可以单独深究。
pVirtualTable[3] = (int)MyFunc;
if (bChangeProtect)
{
DWORD dwTemp = 0;
if (!VirtualProtectEx(hdProcess, mbi.BaseAddress, 16, m_dwOldProtectFlag, &dwTemp))
{
return 0;
}
CloseHandle(hdProcess);
}
}
std::cout << "+++++++++++++++++++++++++++++++" << std::endl;
_ttt->FunA();
_ttt->FunB();
_ttt->FunC();
_ttt->FunD();
std::cout << "+++++++++++++++++++++++++++++++" << std::endl;
// 注意以下函数运行,不会触发虚函数机制。
CChildEx tt(333);
tt.FunA();
tt.FunB();
tt.FunC();
tt.FunD();
system("pause");
return 0;
}