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;
}