Verständnisproblem bei Thunks...
-
Wie manche Fortgeschrittene von euch bestimmt wissen, gibt es einen Programmier-Trick um CALLBACK-Funktionen in eine Klasse zu bringen. Ich bin dabei auf zwei Versionen dieses Tricks gestoßen:
Als Beispiel will ich eine WNDPROC Funktion in meine Klasse kapseln. Man kann den Thunk so setzen, dass er entweder den HWND Parameter auf dem Stack modifiziert und zu einer statischen Memberfunktion springt (in der dann der HWND Parameter zum this-Zeiger gecastet wird und schließlich die gewünschte Memberfunktion der Klasse aufgerufen werden kann), oder dass er den this-Zeiger zusätzlich auf den Stack legt und gleich zur gewünschten Memberfunktion springt.
Erstere Version (von Codeproject):
void Init(F2 proc, T1 * pClass) // ist ein template { //make sure that 'this' is in ecx pointer // otherwsie code does not work #ifdef _DEBUG long pthis; _asm { mov dword ptr [pthis],ecx }; assert(pthis == (long) this); #endif // put values thunk.m_movcx = 0xB9; //B9 thunk.m_pThis= (DWORD)pClass; thunk.m_jmp = 0xE9; thunk.m_relproc = *((int *) &proc) - ((int)this+sizeof(_ProcThunk)); // write block from data cache and // flush from instruction cache FlushInstructionCache(GetCurrentProcess(), &thunk, sizeof(thunk)); }Letztere Version (http://staff.develop.com/onion/Articles/cpprep0399.htm):
void Init(WNDPROC proc, void* pThis) { thunk.m_mov = 0x042444c7; // mov dword ptr [esp+0x4], pThis thunk.m_this = (DWORD)pThis; //HWND durch pThis ersetzen thunk.m_jmp = 0xe9; // jmp WndProc thunk.m_relproc = (int)proc - ((int)this+sizeof(_WndProcThunk)); // Relative jmp }Ich hoffe, dass ich das Prinzip soweit verstanden habe (gegebenenfalls bitte ausbessern falls unrichtig). Nun möchte ich darum bitten, dass mir jemand die "Formel" für m_relproc erklärt. Die begreife ich nämlich nicht so ganz.
-
relproc scheint ein Offset zwischen der Member-Funktion und Adresse des Objekts im Speicher zu sein.
Muss allerdings nicht stimmen

-
Also ich habe das so verstanden, ich hoffe das ist richtig:
Bei einem relativen Sprung musst du ja nur einen Offset ermitteln den du von der aktuellen Position springst.
Mit ((int)this+sizeof(_WndProcThunk)) ermittelt man den Instruction Pointer. (der zeigt ja auf den Befehl der als nächstes ausgeführt wird)
Offset = Ziel - aktuelle Position
weil
Ziel = aktuelle Position + Offset
Ich habe mich für die zweite Möglichkeit entschieden, weil die erste IMHO nicht auf allen Windows-Compilern funktioniert. (hab ich nicht auf dem g++ zum Laufen bekommen, da er glaube ich den this pointer nicht in ecx speichert. Werde es aber heute nochmal für mich selbst ausprobieren)
-
Das ist eben das, was ich nicht verstehe. Wieso ein Offset? Wieso nicht einfach:
thunk.m_relproc = *((int *) &proc);
-
Matthias. schrieb:
Also ich habe das so verstanden, ich hoffe das ist richtig:
Bei einem relativen Sprung musst du ja nur einen Offset ermitteln den du von der aktuellen Position springst.
Mit ((int)this+sizeof(_WndProcThunk)) ermittelt man den Instruction Pointer. (der zeigt ja auf den Befehl der als nächstes ausgeführt wird)
Offset = Ziel - aktuelle Position
weil
Ziel = aktuelle Position + Offset
Ich habe mich für die zweite Möglichkeit entschieden, weil die erste IMHO nicht auf allen Windows-Compilern funktioniert. (hab ich nicht auf dem g++ zum Laufen bekommen, da er glaube ich den this pointer nicht in ecx speichert. Werde es aber heute nochmal für mich selbst ausprobieren)
Ja, es funktioniert leider nur auf bestimmten Compilern...Ich hab aber nicht vor, von Visual C++ 6.0 auf was anderes umzusteigen. Man kann es aber trotzdem ohne viel Aufwand umstellen, falls man es doch auf einem anderen Compiler kompilieren will.
Ich versuch jetzt mal deine Erklärung zu behirnen

-
Aziz schrieb:
Das ist eben das, was ich nicht verstehe. Wieso ein Offset? Wieso nicht einfach:
thunk.m_relproc = *((int *) &proc);Das habe ich mich auch schon gefragt, warum kein direkter Sprung gemacht wird. Ich kenne mich aber leider auch nicht in Assembler aus - wird bestimmt seine Gründe haben.

Aber solange du einen relativen Jump ausführst (Opcode 0xE9) musst du natürlich den Offset berechnen, anstatt die Adresse direkt zu benutzen.
-
Ok, ich denke jetzt verstehe ich es. Danke vielmals!
Ich hätte nie gedacht, dass ich diese "Thunk-Geschichte" jemals verstehen werde. Als ich das erste mal im Web darüber gestolpert bin, bin ich sofort vor Schreck davongelaufen
