Część 1 z 3
Jeśli poważnie podchodzisz do inżynierii odwrotnej - zapomnij o trenerach i oszukujących silnikach.
Dobry inżynier odwrotny powinien najpierw zapoznać się z systemem operacyjnym, podstawowymi funkcjami API, ogólną strukturą programu (czym jest pętla uruchamiania, struktury Windowsa, procedury obsługi zdarzeń), format pliku (PE). Pomocne mogą być klasyki Petzolda „Programowanie Windows” (www.amazon.com/exec/obidos/ISBN=157231995X), a także MSDN online.
Najpierw zastanów się, gdzie można wywołać procedurę inicjalizacji pola minowego. Pomyślałem o następującym:
- Po uruchomieniu gry
- Po kliknięciu radosna buźka
- Po kliknięciu Game-> New lub wciśnięciu F2
- Kiedy zmienisz poziom trudności
Postanowiłem sprawdzić komendę akceleratora F2.
Aby znaleźć kod obsługi akceleratora, należy znaleźć procedurę obsługi komunikatów okna (WndProc). Można go prześledzić za pomocą wywołań CreateWindowEx i RegisterClass.
Czytać:
Otwórz IDA, okno importu, znajdź "CreateWindow *", przejdź do niego i użyj polecenia "Skocz odnośnik do operandu (X)", aby zobaczyć, gdzie jest wywoływany. Powinien być tylko jeden telefon.
Teraz spójrz powyżej na funkcję RegisterClass i jej parametr WndClass.lpfnWndProc. W moim przypadku nazwałem już funkcję mainWndProc.
.text:0100225D mov [ebp+WndClass.lpfnWndProc], offset mainWndProc
.text:01002264 mov [ebp+WndClass.cbClsExtra], edi
.text:01002267 mov [ebp+WndClass.cbWndExtra], edi
.text:0100226A mov [ebp+WndClass.hInstance], ecx
.text:0100226D mov [ebp+WndClass.hIcon], eax
.text:01002292 call ds:RegisterClassW
Naciśnij Enter na nazwie funkcji (użyj „N”, aby zmienić jej nazwę na lepszą)
Teraz spójrz na
.text:01001BCF mov edx, [ebp+Msg]
Jest to id wiadomości, która w przypadku naciśnięcia klawisza F2 powinna zawierać wartość WM_COMMAND. Masz znaleźć porównanie do 111 godzin. Można to zrobić, śledząc w dół edx w IDA lub ustawiając warunkowy punkt przerwania w WinDbg i naciskając klawisz F2 w grze.
Tak czy inaczej prowadzi do czegoś takiego
.text:01001D5B sub eax, 111h
.text:01001D60 jz short loc_1001DBC
Kliknij prawym klawiszem myszy na 111h i użyj "Stała symboliczna" -> "Użyj standardowej stałej symbolicznej", wpisz WM_ i Enter. Teraz powinieneś
.text:01001D5B sub eax, WM_COMMAND
.text:01001D60 jz short loc_1001DBC
Jest to łatwy sposób na znalezienie wartości identyfikatora wiadomości.
Aby zrozumieć obsługę akceleratora, sprawdź:
Na jedną odpowiedź to sporo tekstu. Jeśli jesteś zainteresowany, mogę napisać jeszcze kilka postów. Długopisy, krótkie pole minowe zapisane jako tablica bajtów [24x36], 0x0F pokazuje, że bajt nie jest używany (gra mniejsze pole), 0x10 - puste pole, 0x80 - moje.
Część 2 z 3
Ok, przejdźmy do przycisku F2.
Zgodnie z funkcją wndProc przy użyciu akceleratorów klawiatury po naciśnięciu przycisku F2
... odbiera wiadomość WM_COMMAND lub WM_SYSCOMMAND. Najniższe słowo parametru wParam zawiera identyfikator akceleratora.
Ok, już znaleźliśmy, gdzie przetwarzany jest WM_COMMAND, ale jak określić odpowiednią wartość parametru wParam? Tutaj do gry wkracza haker zasobów . Nakarm go binarnymi i pokaże ci wszystko. Jak dla mnie stół akceleratorów.
tekst alternatywny http://files.getdropbox.com/u/1478671/2009-07-29_161532.jpg
Jak widać, przycisk F2 odpowiada 510 w wParam.
Wróćmy teraz do kodu, który obsługuje WM_COMMAND. Porównuje wParam z różnymi stałymi.
.text:01001DBC HandleWM_COMMAND: ; CODE XREF: mainWndProc+197j
.text:01001DBC movzx eax, word ptr [ebp+wParam]
.text:01001DC0 mov ecx, 210h
.text:01001DC5 cmp eax, ecx
.text:01001DC7 jg loc_1001EDC
.text:01001DC7
.text:01001DCD jz loc_1001ED2
.text:01001DCD
.text:01001DD3 cmp eax, 1FEh
.text:01001DD8 jz loc_1001EC8
Użyj menu kontekstowego lub skrótu klawiaturowego „H”, aby wyświetlić wartości dziesiętne i możesz zobaczyć nasz skok
.text:01001DBC HandleWM_COMMAND: ; CODE XREF: mainWndProc+197j
.text:01001DBC movzx eax, word ptr [ebp+wParam]
.text:01001DC0 mov ecx, 528
.text:01001DC5 cmp eax, ecx
.text:01001DC7 jg loc_1001EDC
.text:01001DC7
.text:01001DCD jz loc_1001ED2
.text:01001DCD
.text:01001DD3 cmp eax, 510
.text:01001DD8 jz loc_1001EC8 ; here is our jump
Prowadzi to do fragmentu kodu, który wywołuje pewne procenty i kończy działanie wndProc.
.text:01001EC8 loc_1001EC8: ; CODE XREF: mainWndProc+20Fj
.text:01001EC8 call sub_100367A ; startNewGame ?
.text:01001EC8
.text:01001ECD jmp callDefAndExit ; default
Czy to jest funkcja, która inicjuje nową grę? Dowiedz się w ostatniej części! Bądźcie czujni.
Część 3 z 3
Przyjrzyjmy się pierwszej części tej funkcji
.text:0100367A sub_100367A proc near ; CODE XREF: sub_100140C+CAp
.text:0100367A ; sub_1001B49+33j ...
.text:0100367A mov eax, dword_10056AC
.text:0100367F mov ecx, uValue
.text:01003685 push ebx
.text:01003686 push esi
.text:01003687 push edi
.text:01003688 xor edi, edi
.text:0100368A cmp eax, dword_1005334
.text:01003690 mov dword_1005164, edi
.text:01003696 jnz short loc_10036A4
.text:01003696
.text:01003698 cmp ecx, dword_1005338
.text:0100369E jnz short loc_10036A4
Istnieją dwie wartości (dword_10056AC, uValue) wczytywane do rejestrów eax i ecx i porównywane z kolejnymi dwoma wartościami (dword_1005164, dword_1005338).
Spójrz na rzeczywiste wartości za pomocą WinDBG ('bp 01003696'; on break 'p eax; p ecx') - wydawały mi się wymiarami pola minowego. Gra z niestandardowym rozmiarem pola minowego pokazała, że pierwsza para to nowe wymiary, a druga - aktualne wymiary. Ustalmy nowe nazwy.
.text:0100367A startNewGame proc near ; CODE XREF: handleButtonPress+CAp
.text:0100367A ; sub_1001B49+33j ...
.text:0100367A mov eax, newMineFieldWidth
.text:0100367F mov ecx, newMineFieldHeight
.text:01003685 push ebx
.text:01003686 push esi
.text:01003687 push edi
.text:01003688 xor edi, edi
.text:0100368A cmp eax, currentMineFieldWidth
.text:01003690 mov dword_1005164, edi
.text:01003696 jnz short loc_10036A4
.text:01003696
.text:01003698 cmp ecx, currentMineFieldHeight
.text:0100369E jnz short loc_10036A4
Nieco później nowe wartości zastępują aktualny i wywoływany jest podprogram
.text:010036A7 mov currentMineFieldWidth, eax
.text:010036AC mov currentMineFieldHeight, ecx
.text:010036B2 call sub_1002ED5
A kiedy to zobaczyłem
.text:01002ED5 sub_1002ED5 proc near ; CODE XREF: sub_1002B14:loc_1002B1Ep
.text:01002ED5 ; sub_100367A+38p
.text:01002ED5 mov eax, 360h
.text:01002ED5
.text:01002EDA
.text:01002EDA loc_1002EDA: ; CODE XREF: sub_1002ED5+Dj
.text:01002EDA dec eax
.text:01002EDB mov byte ptr dword_1005340[eax], 0Fh
.text:01002EE2 jnz short loc_1002EDA
Byłem całkowicie pewien, że znalazłem tablicę minową. Przyczyna cyklu, który inicjuje tablicę długości 360h bajtów (dword_1005340) z 0xF.
Dlaczego 360h = 864? Poniżej znajduje się kilka wskazówek, które zajmują 32 bajty, a 864 można podzielić przez 32, więc tablica może pomieścić 27 * 32 komórek (chociaż interfejs użytkownika pozwala na maksymalnie 24 * 30 pól, istnieje jedno bajtowe wypełnienie wokół tablicy dla granic).
Poniższy kod generuje górną i dolną granicę pola minowego (0x10 bajtów). Mam nadzieję, że w tym bałaganie widać iterację pętli;) Musiałem użyć kartki i długopisu
.text:01002EE4 mov ecx, currentMineFieldWidth
.text:01002EEA mov edx, currentMineFieldHeight
.text:01002EF0 lea eax, [ecx+2]
.text:01002EF3 test eax, eax
.text:01002EF5 push esi
.text:01002EF6 jz short loc_1002F11 ;
.text:01002EF6
.text:01002EF8 mov esi, edx
.text:01002EFA shl esi, 5
.text:01002EFD lea esi, dword_1005360[esi]
.text:01002EFD
.text:01002F03 draws top and bottom borders
.text:01002F03
.text:01002F03 loc_1002F03: ; CODE XREF: sub_1002ED5+3Aj
.text:01002F03 dec eax
.text:01002F04 mov byte ptr MineField?[eax], 10h ; top border
.text:01002F0B mov byte ptr [esi+eax], 10h ; bottom border
.text:01002F0F jnz short loc_1002F03
.text:01002F0F
.text:01002F11
.text:01002F11 loc_1002F11: ; CODE XREF: sub_1002ED5+21j
.text:01002F11 lea esi, [edx+2]
.text:01002F14 test esi, esi
.text:01002F16 jz short loc_1002F39
Reszta podprogramu rysuje lewą i prawą granicę
.text:01002F18 mov eax, esi
.text:01002F1A shl eax, 5
.text:01002F1D lea edx, MineField?[eax]
.text:01002F23 lea eax, (MineField?+1)[eax+ecx]
.text:01002F23
.text:01002F2A
.text:01002F2A loc_1002F2A: ; CODE XREF: sub_1002ED5+62j
.text:01002F2A sub edx, 20h
.text:01002F2D sub eax, 20h
.text:01002F30 dec esi
.text:01002F31 mov byte ptr [edx], 10h
.text:01002F34 mov byte ptr [eax], 10h
.text:01002F37 jnz short loc_1002F2A
.text:01002F37
.text:01002F39
.text:01002F39 loc_1002F39: ; CODE XREF: sub_1002ED5+41j
.text:01002F39 pop esi
.text:01002F3A retn
Inteligentne użycie poleceń WinDBG może zapewnić fajny zrzut pola minowego (niestandardowy rozmiar 9x9). Sprawdź granice!
0:000> db /c 20 01005340 L360
01005340 10 10 10 10 10 10 10 10-10 10 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
01005360 10 0f 0f 0f 0f 0f 0f 0f-0f 0f 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
01005380 10 0f 0f 0f 0f 0f 0f 0f-0f 0f 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
010053a0 10 0f 0f 0f 0f 0f 0f 0f-0f 0f 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
010053c0 10 0f 0f 0f 0f 0f 0f 0f-0f 0f 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
010053e0 10 0f 0f 0f 0f 0f 0f 0f-0f 0f 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
01005400 10 0f 0f 0f 0f 0f 0f 0f-0f 0f 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
01005420 10 0f 0f 0f 0f 0f 0f 0f-0f 0f 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
01005440 10 0f 0f 0f 0f 0f 0f 0f-0f 0f 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
01005460 10 0f 0f 0f 0f 0f 0f 0f-0f 0f 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
01005480 10 10 10 10 10 10 10 10-10 10 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
010054a0 0f 0f 0f 0f 0f 0f 0f 0f-0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
010054c0 0f 0f 0f 0f 0f 0f 0f 0f-0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
010054e0 0f 0f 0f 0f 0f 0f 0f 0f-0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
Hmm, wygląda na to, że potrzebuję kolejnego wpisu do zamknięcia tematu