Faq crackerskie #2 by mNICH

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 :

  1. Uruchamimy Crackme wpisujemy NAZWE i SERIAL
  2. Wchodzimy do S-ice (Ctrl+d)
  3. Ustawiamy breakpoint (bpx) na GetDlgItemTextA
  4. Wychodzimy z S-ice ... OK ... i znowu jestesmy w S-ice
  5. Ustawiamy breakpointy (bpm) na 0040218E i 0040217E
  6. Naciskamy F5 ... ogladamy kod ... acha, program zamienia kazda mala litere w naszej NAZWIE na duza.
  7. Potem widzimy ze program sumuje kazda litere z naszej NAZWY (jej hexadecymalny kod i to gdy jest juz duza litera!!!)
  8. Ta suma jest XOR-owana z 00005678
  9. Wynik tego XOR-owania idzie do EAX - ale my i tak zapisujemy ta liczbe na kartce !
  10. Potem nasz SERIAL jest zamieniany na liczbe Haxadecymalna
  11. Ten hexadecymalny SERIAL jest XOR-owany z 00001234
  12. Wynik XOR-owania SERIALA idzie do EBX
  13. Te dwa wyniki (EAX i EBX) sa porownywane, jesli pasuja to zanczy ze nasz SERIAL byl prawidlowy
  14. Korzystajac z wlasnosci instrukcji XOR, odXOR-owujemy to co jest w EAX z 00001234 i otrzymujemy szesnastkowa reprezentacje poprawnego SERIALA, od razu S-ice pokazuje nam ta wartosc jako dziesietna i ... otrzymujemy wlasciwy SERIAL !!!

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!) :