首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

利用 Thunk 技術讓 C++ 成員函數成為回呼函數

     編輯:關於C++

Windows API 常需回呼函數(__stdcall),但 C++ 成員函數隱含 this 指標(__thiscall),無法直接作為回呼。Thunk 技術可動態生成機器碼,將 this 載入 ECX 後跳轉至成員函數,橋接兩種呼叫慣例。

核心原理

__thiscall 將 this 放入 ECX,參數右至左入棧,呼叫者清棧;__stdcall 參數同樣右至左入棧,但被呼叫者清棧。兩者棧平衡一致,僅差 this 傳遞方式。Thunk 只需兩條指令:

mov ecx, pThis
jmp member_func_address

Thunk 結構實作

#pragma pack(push, 1)
struct MemFunToStdCallThunk {
    BYTE  m_mov;      // 0xB9 (mov ecx, imm32)
    DWORD m_this;     // this 指標
    BYTE  m_jmp;      // 0xE9 (jmp rel32)
    DWORD m_relproc;  // 相對跳轉位移

    BOOL Init(DWORD_PTR proc, void* pThis) {
        m_mov = 0xB9;
        m_this = PtrToUlong(pThis);
        m_jmp = 0xE9;
        m_relproc = DWORD((INT_PTR)proc - ((INT_PTR)this + sizeof(MemFunToStdCallThunk)));
        FlushInstructionCache(GetCurrentProcess(), this, sizeof(*this));
        return TRUE;
    }
    void* GetCodeAddress() { return this; }
};
#pragma pack(pop)

使用範例

class CTestClass {
    int m_nBase;
    MemFunToStdCallThunk m_thunk;
    void memFun(int m, int n) { /* ... */ }
public:
    CTestClass() : m_nBase(10) {}
    void Test() {
        m_thunk.Init(UnionCastType<DWORD_PTR>(&CTestClass::memFun), this);
        auto fun = (StdCallFun)m_thunk.GetCodeAddress();
        fun(1, 3); // 觸發回呼
    }
};

成員函數指標轉換

利用 union 進行型別懲罰,將成員函數指標轉為 DWORD_PTR:

template <typename TDst, typename TSrc>
TDst UnionCastType(TSrc src) {
    union { TDst uDst; TSrc uSrc; } uMedia;
    uMedia.uSrc = src;
    return uMedia.uDst;
}

調用流程:參數入棧 → 跳入 Thunk → ECX 載入 this → 跳轉成員函數執行。需注意資料執行保護(DEP)環境下需確保記憶體可執行,FlushInstructionCache 確保指令快取一致性。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved