Thunki nie dają mi spać

0

Ostatnio zainteresował mnie temat thunków. Siedzę i kombinuję, ale gdzieś robię błąd i nie mogę wpaść na to gdzie. Przy okazji mam też kilka pytań na temat assembly.
EDIT: kod usunalem poniewaz znalazlem zrodlo problemow. Jak to przewaznie bywa - sprawka braku wiedzy ;)

1. Czy jest w ogóle możliwe ZUPEŁNE ominięcie statycznej/globalnej funkcji? I jeśli tak, to można docelową metodę wywołać konwencją thiscall, żeby nie ruszać parametru HWND już na wejściu?
Przekonalem sie, ze to w sumie nic trudnego. Wystarczy thunk robiacy miejsce miedzy HWND a adresem powrotu i wrzucajacy tam pointer instancji klasy. Oczywiscie metoda musi byc oznaczona jako __stdcall.
Jednak pojawia sie inny problem, poniewaz oszukujac kompilator thunkiem, nie mozna uzyc powrotu z funkcji w stylu

return DefWindowProc( hwnd, msg, wparam, lparam )

. Najpierw trzeba przywrocic stos do pierwotnej formy czyli wywalic wskaznik klasy oraz poprzesuwac argumenty. Rozwiazanie z zastapieniem HWND jest lepsze i prostsze. Coz... przynajmniej sie czegos nauczylem :)
2. Rev w tym poście http://4programmers.net/Forum/1048116 użył dla

JMP

opkodu E9, co wyczytałem w manualu Intela, że podaje się jako operand adres relatywny. Dlaczego nie użyć tutaj np. FF25</del> juz ogarniam z grubsza w czym rzecz.
3. Czy można w thunkach swobodnie mieszać argumentami potencjalnie wywołanej funkcji zakładając, że nie będzie się gmyrać ze stosem? Czyli np. rozbijając LPARAM na dwa 16 bitowe wartości i wrzucenie jednej z nich na miejsce nieużywanego WPARAM?
5. Czy ta strona https://defuse.ca/online-x86-assembler.htm#disassembly jest dobrym źródłem na sklecanie bytecode'u czy lepiej tworzyć pseudo-programy np. w NASM i stamtąd brać opkody?
6. Jak wygląda użycie thunków w przypadku dziedziczenia i metod wirtualnych? Rozumiem, że bez "ręcznego" znajdowania adresu vtable, indeksu metody i delty "thisa" się nie obejdzie?
7. Kod 32-bitowy, VS2012 CTP nov 2012. non-portable mnie nie uwiera ;)

Z góry dziękuję za odpowiedzi i wskazówki :)

1

Najpierw trzeba przywrocic stos do pierwotnej formy czyli wywalic wskaznik klasy oraz poprzesuwac argumenty.

Wystarczy ustawić prawidłową wartość wskaźnika stosu. To co na nim jest istotne, to tylko adres powrotu na wierzchołku. Argumenty możesz na tym etapie nawet zamazać.

0

Dzięki za odp.

Nie wiem czy dobrze to rozumiem. Do metody nie mogę zwyczajnie skoczyć tylko muszę ją jawnie wywołać
poprzez CALL, lub JMP z wrzuceniem adresu powrotnego, żeby posprzątać bałagan jaki zrobiłem na stosie.
Wtedy w ciele metody mogę użyć

 return DefWindowProc(...)

bo i tak wrócę z powrotem
do thunku a nie do funkcji wywołującej "wndproc". Czy taki kod będzie poprawny?

PUSH EBP
MOV EBP, ESP
SUB ESP, 4				; ramka 
MOV EAX, DWORD PTR SS:[EBP+4]           ; ; dotychczasowa wartość EAX nie jest ważna, bo i tak znajdzie się tam rezultat metody
MOV DWORD PTR SS:[EBP], EAX		; przeniesienie starego EBP
MOV EAX, DWORD PTR SS:[EBP+8]           
MOV DWORD PTR SS:[EBP+4], EAX	; przeniesienie adresu callera
MOV DWORD PTR SS:[EBP+8], THIS_POINTER	; wrzucenie pointera na instancję klasy
MOV EAX, METHOD_ADDRESS	; wrzucenie adresu metody
MOV ESP, EBP
POP EBP					; ustawienie poprawnego EBP i ESP
CALL EAX				; wywołanie metody
RETN 14							; zdjęcie adresu do EIP i "usunięcie" pięciu argumentów (cztery standardowe + "extra" this o którym caller nie miał bladego pojęcia w momencie wywoływania metody )

Trochę się rozmnożyło tych instrukcji, ale od razu mówię, że mistrzem assembly nie jestem :P

Trochę pogrzebałem nt. systemu metod wirtualnych i jego implementacji w Visualu. Z tego co zrozumiałem
to wystarczy przechowywać gdzieś rozmiar danej klasy ( każda kolejna dziedzicząca to += sizeof(LevelUpClass) ), aby móc policzyć deltę dla "thisa" oraz indeks
danej metody. Wtedy rzeczywisty adres można sobie wyliczyć. Zgadza się? Wiem, że czeka mnie studiowanie
kodu w OllyDbg, ale czy ktoś kiedyś liznął trochę tego tematu? Ciężka to sprawa czy można przełknąć? :)

1
PUSH EBP
MOV EBP, ESP
SUB ESP, 4                ; ramka 
MOV EAX, DWORD PTR SS:[EBP+4]

niepotrzebna ci ta ramka, przecież wystarczy adresować wzgledem esp:

MOV EAX, DWORD PTR SS:[ESP+8] ; chyba

ego co zrozumiałem to wystarczy przechowywać gdzieś rozmiar danej klasy ( każda kolejna dziedzicząca to += sizeof(LevelUpClass) ), aby móc policzyć deltę dla "thisa" oraz indeks danej metody. Wtedy rzeczywisty adres można sobie wyliczyć. Zgadza się?

Tylko do czego ci to?

0

No tak, właśnie się skapnąłem, że coś przekombinowałem.

Zasadniczo to do niczego, zwykła ciekawość :)

EDIT: Niestety jest to twardszy orzech do zgryzienia niż się wydawało na początku. Żeby metoda poprawnie interpretowała argumenty musiałem ręcznie przesunąć ESP co wiązało się z tym, że adres powrotny zostanie prędzej czy później nadpisany przez lokalne zmienne. Rozwiązałem to w ten sposób, że adres powrotu kopiuje do zmiennej zaalokowanej razem z thunkiem a na miejsce adresu na stosie wrzucam wskaźnik na obiekt.

MOV EAX, DWORD PTR SS:[EBP]
MOV DWORD PTR DS:[0x00000000], EAX   ; tutaj kopiuję adres powrotu
MOV EAX, 0x00000000    
MOV DWORD PTR SS:[EBP], EAX   ; tutaj wrzucam pointer
MOV EAX, 0x00000000    ; tutaj adres metody
CALL EAX
MOV EDX, DWORD PTR DS:[0x00000000]   ; przywracam adres powrotu
MOV EBP, EDX
RET

No i jakoś przeszło... ale oczywiście do czasu. Pokazuje się okno, jednak po chwili rzuca crash.

GETMINMAXINFO, WM_NULL, WM_NCCREATE, WM_CREATE, WM_NCCALCSIZE, WM_NULL, WM_CREATE, WM_CREATE, 
WM_SHOWWINDOW, WM_NULL, WM_CREATE, WM_WINDOWPOSCHANGING, WM_NULL, WM_CREATE, WM_WINDOWPOSCHANGING,
WM_CREATE, WM_ACTIVATEAPP, WM_NULL, WM_CREATE, WM_NCACTIVATE, WM_GETICON, WM_NULL, WM_GETICON, 
WM_NULL, WM_GETICON, WM_NULL, WM_GETICON, WM_GETICON -> ACCESS VIOLATION WHEN WRITING.

Takie komunikaty dostaje. Po jednym z kolejnych WM_GETICON jest crash.

Edit2: W zasadzie to nie wiem czemu się tak przyczepiłem do __stdcall. Przecież wystarczy wywołać metodę wrzucając wskaźnik do ECX. Zaćmienie umysłu? ;)

MOV ECX, POINTER
MOV EAX, METHOD_ADDRESS
JMP EAX

1 użytkowników online, w tym zalogowanych: 0, gości: 1