我所遇到的問題是,我想給一個flash中的某個函數加鉤子,我知道這個函數的地址,但是它是一個C++的類成員函數。detours的安裝目錄下的samples\member就是一個如何對C++的類成員函數加hook的例子。
假設原來的函數聲明是:
class CMember {
public:
void Target(const char* str);
};
一般來說,類成員函數都是遵守__thiscall。但是我們不能強行給一個全局函數加上__thiscall的修飾符。所以,首先,必須得先寫一個虛假的類,然後重新聲明這個函數。
class CDetour {
public:
void Mine_Target(const char* str);
};
然後我做了一點小技巧,把以前的函數聲明成__stdcall
typedef void (__stdcall *pfunc)(const char* str);
pfunc oldfunc=(pfunc)0×00401040;
然後還是按以前那樣attach,但是此時成員函數指針的轉換很巧妙
DetourAttach( &(PVOID &)oldfunc,(PVOID)(&(PVOID&) A::func));
你不能把一個成員函數轉換成void*,但是你可以把它轉換成void*&。
順便今天發現一個技巧,這樣輸出的結果是一個index,如0、1、2
void CMember::Target(const char* str){
std::cout<<&CMember::Target<<std::endl;
}
但是這樣輸出的就是函數地址:
void CMember::Target(const char* str){
std::cout<<(void*&)CMember::Target<<std::endl;
}
嗯,繼續說,Mine_Target的實現
我想到的第一個方法是,利用__stdcall模擬__thiscall。
先保存ecx,並用this給ecx賦值。p.s.如果不是this,而是一個棧變量,mov就要換成lea。
__asm{
push ecx;
mov ecx,this;
}
然後調用以前的函數:
oldfunc(str);
然後恢復ecx:
__asm{
pop ecx;
}
這裡利用的原理就是,__stdcall和__thiscall的區別僅在於後者多傳一個ecx。但是這種方法有時候不成功。因為在調用oldfunc的時候,需要傳遞參數,此時有可能會利用ecx作臨時寄存器,先把參數mov到ecx中,然後push。
我最後的辦法是用__fastcall來模擬__thiscall,這種方法很優雅,不需要內聯匯編。原來的函數定義就變成了
typedef char (__fastcall *pFunc)(void* pthis,int dummy,const char *str);
按照__fastcall的規范,最前面2個參數應該通過ecx,edx傳遞,後面的從右向左依次push。相當於我多傳遞了一個edx,無所謂啦。
作者 changming