我正在尝试使用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的值未在函数调用中正确保存。这通常是调用使用一个调用约定声明的函数的结果,函数指针使用不同的调用约定声明。
我该如何解决这个问题呢? 感谢。
答案 0 :(得分:0)
此失败是由stdcall约定引起的。调用者希望被调用者清理3个参数堆栈,而被调用者(你的回调)清理4个参数,导致esp进入错误的位置。此外,您不能只写esp+10h
,因为调用者可能正在使用它。
现在这里有另一种想法:你不能只将ecx
设置为this
并直接调用成员函数(假设它使用stdcall约定)吗?
更新:或者您可以将This
作为静态成员函数的第一个参数,使其最接近堆栈顶部; thunk然后可以修改堆栈,使其看起来像一个带有4个参数的stdcall
函数调用。它看起来像
pop eax
push This
push eax
jmp func