1

I'm trying to create a thunk with C++ and Win32 API, that binds this pointer to static member function, so I can use that function as a callback.

Now, I have a working thunk for x64, it works by setting the value of r9 register (corresponds to 4th parameter of a function) to the address of this pointer.

But I'm having a problem with thunk for x86, I tried to setting the value of [esp+10h] (also corresponds to 4th parameter).

Here's the 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)

And here's the class that uses the 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);
    }
};

And here's the callback user:

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

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

However, when I ran the program, it gave me the failure:

Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.

How can I solve this problem?
Thanks.

4

1 に答える 1

0

この失敗は stdcall 規則が原因です。呼び出し元は、呼び出し先が 3 つの引数に相当するスタックをクリーンアップすることを期待していますが、呼び出し先 (コールバック) は 4 つの引数をクリーンアップし、esp が間違った場所に移動する原因となります。esp+10hまた、呼び出し元が使用している可能性があるため、単に書き込むことはできません。

ここに別のアイデアがあります: メンバー関数を直接設定ecxthisて呼び出すことはできませんか (stdcall 規則を使用している場合)。

更新:またはThis、静的メンバー関数の最初の引数として配置して、スタックトップに最も近づけることができます。stdcallthunk はスタックを変更して、4 つの引数を持つ関数呼び出しのように見せることができます。それは次のようになります

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