Detour Hook COM成员函数present和Hook 类成员函数
void * pPresent=NULL;//IDirect3DDevice9::Present函数地址指针
pPresent=(void*)*(DWORD*)(*(DWORD*)Device+0x44);//IDirect3DDevice9* Device DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); DetourAttach((PVOID *)&pPresent, New_Present); DWORD nErr = DetourTransactionCommit();
HRESULT __stdcall New_Present( LPDIRECT3DDEVICE9 pDxdevice, //类的this指针 CONST RECT * pSourceRect, //此参数请参考dx sdk CONST RECT * pDestRect, //同上 HWND hDestWindowOverride, //同上 CONST RGNDATA * pDirtyRegion //同上 ) { __asm pushad static float lastTime = (float)timeGetTime(); float currTime = (float)timeGetTime(); float timeDelta = (currTime - lastTime)*0.001f; Device->BeginScene(); Device->Clear(0, NULL, D3DCLEAR_TARGET,D3DCOLOR_XRGB(0,0,0), 1.0f, 0); fps.CalcFPS(timeDelta); fps.Show(); Device->EndScene(); __asm popad __asm { push pDirtyRegion push hDestWindowOverride push pDestRect push pSourceRect push pDxdevice call pPresent } lastTime = currTime; }
另一种方式,采用__declspec(naked)方式,注意的是这种方式不需要编译器添加任何汇编代码,在标明naked的函数中是不可以使用任何赋值,所以把它单独提出来:还要注意子函数New_Present_Sub最好用__stdcall,这样不用考虑堆栈平衡
void __stdcall New_Present_Sub( LPDIRECT3DDEVICE9 pDxdevice) { if (pDxdevice) { static BOOL bInitFps = FALSE; if (!bInitFps) { bInitFps = g_fps.Create(pDxdevice); MyOutputDebugString("tpfps-[New_Present] g_fps.Create"); } if (bInitFps) { static DWORD lastTime = timeGetTime(); DWORD currTime = timeGetTime(); DWORD timeDelta = (currTime - lastTime); pDxdevice->BeginScene(); g_fps.CalcFPS(timeDelta);//传入的是毫秒 switch (g_nOpe) { case SHOW_FPS: { g_fps.Show(); } break; case RECODE_FPS: { g_fps.RecordShow(pDxdevice, g_bAutoRecord, g_nAutoRecordTime); if (g_fps.bAutoRecordExit()) { MyOutputDebugString("[New_Present]bAutoRecordExit"); g_nOpe = UNRECODE_FPS; } } break; case UNRECODE_FPS: { g_fps.EndRecord(pDxdevice); g_nOpe = SHOW_FPS; } break; default: break; } pDxdevice->EndScene(); lastTime = currTime; } } } __declspec(naked) HRESULT __stdcall New_Present( LPDIRECT3DDEVICE9 pDxdevice, //类的this指针 CONST RECT * pSourceRect, //此参数请参考dx sdk CONST RECT * pDestRect, //同上 HWND hDestWindowOverride, //同上 CONST RGNDATA * pDirtyRegion //同上 ) { __asm// 必不可少的,把ebp代码去掉或pushad去掉,都会程序OVER { push ebp mov ebp, esp pushad pushfd } __asm { mov esi, pDxdevice push esi mov eax, New_Present_Sub call eax } __asm { popfd popad mov esp, ebp pop ebp jmp g_pPresent } }
Hook类成员函数:
术语说明:
原函数:被hook的target
hook函数:指自己写的函数,也即让原函数jmp到该函数
原因分析:
用detours hook类成员函数,因为成员函数会有一个this指针,如果在hook函数中调用原函数,没有传入this指针,那么原函数的调用将会出错。
解决方法是:
1. 首先在ollydbg中找到这个this指针。其实也就是那个类的对象地址,一般来说,在ollydbg中调用原函数时,也即在call原函数之前,总会有一条指令mov ecx,xxx或者lea ecx,xxx,那个ecx就是我们要找的this指针!
2. 在hook函数调用原函数时使用内联汇编代码调用,push所有的参数之后,然后初始化ecx为this指针的值!然后在call原函数,这样call原函数就不会出现崩溃。
需要注意的地方:
1. 注意原函数是否能自动平衡堆栈,如不行,则需要在hook函数中自己手动平衡堆栈
2. hook函数返回时,必须是手动编写汇编返回(ret),不要让系统帮忙返回!在带有参数的hook例子中,系统帮忙放回必定出错!
3. ret后面的值与传入hook函数的参数有关,一般来说,该值=4*参数个数
或者如上面:代替的函数原型第一个参数加this即可。