程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 用detours對C++類成員函數加鉤子

用detours對C++類成員函數加鉤子

編輯:C++入門知識

我所遇到的問題是,我想給一個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

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved