ESET vs ME
Na tegorocznej edycji konferencji CONFidence 2009 ukazało się CrackMe przygotowane przez dzielnych pracowników polskiego oddziału firmy antywirusowej ESET.
Jako, że nie uczeszczam na tego typu zloty, dowiedziałem się o tym CrackMe ze wpisu na blogu Gynvaela, który opisał metodę jego złamania.
Zachęcony usunięciem moich negatywnych komentarzy z blogu Gynvaela, dotyczących metod zabezpieczeń zastosowanych w CrackMe, postanowiłem w ramach rozrywki i jednocześnie zemsty za cenzurę, opublikować moją analizę i jednocześnie dekompilację CrackMe.
Opis ogólny
CrackMe zostało stworzone w assemblerze pod MASMem w wersji 5 (czyli dość starej), celem jest odnalezienie właściwego hasła (samego, bez pary name/serial).
Użyte zabezpieczenia
Największym zabezpieczeniem CrackMe jest jego niekompatybilność ze wszystkimi innymi wersjami systemu Windows oprócz Windows XP. Na starszych wersjach systemu Windows nawet nie ma co go odpalać (brak mechanizmu obsługującego TLS Callbacks), Windows 2000 wisi, a na nowszych występuje problem z prawami administracyjnymi. Ale może od początku.
Dodatkowym zabezpieczeniem są debug messages, które zostawił programista (zapewne dla zmyłki), choć jak czytam na blogu Gyna, po prostu ich chłopaczyna zapomniał usunąć — biedne, zagubione dziecko.
TLS Callbacks
CrackMe po uruchomieniu wykorzystuje funkcje TLS Callbacks do pobrania adresów swoich funkcji API. Są one wywoływane przez loadera Windows przed wejśćiem w entrypoint, mechanizm ten jest wykorzystywany m.in. przez protektor ExeCryptor. Adresy funkcji API (tylko z biblioteki KERNEL32.dll) pobierane są poprzez liczenie sum kontrolnych wszystkich eksportowanych funkcji z KERNEL32.dll i porównywanie ich z zapisanymi sumami funkcji wykorzystywanych przez CrackMe.
Procedura obliczająca sumę kontrolną wydała mi się na tyle charakterystyczna, że od razu pomyślałem, żeby sprawdzić z jakiego wirusa została podprowadzona przez sprytnego kodera ESET. Po krótkim poszukiwaniu wśród archiwów magazynów virii, poniżej przedstawiam procedurę z CrackMe oraz 2 procedury CRC32 autorstwa Vecny, znanego twórcy wirusów z grupy 29A:
Zmieniona została jedynie wartość inicjalizacyjna CRC_POLY. Wnioski co do korzystania z kodów wirusów w CrackMe firmy antywirusowej pozostawiam czytelnikom.
Tracer
Po pobraniu adresów funkcji API, CrackMe uruchamia kopię swojego procesu w trybie debug (na wzór protektora Armadillo) i oczekuje na wyjątek.
Sprawdzanie hasła
Uruchomienie kopii swojego procesu w trybie debug wykorzystywane jest w procedurze sprawdzającej hasło, gdzie wpisany tekst jest szyfrowany a następnie porównywany z zaszyfrowanym wzorem poprawnego hasła.
Tutaj wchodzi do gry 2 kopia procesu, w pętli debugującej oczekuje na wyjątek. Procedura szyfrowania wpisanego hasła, przed przystąpieniem do samego szyfrowania ustawia flagę TRAP FLAG w rejestrze flag (używając instrukcji:
pushfd ; zapamiętaj stan flag na stosie
xor dword ptr ds:[esp], 100000000b ; ustaw flagę TRAP
popfd ; przywróć stan flag ze stosu
Ustawienie flagi TRAP FLAG sprawi, że wykonanie każdej następnej instrukcji spowoduję wyjątek single step exception (EXCEPTION_SINGLE_STEP) i kontrola zostanie przekazana do pętli debugującej.
O co w tym chodzi? Szyfrowanie wpisanego hasła wykorzystuje serię instrukcji ADD, SUB, XOR i LEA. Jednak przy ustawionej fladze śledzenia krokowego, przed wykonaniem kolejnych instrukcji szyfrujących, kontrola przekazywana jest do pętli debugującej, która sprawdza jaka ma być aktualnie wykonana instrukcja szyfrująca i w zależności co to będzie, dodatkowo modyfikuje wartość przed zaszyfrowaniem.
Wykrywane są 3 rodzaje instrukcji szyfrujących i przed ich wykonaniem modyfikowany jest rejestr EAX (w którym znajduje się fragment wpisanego hasła):
- XOR EAX, IMM32 -> EAX = EAX – 2
- SUB EAX,IMM32 -> EAX = EAX + 1
- ADD EAX,IMM32 -> EAX = EAX ^ 0x10101010
Cała pętla szyfrująca jest na bieżąco śledzona przez drugą kopię programu (cały czas odświeżana jest flaga TRAP FLAG), aż do momentu napotkania specjalnego markera (4 x NOP), który przerywa proces śledzenia, co oznacza, że całe hasło zostało zaszyfrowane, po czym jest porównywane z wzorcem oczekiwanego hasła i na tej podstawie stwierdzana jest jego poprawność.
Odtworzenie poprawnego hasła wymaga odwrócenia kolejności instrukcji szyfrujących oraz dodania dodatkowych instrukcji modyfikujących stan rejestru EAX (symulacja pętli debugującej), całość klepnąłem w PHP i jest dostępna na końcu.
Poprawne hasło to „You talkin’ to me?”.
Bug
CrackMe posiada również buga (feature?), który dzięki sprytnym panom z Microsoft jest poprawnie rozpoznawany przez Windows i nie powoduje zwiechy:
Dekompilacja
Dekompilacja crackme + dekoder i oryginalne 2 wersje crackme:
eset_crackme.zip (102 kB)
Fin
Wklepując w google frazę „ESET Polska” wyskakuje m.in. strona zapowiadająca:
ESET w Polsce: zatrudnimy wszystkich najlepszych specjalistów
Jeśli u was rzeczywiście pracują sami najlepsi, to czy całą resztę macie za idiotów?
PS.
I jeszcze krótka wiadomość dla panów z ESET, którzy odgrażali się, że zrobią mi CrackMe z jakimś RSA64 i zakazem patchowania:
Jak widzicie, takie mózgi jak wy zjadam na śniadanie 😛