DATA: 1998.10.20 ; Bielsko-Biala
Yo! Oto moje drugie faq na temat Crackingu ;) Tym razem zlamiemy zabezpieczenie w malutkim programiku pod wiele mowiaca nazwa - "Crackme" (ver 1) napisanym przez Cruehead/MiB Jesli pliczek nie jest dolaczony do faq to szukac na stronie domowej MiB - server.kibla.org/lusers/mib/ Programik ma dosc proste zabezpieczenie nazwa/kod czyli kod jest kalkulowany na podstawie nazwy ;). Podstawy opisalem w czesci pierwszej wiec nie ma po co pisac tego drugi raz. Chociaz i tak jest to napisane najprosciej jak umialem ;).
Co bedzie potrzebne : tylko SoftICE dla W95 no i Crackme v1.0 ;) przyda sie tez kartka i olowek.
Jesli: chcesz zadac pytanie/przeslac kase/masz jakies sugestie/jestes szefem browaru/firmy computerowej to pisz na adres :tomroy@kki.net.pl tomroy@polbox.com lub tomroy@friko6.onet.pl ;)
UWAGA !!!
Musze sie zorientowac czy te moje wypociny ktos czyta, wiec jesli zalezy Ci na wydaniu kolejnych czesci (nie obiecuje !!!) to napisz na ktorys z powyzszych adresow Napisz obojetnie co - wazne tylko zebym wiedzial ze czytales to FAQ ;)) Mozesz napisac jakies sugestie ;) np. ze to co pisze jest niezrozumiale/zrozumiale i musze/nie_musze zmieniac stylu, a moze zauwazyles jakies bledy ???
Uwaga! przysylac tylko do konca 1998 !!!
Jesli uwazasz ze FAQ jest do Dupy ... to go nie czytaj. Nie odpowiadam za nie zgodne z prawem uzycie wiadomosci tutaj nabytych!!! Jesli nie rozumiesz tego co tu napisalem to sie nie przejmuj bo przyznam ze troche sie zapetlilem i mozliwe ze to napisalem nie zrozumiale :)
No wiec zaczynamy ;)
Na poczatek : przypuszczam ze zainstalowales S-ice'a (SoftICE). Znasz ASM-a (hmmm...tego to chyba z tekstu nie mozna wywnioskowac ;)) ). Masz jakiekolwiek pojecie (i tego tez nie ;) ).
1. Uruchamiamy Crackme.exe wchodzimy Help|Register wpisujemy dowolna NAZWE ponizej dowolny SERIAL np. ja wypelnilem te pola tak : "tomroy" i "1234567"
2. Wchodzimy do s-ice wciskajac Ctrl+D (mozna ta kombinacje zmienic) ustawiamy Breakpoint na GetDlgItemTextA
bpx getdlgitemtexta
wychodzimy z s-ice naciskajac Ctrl+d lub F5 lub komenda x klikamy OK, teraz s-ice powinien sie pokazac.
3. UWAGA! - przerwanie zostalo spowodowane pierwszym polem tekstowym czyli pobraniem do pamieci NAZWY ("tomroy") nie SERIALA ! naciskamy F11 aby wrocic do programu (poniewaz aktualnie jestesmy w miejscu skad zostala wywolana funkcja GetDlgItemTextA) - zobaczmy teraz kilka pol wyzej od miejsca gdzie jestesmy - okno kodu przesuwamy Ctrl+'strzalka w gore' albo myszka (po prawej stronie jest przewijanie).
PUSH 0B // nie wazne PUSH 0040218E // WAZNE !!! PUSH 000003E8 // nie wazne PUSH DWORD PTR [EBP+08] // nie wazne CALL USER32!GetDlgItemTextA // miejsce wywolania funkcji
Jestesmy teraz na jednej instrukcji ponizej wywolania funkcji. Co to jest stos to chyba wszyscy wiemy ;). wszystkie argumenty PUSH-y (ze tak powiem ;) ) sa argumentami wywolania GetDlgItemTextA. "Tlumaczac" to na C++ wyglada to tak :
int GetDlgItemTextA(int dialoghandle,int controlid,char *buffer,int maxlen);
| DWORD PT... | | 000003E8 | | 0040218E | | 0B |
Interesuje nas 3 argument czyli wskaznik do jakiegos lancucha znakow. Ten 3 argument to PUSH 0040218E !! Oczywiscie nie musisz pamietac ktory to PUSH wystarczy ze sobie wpiszesz
d 0B //nic d 0040218E //Wow! nasza NAZWA ! ....... //dalej nie musisz sprawdzac ;)
UWAGA ! jest jeszcze takie cos : jesli nie jestes pewien w jakim miejscu w pamieci jest aktualnie nasz SERIAL lub NAZWA mozemy to sprawdzic tak:
s 0 L FFFFFFFFFF "3459310"
Ta komenda szuka w pamieci komorki z dana wartoscia ("3459310"). 0 to poczatkowy adres , L nie ma specjalnego znaczenia ale trzeba wpisac ;) , FFFFFFFFFF to koncowy adres (czyli przeszukiwannie od 0 do FFFFFFFFFF) , "3459310" to wartosc ktorej szukamy - ale uwazaj jesli twoj SERIAL jest np. taki "1234567" to S-ice prawdopodobnie znajdzie kilka/kilkanascie/kilkadziesiat/kil... miejsc w pamieci gdzie zawarta jest ta wartosc !!! wiec aby znalezc ta wlasciwa daj jakis "egzotyczny" SERIAL np. taki jaki dalem w przykladzie do komendy <s>.
Teraz za kazdym razem gdy program bedzie chcial pobrac nasza NAZWE odwola sie do komorki pamieci o adresie 0040218E !! Zapisujemy sobie adres tej komorki na kartce: nazwa - 0040218E
4.Teraz naciskamy F5 (aby wyjsc s s-ice i pozwolic programowi aby sie dalej wykonywal), pamietamy ze mamy ustawiony breakpoint na GetDlgItemTextA i to ze program juz wczytal NAZWE. No wiec wychodzimy i ... znowu jestesmy w s-ice, teraz przerwanie spowodowal SERIAL (nie NAZWA). Postepujemy tak samo jak przy NAZWIE -
5. a) F11
b) Przechodzimy (Ctrl+strz. w gore) kilka linii w gore ... znowu te PUSH-e ;) ...
PUSH 0B
PUSH 0040217E
PUSH 000003E9
PUSH DWORD PTR [EBP+08]
CALL USER32!GetDlgItemTextA
sprawdzamy co sie kryje za argumentem (to jest adres) drugiego PUSH-a czyli:
d 0040217E
wow! nasz SERIAL ;)
c) zapisujemy sobie adres SERIALA na kartce argument drugiego PUSH-a (jesli nie pamietasz ktory to komenda <d>): SERIAL - 0040217E
5. No, mamy teraz dwa adresy (zapisane na kartce) gdzie przechowywane sa nasze dane: NAZWA i SERIAL. Ustawmy sobie dwa breakpointy na odwolaniu do pamieci - komenda <bpm> :
bpm 0040218E //nazwa bpm 0040217E //serial
Teraz za kazdym razem gdy program bedzie chcial cos zrobic z naszymi danymi to siegnie do powyzszych adresow, a wtedy s-ice sie pojawi ;). Mozemy juz wylaczyc breakpoint na funkcji GetDlgItemTextA :
bd 0
Nie usuwamy go komenda <bc> bo gdy sie pomylimy podczas sledzenia kodu i bedziemy musieli od nowa wszystko robic to mozemy go wtedy latwo wlaczyc komenda <be>. Zobaczmy teraz jakie mamy breakpointy komenda <bl>:
bl
Powinno nam sie pojawic takie cos:
00) * BPX USER32!GetDlgItemTextA
01) BPMB #0030:0040218E RW DR2
02) BPMB #0030:0040217E RW DR2
Tam gdzie gwiazdka to breakpoint wylaczony (nie usuniety!)
6. Wiemy juz jak mamy ustawione te breakpointy. Teraz pozwalamy programikowi wykonywac sie (Ctrl+d lub F5) Dlugo sie na robi, bo oto znow jestesmy w s-ice. Patrzymy do okna komend (tam gdzie wpisujemy komendy):
Break due to BPMB #0030:0040218E RW DR2
Czyli - patrzymy na kartke z adresami - 0040218E - to przeciez nasza NAZWA ! acha... wiec teraz program pobral nasza NAZWE i bedzie cos z nia robil !
7. Zobaczmy teraz gdzie sie znalezlismy :
0137:00401383 MOV AL,[ESI] (1) <------|-|
0137:00401385 TEST AL,AL (2) | |
0137:00401387 JZ 0040139C (3) | |
0137:00401389 CMP AL,41 (4) | |
0137:0040138B JB 004013AC (5) | |
0137:0040138D CMP AL,5A (6) | |- petle
0137:0040138F JAE 00401394 (7) | |
0137:00401391 INC ESI (8) | |
0137:00401392 JMP 00401383 (9) <------| |
0137:00401394 CALL 004013D2 (10) |
0137:00401399 INC ESI (11) |
0137:00401391 JMP 00401383 (12) <--------|
Liczba przed dwukropkiem (0137 - segment) moze byc inna na twoim komputerze!
Jestesmy w linijce (2), program odwolal sie do naszej NAZWY w linijce ponad ta na ktorej jestesmy, czyli w linii nr. (1). instrukcja (1) "wklada" bajt wskazywany przez [ESI] do nizszej polowki rejestru AX czyli AL. Uwaga! przypomnienie : to co sie znajduje w AL znajduje sie takze w AX czy EAX !!! Zobaczmy wiec co kryje sie pod ESI ?
d esi
spojzcie na okno danych - co widzimy ? - nasza NAZWE ! ... tzn. ze w ESI jest adres naszej NAZWY !
Spojzmy na powyzszy kawalek kodu jeszcze raz tyle ze calosciowo Mozna latwo zobaczyc ze jest to najzwyklejsza petla! Zanim dalej bedziecie czytac sprobojcie sami zorientowac sie co jest robione przy kazdym obiegu petli ??????
........ ........
No wiec wiecie ? ... dla tych co jeszcze nie wiedza : Linijka (2) sprawdza czy w rejestrze AL jest wartosc (w AL jest wartosc kolejnego znaku w naszej NAZWIE - pamietamy ze adres naszej NAZWY jest w ESI - a jak widac przy kazdym obiegu petli wartosc ESI jest inkrementowana ( ESI:=ESI+1 ) czyli co kazdy obieg w AL jest wartosc kolejnego znaku!) wieksza od 0 (od NULL) Jezeli jest NULL (to znaczy koniec stringu -naszej NAZWY) petla jest konczona i wykonywana jest nastepna instrukcja za petla. Koniec petli powoduje skok za petle czyli linijka (3) (JZ-skok jesli zero). No a teraz zobaczmy na instrukcje (4) ... dlaczego rejestr AL jest porownywany z wartoscia 41 ??? heh... zobaczcie na tablice kodow ASCII co to jest 41 ??? ... jest to kod hexadecymalny litery 'A' ! ... czyli kazdy znak jest porownywany z litera 'A'... jezeli znak (wartosc hex) jest mniejszy od 'A' czyli znak jest jakas cyfra albo znaczkiem (nie litera!) programik skacze (5) (JB - skok jesli ponizej) pod adres 004013AC ... zobaczmy co jest pod tym adresem - jest tam kilka PUSH-y i CALL jesli przejdziemy przez ten CALL (F10) to program sie konczy i program wyswietla komunikat ze wprowadzilismy zle haslo !. Jaki z tego wniosek ? - kazdy znak musi byc litera! (no... kazdym znakiem "wiekszym" od 'A' ;) ). No dobra co dalej ? ... patrzymy na linijke (6) ... ooo! znowu AL jest porownywane z jakas wartoscia - 5A ... znowu zerkamy do tablicy ASCII - co to jest 5A ? ... To jest 'Z' ! Co tu jest robione ? - porownywane jest AL z 5A i jesli wieksze to skok (7) (JAE - skok gdy powyzej lub rowne) do 00401394 ... mozemy przyjac ze skok nastapi gdy znak jest mala litera !!! skok jest do linijki (10). Za chwilke przeanalizujemy ten CALL. Ale teraz jeszcze co gdy warunek nie jest spelniony - czyli znak jest duza litera. jezeli nie spelniony to program przechodzi do linijki (8) co tu jest robione ? - ESI jest inkrementowane, czyli powiekszone o 1 (ESI:=ESI+1) tzn. ze ESI pokazuje teraz na kolejny znak w naszej NAZWIE !!!. W linijce (9) nastepuje skok do poczatku petli - i wszystko od nowa ;). A teraz co sie dzieje jesli warunek w (7) jest spelniony (czyli gdy aktualnie analizowany znak w NAZWIE jest mala litera) i nastepuje skok do (10) czyli do CALL-a. Aby to zrozumiec musimy wejsc do wnetrza tej funkcji (wywolanej CALL-em). Naciskamy pare razy F10 az podswietli nam sie linia z CALL-em (10). Teraz naciskamy F8 ... i jestesmy wewnatrz funkcji !
RET //nie wazne
SUB AL,20 (13)
MOV [ESI],AL (14)
RET (15)
Powinienes sie domyslic co tu sie dzieje! ...
no wiec tak : od wartosci rejestru AL odejmowana jest wartosc 20 - linijka (13) po co ??? ... przypuscmy ze aktualnie analizowany znak (w AL) to 'k' (jeden ze znakow w twojej NAZWIE), co sie stanie jesli od 'k' ('k'= 6B) odejmiemy 20 (oczywiscie 20h tzn. 20 hexadecymalnie) - 6B-20=4B a 4B='K' , nastepnie wartosc w AL (linijka (14)) jest podstawiana do [ESI], czyli oryginalna wartosc tam zapisana zostaje zamieniona wartoscia 'K' , a co bylo przedtem w [ESI] ??? ... oczywiscie 'k' ... wiec to co robi ta funkcja to zamienia wszystkie male litery w naszej NAZWIE na duze !!! No i (15) to wyjscie z funkcji i powrot do miejsca z kad zostala wywolana. Po wyjsciu z funkcji jestesmy w linii (11), czyli instrukcja INC ESI - oczywiscie inkrementuje ona ESI (ESI:=ESI+1) teraz ESI pokazuje, na nastepny znak w naszej NAZWIE. no i (12) to skok znowu do poczatku petli. Petla jest dotad powtarzana dopoki ESI nie pokazuje na NULL, wiec jesli wartoscia komorki o adresie ESI jest NULL (koniec stringu - naszej NAZWY) petla zostaje przerwana i przenosimy sie do 0040139C (JZ 0040139C). I tyle tlumaczenia z powodu jednej krotkiej operacji na stringu, tzn. zamienienia kazdej litery w NAZWIE z malej na duza ;).
Uffff ! ale namieszalem ;) ... ale mam nadzieje ze zrozumiales ... jesli nie to sie nie przejmuj tylko przeczytaj powoli jeszcze raz ;). Ta operacja jest czesto wykorzystywana w zabezpieczeniach typu nazwa-kod.
7. No wiec jestesmy teraz przy 0040139C.
Zobaczmy co my tu mamy:
POP ESI
CALL 004013C2
Pierwsza instrukcja sciaga ze stosu jakas wartosc i wklada ja do ESI ... po co? ... zobacz jaka jest instrukcja zaraz przed nasza dlugo analizowana petla powiekszajaca litery - PUSH ESI - wiec teraz jest - POP ESI - wiec sciagamy ze stosu adres naszej NAZWY ! Zaraz potem jest wywolywana funkcja 004013C2 - musimy zagladnac do jej wnetrza :
0137:004013C1 RET //nie wazne
0137:004013C2 XOR EDI,EDI (16)
0137:004013C4 XOR EBX,EBX (17)
0137:004013C6 MOV BL,[ESI] (18) <---|
0137:004013C8 TEST BL,BL (19) |
0137:004013CA JZ 004013D1 (20) |- petla
0137:004013CC ADD EDI,EBX (21) |
0137:004013CE INC ESI (22) |
0137:004013CF JMP 004013C6 (23) <---|
0137:004013D1 RET (24)
hmmm... moze teraz odgadles co robi ta funkcja ;) ??? ... jak nie to ja teraz przeanalizujemy.
Najpierw instrukcje (16) i (17) heh... powinienes wiedziec co one robia ;). XOR jest bardzo czesto stosowana instrukcja przy roznych zabezpieczeniach !!!. Jest to tzw. wykluczajaca sie alternatywa ... nie przerazaj sie nazwa ;) ... o co tu chodzi ? ... to jest tak : porownywane sa bity z pierwszego operandu i drugiego jesli sa przeciwne (1 i 0) to w wyniku bit ma wartosc 1, w przeciwnym przypadku ma 0. Wynik zapisywany jest w operandzie pierwszym ! oto tabelka ktora pamietamy z matmy:
a | b | c
==========
1 | 1 | 0
0 | 1 | 1
1 | 0 | 1
0 | 0 | 0
No dobra ale mogles pomyslec po co sa porownywane te same rejestry ???
Odp. : w wyniku tych dwoch instrukcji zerowane sa rejestry EDI i EBX dlaczego? pomysl ... poniewaz gdy XOR-owane sa dwa te same rejestry, to maja one w sobie wartosci z takim samym rozstawieniem bitow ! wiec nigdy zaden wynik XOR-owania bitow nie bedzie mial wartosci 1 !!! i w rezultacie EDI i EBX zostana wyzerowane !!! Nastepna jest linijka (18), co robi? wklada do BL wartosc wskazywana w ESI ... a co my mamy w ESI ??? pamietasz ze zaraz przed wywolaniem funkcji w ktorej teraz jestesmy program sciagnal ze stosu wartosc i wlozyl ja do ESI ! na a co bylo z kolei na stosie ??? - byl tam wskaznik do stringu z nasza NAZWA !. Z tego wniosek - co robi instrukcja? - bierze pierwszy znak z naszej NAZWY i wklada go do dolnej polowki rejestru EBX czyli BL !. Linijka (19) - Tutaj sprawdzane jest czy w BL jest jakas wartosc rozna od 0 (od NULL), jezeli nie ma to znaczy ze jest to koniec naszej NAZWY (kazdy string konczy sie znakiem NULL !!!) - nastepuje skok (JZ-skok jesli rowne 0) (linijka (20)) do (24), a jest to powrot do miejsca wywolania funkcji (RET). Natomiast gdy wartosc w BL jest rozna od zera to nie nastapiskok ! - zostanie wykonana instrukcja (21), a teraz co ona robi : Jak pamietamy EDI i EBX zostaly wyzerowane ... teraz przy kazdym obiegu petli do BL (dolna czesc EBX) zaladowana zostaje wartosc kolejnego znaku z naszej NAZWY. Zmierzam do tego iz, przy kazdym obiegu petli do wartosci trzymanej w EDI zostaje dodana wartosc kolejnego znaku z naszej NAZWY !!! w linijce (22) inkrementowane jest ESI (wskaznik przestawiany jest na nastepny znak), no i przez linijke (23) petla przebiega znowu ;). Gdy przejdziemy przez cala NAZWE to wykonywana jest instrukcja (24) i powrot do miejsca wywolania funkcji. Co jest efektem calej tej funkcji ??? ... to ze teraz w EDI siedzi suma wszystkich znakow (ich wartosci hexadecymalnych) naszej NAZWY !!!!! SHIT !!! znowu niezle namieszalem ... niestety nie jestem uzdolniony polonistycznie ... SORRY ! ;))
8. Po tej funkcji ktora omowilem powyzej sa te oto instrukcje :
XOR EDI,00005678 (25)
MOV EAX,EDI (26)
No a co jest teraz robione ??? ...
Jak pamietamy w EDI siedzi suma wszystkich znakow naszej NAZWY (juz po powiekszeniu wszystkich liter !!!), teraz ta suma jest XOR-owana z wartoscia 00005678 w linijce (25), nastepnie ta zXOR-owana wartosc idzie do EAX - linijka (26). np. dla mojej NAZWY "tomroy", sa takie wartosci : suma liter ("tomroy" zostal oczywiscie zamieniony na "TOMROY" !!!) to : 000001EA natomiast po zXOR-owaniu z 00005678 w EAX mam cos takiego : 00005792 (u ciebie zobacz w lewym gornym rogu - w oknie rejestrow - na wartosc rejestru EAX (cos takiego - EAX=xxxxxxxx)) teraz zapisz sobie ta wartosc na kartce (ta po zXOR-owaniu) !!!!!
9. Tym sposobem przeszlismy przez czesc programu gdzie odbywa sie manipulacja na NAZWIE (nie na KODZIE) heh... mysleliscie ze to koniec ??? ooo ... to musze was rozczarowac ;))
10.Naciskamy teraz F5 lub Ctrl+d ... znowu pojawil sie s-ice w oknie komend widzimy co spowodowalo przerwanie wykonywania programu :
Break due to BPMB #0030:0040217E RW DR2
Zerknijmy na nasza kartke z adresami : adres 0040217E to adres w pamieci gdzie przechowywany jest nasz SERIAL ! Jak chcesz mozemy sprawdzic:
d 0040217E
Oczywiscie w oknie danych otrzymamy nasz SERIAL ! No dobra przechodzimy teraz do kolejnej analizy kodu .... hmmmm zobaczmy gdzie sie teraz znalezlismy (UWAGA ! musimy sobie uzmyslowic ze jestesmy teraz WEWNATRZ jakiejs funkcji !!! wskazuje na to instrukcja RET kilka/kilkanascie linijek nizej - bedzie nam to potrzebne do pozniejszej analizy !!! - wiec to sobie zapamietaj !!!):
0137:004013E2 MOV AL,0A <----|
0137:004013E4 MOV BL,[ESI] |
0137:004013E6 TEST BL,BL |
0137:004013E8 JZ 004013F5 |
0137:004013EA SUB BL,30 |- petla
0137:004013ED IMUL EDI,EAX |
0137:004013F0 ADD EDI,EBX |
0137:004013F2 INC ESI |
0137:004013F3 JMP 004013E2 <----|
No, a teraz powiem wam tylko ze powyzszy kawalek kodu zamienia nasz SERIAL z liczby dziesietnej np. moj "1234567" na hexadecymalna (szesnastkowa) - w moim przypadku - "0012D687" I wynik umieszcza w EDI ! ale wiecej wam nie powiem i SAMI musicie dojsc do tego jak to jest robione ... nie bede przeciez podawal wam wszystkiego na tacy !!! ;)).
11.To wy sie zastanawiajcie nad powyzszym kodem ... a ja przejde no nastepnych instrukcji :
XOR EDI,00001234 (27)
MOV EBX,EDI (28)
Heh ... co my tu mamy ? ... w linijce (27) mamy XOR-owanie ... ale czego z czym ??? ... tak, tak, zgadles ! ... w EDI mamy przeciez wartosc hexadecymalna naszego SERIALA , a wiec ta wartosc jest XOR-owana z taka smieszna liczba jak 00001234, i wynik umieszczany jest z powrotem w EDI !!! No a w linijce (28) widzimy dobrze nam znana instrukcje MOV, co ona robi ? ... jak sie juz pewnie domysliles przenosi (kopiuje) nasz zhexadecymalizowany (ze tak powiem ;) ) i zXOR-owany SERIAL do rejestru EBX !!! Dla mojego SERIALA ("1234567") aktualna wartosc w rejestrze EBX (i EDI) to 012C4B3 !!!
12.przechodzimy pare linijek dalej (F10) (UWAGA! przeszlismy przez instrukcje RET tzn. ze wyszlismy wlasnie z funkcji !!!) az do ujzenia ponizszego kodu :
POP EAX (29)
CMP EAX,EBX (30)
JZ 0040124C (31)
CALL 00401362 (32)
Eh ... tym razem nie zostawie was na lodzie i razem przeanalizujemy powyzszy kod ;). No wiec tak ... w linijce (29) jest instrukcja sciagajaca COS ze stosu i wkladajaca TO do EAX ... no tak, ale co my mamy na stosie ??? odp. i teraz wlasnie ma znaczenie to ze bylismy przed chwila w funkcji - nie widzielismy tam zadnych PUSH-y !!! ... Zobaczmy na chwile na linijki powyzej linii (29) :
PUSH EAX (a1)
PUSH 0040217E (a2)
CALL 004013D8 (a3)
ADD ESP,04 (a4)
Od razu powiem ze instrukcja (a3) to wlasnie wywolanie tej funkcji o ktorej ostatnio tak duzo mowie !!! w linijce (a1), to co jest w rejestrze EAX jest wkladane na stos ... a teraz wazne pytanie co my do cholery mamy w tym EAX ??? Odp. pamietasz jak kazalem Ci zapisac na kartce wynik kilku operacji na naszej NAZWIE (np. dla mnie to bylo : 00005792) ??? to wlasnie siedzi w EAX (przed wywolaniem funkcji (a3)) ... heh ... a wiec wartosc EAX nie zmienila sie od czasu ostatniej operacji na NAZWIE ! Bo chyba widzisz ze w tej funkcji ktora kazalem Ci samemu przeanalizowac jest jakas operacja zwiazana z EAX. Ta funkcja to oczywiscie (a3) (jej adres to argument CALL-a). A wiec wiemy ze w linijce (a1) na stos jest kladziony wynik operacji na naszej nazwie (00005792) !!! a teraz co w linijce (a2) ??? - no pewnie wiesz czego to jest adres (argument PUSH-a)? jesli jeszcze nie to zobacz na kartke ... albo sprawdz :
d ds:0040217E
(DS-data segment) nie musielismy pisac "ds:" ale dla pewnosci lepiej napisac ;). Heh ... no i co wam wyszlo ... oczywiscie nasz SERIAL ! Jak teraz wyglada stos ??
|--------------------------|
| nasz SERIAL | //1234567
|--------------------------|
| wynik operacji na NAZWIE | //00005792
|--------------------------|
Z tego wynika ze na samej gorze stosu znajduje sie nasz SERIAL !!! No potem wywolanie funkcji (a3) (ta ktora kazalem Ci przeanalizowac) - to juz znamy ;). A teraz co jest robione w (a4) ??? Chyba wiemy ze ESP (stack pointer) to wskaznik stosu ! przed instrukcja (a4) ESP pokazuje na poczatek stosu (nasz SERIAL) ale po dodaniu do niego 04, na co pokazuje ??? ... oczywiscie na wynik operacji na NAZWIE (dla mnie - 00005792) !!! OK. rozumiesz ? ... to przechodzimy dalej.
Przechodzimy z powrotem do analizy linijek (29)(30)(31)i(32) : w linijce (29) jest sciagana wartosc ze stosu i wkladana do EAX... teraz juz wiemy co sciagamy - jest to koncowy wynik operacji na NAZWIE (00005792) bo przeciez program przestawil ESP !!! Natomiast linijka (30) to jakies porownanie ... ale czego z czym ? ... juz wiemy co mamy w EAX ... a co w EBX ??? - Zobacz na linijke (28) !!! A wiec w EBX mamy koncowy wynik wszystkich operacji na SERIALU ! ... podsumujmy - W rejestrze EAX mamy koncowy wynik wszystkich operacji na NAZWIE, a w EBX mamy tez koncowy wynik tyle ze na SERIALU, te dwie wartosci sa porownywane (30) i jesli sa rowne to nastepuje skok (31) (JZ-skok jesli zero, instrukcja CMP ustawia flage (bit - 1) Z, jesli argumenty sa rowne!) w przeciwnym wypadku wykonywany jest CALL (32) ... powiedzmy najpierw co robi ta funkcja kryjaca sie pod CALL-em - wypisuje ona informacje ze wprowadziles zly SERIAL !!! hmmm ... zastanowmy sie jak obejsc ten CALL .... hehe to proste wystarczy zamienic JZ 0040124C na JNZ 0040124C albo cos podobnego ... ale nie o to tu chodzi, poniewaz autor zastrzegl ze patche nie sa dozwolone !!! wiec musimy odgadnac prawidlowy SERIAL na podstawie naszej NAZWY, nie mozemy zmieniac kodu programu ;).
Jezeli EBX jest rowne EAX to wprowadzilismy poprawny kod !!! Tzn. ze jezeli nasz SERIAL po zamienieniu go na liczbe haxadecymalna i zXOR-owaniu go z 00001234 - jest rowny - sumie wszystkich liter naszej NAZWY po przeksztalceniu kazdej litery z malej na wielka, a potem zXOR-owaniu tej sumy z liczba 00005678 - to wprowadzilismy poprawny SERIAL !!!!!! hmmm.... to zastanowmy sie taraz jak wyliczyc ten prawidlowy SERIAL - do tego musze przypomniec jak dziala XOR : UWAGA! teraz uwazac ... XOR jest czesto stosowany przez programistow do oprogramowania zabezpieczen w programach, poniewaz cechuje go tzw. odwrotnosc tzn. jezeli :
a XOR b = c np. a= 10011101 b= 10110000 -------- c= 00101101
to wedlug tabeli prawdy (podalem ja wczesniej) :
c XOR b = a c= 00101101 b= 10110000 -------- a= 10011101 c XOR a = b c= 00101101 a= 10011101 -------- b= 10110000
Jest to BARDZO wazna zaleznosc !!!
13.Teraz juz latwo wykalkulujemy nasz (prawidlowy) SERIAL ! Pomysl ! co musisz teraz zrobic aby otrzymac ten upragniony SERIAL ??? ... heh ... prawidlowym SERIALEM jest ten ktory po zamienieniu na liczbe hexadecymalna i zXOR-owaniu jej z 00001234 da nam wynik koncowej operacji na naszej NAZWIE (dla mnie - dla "tomroy" - 00005792) !!! czyli pamietajac ze instrukcje XOR cechuje odwrotnosc, SERIAL mozemy obliczyc tak: hmmm...jak by to wyjasnic - Poprostu Musimy !Odwrocic operacje ktore zostaly wykonane na naszym SERIALU, (tylko teraz SERIALEM prawidlowym jest 00005792 - a dokladniej mowiac jest to jakby wartosc prawidlowego SERIALA po wykonaniu na nim operacji przeksztalcenia na liczbe HEXadecymalna i zXORowaniu z 00001234) czyli najpierw odXOR-owac (wartosc 00005792) z 00001234, otrzymamy hexadecymalna reprezentacje prawidlowego kodu, a potem zwyczajnie zamienic wynik ktory otrzymalismy na jego decymalna (dziesietna) reprezentacje !
XOR-ujemy otrzymana wartosc operacji na NAZWIE (00005792) (EAX) z wartoscia ktora zostala zXOR-owana z hexadecymalna reprezentacja naszego SERIALA, czyli 00001234 !!! W S-ice robimy to tak :
? 00005792 ^ 00001234
Znaczek ^ oznacza XOR-owanie. ? - ta komenda jest uzywana do roznych celow (jest dosc przydatna) Oczywiscie zamiast mojej wartosci - 00005792 wpisz swoja (Ta co Ci kazalem bezwzglednie zapisac na kartce, nawet ujalem ja w ramke, ale praktycznie to mozesz ja teraz zobaczyc w oknie rejestrow pod rejestrem EAX=xxxxxxxx) hmmm... co otrzymalismy (w oknie komend) ??? - takie cos :
000045A6 0000017830 "E~"
HEX DEC ASCII
Ale co nam to dalo ? ... Tam gdzie jest pod spodem HEX, to jest prawidlowy SERIAL w postaci hexadecymalnej !!! teraz powinnismy zamienic ta wartosc na dziesietna ale ... S-ice zrobil juz to za nas !!! i tam gdzie mamy pod spodem DEC to jest POPRAWNY SERIAL - Dla mnie :
|------------|
| 0000017830 |
|------------|
HURRA !!! ... mamy nasz upragniony SERIAL, teraz mozemy opuscic zera (te 5 na poczatku), zapisac go na kartce, wyjsc z S-ice (przedtem wylaczyc wszystkie Breakpointy), i wpisac do pola z NAZWA - nasza NAZWE no i nasz SERIAL wykalkulowany na podstawie NAZWY, czyli dla mnie :
tomroy i 17830
Klikamy OK ... i otrzymamy komunikat "Great work mate!" (Swietna robota !!!) ;)) .... hehe tzn. ze wreszcie dobrnelismy do konca ;))))))))) Mozecie taraz isc na Pivo i oblac swoj pierwszy zlamany program ;)).
Podsumowanie :
hmmm ... jakby sie teraz na to wszystko papatrzec to wyglada to strasznie skomplikowanie - ale to jest naprawde bardzo proste ... zlamanie tego programiku zajelo mi okolo 15-20 min. - Wam jak macie opis to wystarczy 3-4 min ;). Co? ... Jeszcze nie jestescie przekonani, ze to bardzo proste ??? to zrobmy krotkie podsumowanie z planem :
I co? ... prawda ze proste ;))
Jesli jeszcze czegos nie kapujesz to znasz moj adres ... mozesz zapytac ;).
Teraz sprobuj sciagnac CrackMe v2.0 (na stronie MiB-a) i sproboj zarejestrowac, ma tez bardzo proste zabezpieczenie ... tyle ze nie nazwa/kod ale sam numer rejestracyjny, mam nadzieje ze nie bedziesz mial zbyt duzo klopotu z tamtym zabezpieczeniem ;))
POWODZENIA !!!
Linki (UWAGA! wszystkie strony po angielsku!) :