2016-10-29 13 views
1

Я пытаюсь создать thunk с C++ и Win32 API, который связывает этот указатель со статической функцией-членом, поэтому я могу использовать эту функцию в качестве обратного вызова.Как связать этот указатель со статической функцией-членом с помощью thunk в C++

Теперь у меня есть рабочий кусок для x64, он работает, установив значение регистра r9 (соответствует 4-му параметру функции) на адрес этого указателя.

Но у меня возникла проблема с thunk для x86, я попытался установить значение [esp + 10h] (также соответствует 4-му параметру).

Вот преобразователь:

#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) 

А вот класс, который использует преобразователь,:

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 не было должным образом сохранено во время вызова функции. Обычно это результат вызова функции, объявленной с одним вызовом, с указателем функции, объявленным с другим соглашением о вызовах.

Как решить эту проблему?
Спасибо.

+1

Пожалуйста, предоставьте [mcve]. – IInspectable

ответ

0

Этот отказ вызван конвенцией stdcall. Caller ожидает, что вызывающая сторона очистит 3 аргумента за стек, пока вызываемый (ваш callback) очищает 4 аргумента, заставляя esp переходить в неправильное местоположение. Также вы не можете просто написать esp+10h, поскольку вызывающий может использовать его.

Теперь вот альтернативная идея: не можете ли вы просто установить ecx на this и вызвать функцию-член напрямую (при условии, что она использует соглашение stdcall)?

ОБНОВЛЕНИЕ: Или вы можете поместить This в качестве первого аргумента своей статической функции-члена, чтобы она была ближе всего к вершине стека; thunk затем может модифицировать стек, чтобы он выглядел как вызов функции stdcall с 4 аргументами. Это будет выглядеть примерно так:

pop eax 
push This 
push eax 
jmp func 
+0

'stdcall' не использует регистры, поэтому нет, вы не можете поместить указатель' this' в 'ECX', его нужно передать в стек в качестве первого параметра. Вместо этого вы думаете о 'thiscall', который помещает' this' в 'ECX' (также,' thiscall' не переносится через компиляторы, а 'stdcall' есть) –

+0

Комбинация цельной точки thunk/static member здесь вызов нестатической функции-члена, которая использует соглашение 'thiscall' - мое предложение состояло в том, чтобы попытаться вызвать эту нестационарную функцию-член напрямую. Справедливая точка в переносимости 'thiscall'; Мое замечание о 'stdcall' было об этом также. Теперь я понимаю, что я не правильно его разорвал; by 'stdcall' Я имел в виду« thiscall », который работает точно так же, как' stdcall', кроме 'this' в' ecx'. Однако это можно смягчить, используя '__thiscall', который должен заставить MSVC/gcc делать именно это. –

+0

@ AndreyTurkin Я не могу поставить это как первый аргумент, потому что иначе функция 'callback_user' больше не будет работать. – weedguy