1

我正在尝试使用 C++ 和 Win32 API 创建一个 thunk,将这个指针绑定到静态成员函数,所以我可以将该函数用作回调。

现在,我有一个适用于 x64 的工作 thunk,它通过将 r9 寄存器的值(对应于函数的第 4 个参数)设置为此指针的地址来工作。

但是我遇到了 x86 的 thunk 问题,我尝试设置 [esp+10h] 的值(也对应于第 4 个参数)。

这是thunk:

#pragma pack(push, 1)
struct THUNK {
    DWORD mov;               // mov dword ptr[esp+10h], pThis
    DWORD pThis;
    BYTE  jmp;               // jmp relproc
    DWORD relproc;
}
#pragma pack(pop)

这是使用 thunk 的类:

class foo {
    void callback_impl(int a, int b, int c) {
        ...
    }
    static void __stdcall callback(int a, int b, int c, foo *This) {
        This->callback_impl(a, b, c);
    }
public:
    THUNK *thunk;
    foo() {
        thunk = (THUNK*)VirtualAlloc(NULL, sizeof(THUNK), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
        thunk->mov = 0x102444C7;
        thunk->pThis = (DWORD)this;
        thunk->jmp = 0xe9;
        thunk->relproc = DWORD((INT_PTR)&foo::callback - ((INT_PTR)thunk + sizeof(THUNK)));
        FlushInstructionCache(GetCurrentProcess(), this, sizeof(THUNK));
    }
    ~foo() {
        VirtualFree(thunk, sizeof(THUNK), MEM_DECOMMIT);
    }
};

这是回调用户:

void callback_user(void(__stdcall *callback)(int, int, int)) {
    ...
}

// foo f;
// callback_user((void(__stdcall*)(int, int, int))f.thunk);

但是,当我运行程序时,它给了我失败:

运行时检查失败 #0 - ESP 的值未在函数调用中正确保存。这通常是调用使用一种调用约定声明的函数和使用另一种调用约定声明的函数指针的结果。

我怎么解决这个问题?
谢谢。

4

1 回答 1

0

此故障是由 stdcall 约定引起的。调用者希望被调用者清理堆栈的 3 个参数,而被调用者(您的回调)清理 4 个参数,导致 esp 转到错误的位置。您也不能只写信,esp+10h因为调用者可能正在使用它。

现在这是另一种想法:您不能直接设置ecxthis调用成员函数(前提是它使用 stdcall 约定)?

更新:或者您可以将This静态成员函数作为第一个参数,使其最接近堆栈顶部;然后 thunk 可以修改堆栈以使其看起来像一个stdcall带有 4 个参数的函数调用。它看起来像

pop eax
push This
push eax
jmp func
于 2016-10-29T14:08:54.047 回答