a85k CrackMe 001

Data

by [ active85k ]

 

14/09/2002

UIC's Home Page

Published by Quequero


Sapete che cosa ci fa un passerotto di 350 kg sul ramo di un albero?

Mmmm il tute e' completo nonche' bello...Pero' l'hai fatto tu il crackme, mi sa che non vale :P sei come Vixie che prima scrive il crond e poi rilascia gli exploit per il suo crond hahahah :PPPP

Un passerotto di 350 kg sul ramo di un albero fa: CIIIIIIIIIIIIIIIIIIIIIIP!

:)

E-mail: [email protected]

:)

Difficoltà

( )NewBies (X)Intermedio ( )Avanzato ( )Master

 

Pronti per reversare il mio piccolo e primo CrackMe! Non ne avevo mai scritti prima d'ora! :)


a85k CrackMe 001
Written by [ active85k ]
 
Il Crackme

Introduzione

Innanzitutto un saluto a tutta la U.I.C. Poi un saluto particolare ad AndreaGeddon poerché è grazie a lui se oggi sto scrivendo questo tutorial. Inizio col dire che è la prima volta che mi metto a scrivere uno di questi noiosissimi CrackMe con tutorial, quindi, se trovate qualche bug, abbiate pietà :)

Tools usati

IDA Disassembler v4.17 (ma va bene qualsiasi versione)

SoftICE v4.0 (ma va bene qualsiasi versione)

masm32 v7.0 (aaah! ma va bene qualsiasi versione anche di questo! :)

un cervello funzionale v5.0 (ma allora è un vizio! :-)

URL o FTP del programma

U.I.C. web site

Notizie sul programma

E' uno dei tanti CrackMe che si trovano in giro. Richiede un nome utente e un seriale da inserire. In questo tutorial spiegherò come fare per scrivere un generatore di chiavi per questo programmino. Cercherò di essere quanto più chiaro possibile, oltre che per la spiegazione del funzionamento del proggy, anche per quanto riguarda l'utilizzo dei programmi sopra elencati. 

Essay

 

Bene, al lavoro! Iniziamo con l'avvio del nostro CrackMe per vedere di cosa si tratta e che effetto ci fa! Direi che sembra 'na cosa ben fatta. Ci sono due campi, uno per inserire il nostro nome, l'altro per inserire il nostro seriale. Iniziamo così a riflettere: è facilmente intuibile, soprattutto dalle dimensioni dell'eseguibile, che il programma è scritto in assembly, quindi, da adesso in avanti dobbiamo pensare in assembly.

Inizio ad inserire nel primo campo, un nome a caso (io ci inserisco "mattia") per vedere che cosa succede. Premo "Check!" e giustamente il programma bestemmia. La stessa cosa succede se inserisco un seriale di mia invenzione. Dobbiamo quindi arrivare al punto in cui il programma ritira il nome da noi immesso. Ci sono vari modi per ritirare il nome da un campo in assembly:

    1. con la GetWindowTextA

    2. con la GetDlgItemTextA (se si tratta di un proggy che prende la dialogbox dalle risorse)

    3. usando il messaggio WM_GETTEXT con la SendMessageA o la SendDlgItemMessageA

 

Dobbiamo quindi andare a tentativi, quindi, entriamo in SoftICE (Ctrl+D) e mettiamo un breakpoint sulla prima chiamata digitando, nella riga di comando, "bpx GetWindowTextA". Premiamo di nuovo Ctrl+D per tornare in Windows. Bene, non ci resta che provare. Mettiamo di nuovo un nome, (io ci metto di nuovo "mattia") e un serial a casaccio (io ci metto "12345678"). Premiamo su "Check!" ED ECCO... che non succede proprio nulla! Il programma, imperterrito, ci manda nuovamente a quel paese! Quindi ritorniamo nuovamente in SoftICE, eliminiamo il breakpoint che abbiamo messo ("bc *") e proviamo a breakare sulla seconda chiamata ("bpx GetDlgItemTextA"). Ritorniamo nuovamente sul proggy e ripetiamo la storia: nome="mattia", serial="12345678", premiamo su "Check!". A questo punto credo di dover specificare che, in questo caso, SoftICE popperà solo se il nome è almeno di 10 caratteri, ma ce ne accorgeremo anche dopo. Allora mettiamo un nome di almeno 10 caratteri e ripremiamo su "Check!" Ecco che compare SoftICE che ci riempie di gioia! :) Quindi premiamo F11 per visualizzare la porocedura che ha eseguito il programma. Ecco, ci troviamo esattamente dopo una chiamata a GetDlgItemTextA, quindi, tra i parametri pushati dobbiamo risalire a quello che contiene il nome da noi inserito per vedere che cosa ha combinato. La GetDlgItemText ha la seguente sintassi:

 

    UINT GetDlgItemText(
            HWND hDlg,        //  Handle identificativo della finestra
            int nIDDlgItem,   //  Numero identificativo del campo contenente il testo
            LPTSTR lpString,  //  Indirizzo della stringa in cui verrà messo il testo
            int nMaxCount     //  Numero massimo dei caratteri che verranno catturati
    );

 

Una volta analizzata la sintassi di questa call, andiamo ad analizzare il codice assembly eseguito dal programma: ricapitolando, inseriamo come nome "mattia mattia" (13 caratteri > 10), come serial "12345678" e premiamo il tasto "Check". Comparso SoftICE, premiamo F11 per la procedura principale. Ci troviamo, quindi, in questo punto di codice, che analizzeremo seguendo, quindi, il modello LIFO:

 

      ...

    0177:004011AE    6A1E            PUSH 1E                        // nMaxCount

    0177:004011B0    68E6314000      PUSH 004031E6                  // lpString

    0177:004011B5    68BA0B0000      PUSH 00000BBA                  // nIDDlgItem

    0177:004011BA    FF7508          PUSH DWORD PTR [EBP+08]        // hDlg

    0177:004011BD    E8D2000000      CALL USER32!GetDlgItemTextA    // Chiamata

    ...

 

Questa è la parte di codice che, almeno in teoria, dovrebbe ritirare un testo di, al max 1Eh (= 30) caratteri e mettere il testo ritirato in 004031E6. Quindi, portiamo avanti la nostra curiosità e vediamo che diavolo ha ritirato. Rientriamo in SoftICE e breakiamo sulla procedura. Premiamo F11 e digitiamo "d 004031E6". Una volta premuto invio, in basso a destra, vediamo cosa contiene la locazione 004031E6. Osserviamo e vediamo che prima c'è il nostro seriale, successivamente seguito da un altro numero... e che numero è? Così, su due piedi, sembrerebbe... un seriale! Incuriositi, prendiamo carta e penna, ricopiamo lo strano seriale e inseriamolo nel nostro programmino lasciando sempre il nome "mattia mattia". Premiamo "Check!" e... Dio santo! Il seriale è giusto! Ma vieni!!! Ma vai!!! EVVIVAAAA!! Aspettate un attimo! Noi non volevamo fare questo! Fate attenzione: la chiamata non ha ritirato il nome per generare un seriale valido, ma tutt'altra cosa! Ha ritirato il nostro seriale, e la locazione conteneva già il giusto seriale generato dal proggy! Quindi questo significa che con la GetDlgItemTextA siamo andati più avanti di quanto speravamo! Il nome non lo abbiamo mai trovato! Questo significa che non è la chiamata giusta su cui breakare! Azz, 'sto reversing è veramente 'na palla al piede! Cmq non disperate. Tra le chiamate di prima, ne abbiamo saltata una: la SendMessage. E' la nostra ultima speranza! Entriamo in SoftICE, mettiamo l'ennesimo breakpoint su SendMessageA "bpx SendMessageA", usciamo da SoftICE, mettiamo il nome "mattia mattia", il serial "12345678" e premiamo di nuovo "Check!" (azz, 'sto pulsante si sta consumando a forza di premerlo! :). Ecco qua! SoftICE poppa, trionfante più che mai! Chissà se stavolta abbiamo fatto bene! Premiamo F11 e scorriamo la schermata più sopra di qualche riga. Dobbiamo quindi analizzare la SendDlgItemMessage e il messaggio WM_GETTEXT, che serve per ritirare il testo di un campo. Ecco tutte le sintassi di queste API:

 

        LONG SendDlgItemMessage(
                HWND hDlg,          // Handle identificativo della finestra
                int nIDDlgItem,     // Campo a cui mandare il messaggio
                UINT Msg,           // Messaggio da mandare
                WPARAM wParam,      // Primo parametro richiesto dal messaggio
                LPARAM lParam       // Secondo parametro richiesto dal messaggio
        );

 

Se andiamo a guardare il codice, il messaggio che è stato mandato è il parametro centrare fra i cinque. In questo caso, come vediamo sotto, è stato pushato 0Dh. Ebbene, questo parametro corrisponde al WM_GETTEXT. Abbiamo fatto centro! Come potete notare, ogni messaggio richiede due parametri. Quindi, dobbiamo andare a vedere quali parametri richiede il WM_GETTEXT:

 

      WM_GETTEXT
    wParam = (WPARAM) cchTextMax;   // Numero, al max, di caratteri da catturare
    lParam = (LPARAM) lpszText;     // Indirizzo nel quale verrà inserito il testo catturato

 

Bene, adesso abbiamo tutto quello che ci serve per capire il codice assembly di questa diamine di procedura.

 

      ...

    0177:004010E0    68C8314000        PUSH 004031C8                     // Parametro2

    0177:004010E5    6A1E              PUSH 1E                           // Parametro1

    0177:004010E7    6A0D              PUSH 0D                           // WM_GETTEXT ;)

    0177:004010E9    68B90B0000        PUSH 00000BB9                     // nIDDlgItem

    0177:004010EE    FF7508            PUSH DWORD PTR [EBP+08]           // hDlg

    0177:004010F1    E8B0010000        CALL USER32!SendDlgItemMessageA   // Chiamata

    ...

 

Vediamo se abbiamo capito bene: questa chiamata manda il WM_GETTEXT ad un campo contenente un testo. Di questo testo verranno catturati i primi 1Eh (= 30) caratteri, e il testo catturato verrà messo in 004031C8. Ecco, adesso dobbiamo nuovamente andare a vedere che diavolo ha messo in questa locazione. Entriamo in SoftICE, breakiamo, premiamo F11 e digitiamo "d 004031C8". Perfetto! In basso a destra vediamo che la locazione contiene il nostro nome! (nel mio caso "mattia mattia"). In poche parole, tutta 'sta storia serviva solo per capire da che punto dobbiamo incominciare a mettere in funzione le nostre amate cervella! =) Carta e penna (oppure il notepad.exe come faccio io) e scriviamoci l'indirizzo da cui parte il tutto (cioè dal primo parametro pushato = 0177:004010E0). Dell'indirizzo, serve solo la seconda parte, quindi scriviamo solo "004010E0". Adesso è arrivato il turno del nostro vecchio e caro disassembler! Io ho deciso di usare IDA perché... è più difficile? Bah, diciamo di si: ho scelto IDA perché è più difficile! Ma nooooo! Perché con IDA abbiamo la possibilità di rinominare a piacimento le nostre variabili! ;)

 

Molto bene, per ora, ci stiamo dentro! Adesso impareremo ad utilizzare IDA o almeno le basi ;). Apriamo il disassembler e loadiamo il nostro eseguibile. All'inizio ci verrà chiesto di che tipo di eseguibile si tratta, e dovrete selezionare "Portable Executable for IBM PC" (che poi è sià selezionato ;) e premere OK per avviare la procedura di disassembly. Attendete 1 millesimo di secondo, fino a quando, nel pannello in basso, non comparirà la riga "The initial autoanalysis is finished.". A questo punto il proggy è stato disassemblato. Adesso dobbiamo arrivare al punto in cui inizia la procedura di calcolo del seriale. In IDA, quindi, premiamo il tasto "G" (Go to) e, nel pannello che comparirà, dovete inserire l'indirizzo che avete precedentemente scritto (004010E0). Premete OK e cavalcate! Ci troviamo proprio in groppa all'algoritmo! Adesso dovete chiudere Winamp perché la musica ci disturba in quanto siamo arrivati alla parte cruciale della cosa! Vi riporterò sotto il codice assembly di IDA e vi spiegherò, riga dopo riga, tutte le operazioni che vengono fatte dal proggy per calcolare il seriale. Le mie spiegazioni saranno le scritte colorate e, in base al colore, si tratterà di micro-funzioni differenti. Fate molta attenzione perché, dopo questa spiegazione, scriveremo il KeyGen secondo le informazioni ricavate da tutta la pappa! :)

 

               push offset Nome ; lParam  Ho rinominato la variabile chiamandola Nome
               push 1Eh ; wParam          per rendere più comprensibile la lettura del
               push 0Dh ; Msg             listato.
               push 0BB9h ; nIDDlgItem
               push [ebp+hDlg] ; hDlg
               call SendDlgItemMessageA  Questa è la chiamata che prende il nome
               cmp eax, 0Ah              Se il nome è lungo almeno 10 caratteri bene,
         +---- jnb short loc_0_40111E    altrimenti il proggy vi manda a farvi... 'na pizza :)
         |     push 10h ; uType
         |     push offset aA85kCrackme ; lpCaption
         |     push offset aInvalidNameOrS ; lpText
         |     push [ebp+hDlg] ; hWnd
         |     call MessageBoxA    Questa chiamata ci manda a farci fottere
         |     push dword_0_4031C4 ; hWnd
         |     call SetFocus
         |     leave
         |     retn 10h
         |
         +---> loc_0_40111E:
               xor ecx, ecx                  Azzera ECX per usarlo come contatore

 
      +------> loc_0_401120:
      |        mov al, byte ptr Nome[ecx]    Copia in AL l'ECXesimo carattere del Nome
      |        cmp al, 0                     Se il nome è finito
      |  +---- jz short loc_0_401139         Allora passa avanti
      |  |     cmp al, 5Ah            Se il carattere del nome è minuscolo
      |  | +-- jbe short loc_0_401130 (altrimenti passa avanti) <-------------------+
      |  | |   sub al, 20h            Allora lo fa diventare maiuscolo (char -20h) -+
      |  | |  
      |  | +-> loc_0_401130: ; CODE XREF: sub_0_401061+CB j
      |  |     mov byte ptr Nome[ecx], al   Sostituisce il char minuscolo con quello maiuscolo
      |  |     inc ecx                      Incrementa di 1 il contatore per il prossimo char
      +--+---- jmp short loc_0_401120       Torna indietro per analizzare il prossimo char.
         |    
         |     [ in poche parole la funzione di sopra non fa altro che trasformare ]

         |     [ il nome da minuscolo in maiuscolo. Quando la riscriveremo, la fa- ]

         |     [ remo molto più comprensibile di questa qua ;)                     ]

         |

         +---> loc_0_401139: ; CODE XREF: sub_0_401061+C7 j

               xor ecx, ecx    Azzera ECX
               xor ebx, ebx    Azzera EBX
 
    +--------> loc_0_40113D: ; CODE XREF: sub_0_401061+10B j
    |          mov al, byte ptr Nome[ecx]  Mette in AL l'ECXesimo carattere del Nome maiuscolo
    |          cmp cl, 4                   Se siamo arrivati al quinto carattere del nome
    |    +---- jz short loc_0_40116E       allora passiamo avanti altrimenti continua :)
    |    |
    |    |     loc_0_401148: ; CODE XREF: sub_0_401061+F0 j
    |    |     cmp al, byte ptr a1qaz2wsx3edc4r[ebx]    Questa funzione compara il nostro char
    |    | +-- jz short loc_0_401153    con i chars presenti in una stringa che contiene tutti
    |    | |   inc ebx        i chars che possiamo usare. Quando lo trova, salta alla funzione
    |    | +-- jmp short loc_0_401148      successiva. Ad es: se il nostro char corrisponde al
    |    | |                               sesto char di questa stringa, EBX diventa ugualea 6.

    |    | |           Per visualizzare l'intero contenuto della str, dovete andarci sopra col

    |    | |           cursore (io ho colorato la stringa di bianco) e premere INVIO. Poi, una

    |    | |           volta copiato il contenuto, premete ESC per ritornare alla procedura.
    |    | |
    |    | |   loc_0_401153: ; CODE XREF: sub_0_401061+ED j
    |    | +-> jmp short loc_0_401158
    |    | |    
    |    | |    
    |    | |   loc_0_401155: ; CODE XREF: sub_0_401061+FA j
    |    | |   sub ebx, 9 <---------------------------------------+   Sottrae 9 ad EBX
    |    | |                                                      |
    |    | +-> loc_0_401158: ; CODE XREF: sub_0_401061+F2 j       |
    |    |     cmp ebx, 9             Se EBX è più grande di 9    |  
    |    |     ja short loc_0_401155  allora salta a -------------+
    |    |     mov dl, byte ptr a0123456789[ebx]  Copia in DL l'EBXesimo carattere di una str
    |    |     mov byte ptr unk_0_403204[ecx], dl piena di numeri e lo mette nel serial corrett
    |    |     inc ecx                            Incrementa di 1 ECX (contatore)
    |    |     xor ebx, ebx                       Riazzera EBX
    +----+---- jmp short loc_0_40113D             Salta indietro per ripetere l'operazione con

         |                                        il carattere successivo
         |     
         +---> loc_0_40116E: ; CODE XREF: sub_0_401061+E5 j 

 

   [ A questo punto abbiamo ECX = 4, cioè rappresenta il 5° carattere del serial corretto. ]
   [ 2Dh = "-") per formare un serial di
questo tipo: ####- (# = numero).                  ]

 

               mov byte ptr unk_0_403204[ecx], 2Dh  Mette un trattino al 5° char del Serial
               inc ecx                   Incrementa di 1 ECX
               mov esi, offset Nome      ESI diventa puntatore al nome
 
  +----------> loc_0_40117B: ; CODE XREF: sub_0_401061+13B j
  |            cmp ecx, 0Eh              Se ECX non è uguale a 0Eh (= 14)
  |       +--- jnz short loc_0_401182    allora salta in loc_0_401182
  | +-----+--- jz short loc_0_40119E     altrimenti salta in loc_0_40119E
  | |     |
  | |     +--> loc_0_401182: ; CODE XREF: sub_0_401061+11D j
  | |          mov al, [esi]    Muove in AL l'ESIesimo carattere del nome
  | |          xor al, cl       xora il carattere con la sua posizione all'interno del nome
  | |          cmp al, 5Ah              Se il char xorato è minore o uguale a 5Ah
  | |     +--- jbe short loc_0_40118E   Allora salta, altrimenti...
  | |     |    sub al, 8                ...sottrae 8 al carattere
  | | +---+--- jmp short loc_0_401194
  | | |   |
  | | |   |
  | | |   +--> loc_0_40118E: ; CODE XREF: sub_0_401061+127 j
  | | |        cmp al, 30h            Se AL è maggiore di 30h
  | | +------- jnb short loc_0_401194 allora salta,
  | | |        add al, 0Ah            altrimenti, aggiunge 0Ah (= 10( al carattere
  | | |
  | | +------> loc_0_401194: ; CODE XREF: sub_0_401061+12B j
  | |          ; sub_0_401061+12F j
  | |          mov byte ptr unk_0_403204[ecx], al Muove AL nell'ECXesima posizione del serial
  | |          inc esi                            Incrementa ESI di 1 (passa al char success)
  | |          inc ecx                            Incrementa ECX di 1 (char success del serial)
  +-+--------- jmp short loc_0_40117B             Ritorna per analizzare il prossimo char.
    |
    |
    +--------> loc_0_40119E: ; CODE XREF: sub_0_401061+11F j
               add cl, 20h                         Aggiunge 20h a CL
               mov byte ptr unk_0_403204[ecx], cl  Mette CL nel Serial da lui generato
               mov byte ptr unk_0_403205[ecx], 0   Pone fine al suo serial mettendo uno zero.
              

        [ In pratica, nella nostra attuale situazione, potremmo anche breakare su 0040119E  ]

    [ e scendere tre righe più sotto per sniffare il seriale valido con il comando "D", ]

    [ ma noi dobbiamo diventare dei reverser! Quindi dobbiamo scrivere il KeyGen.       ]

    [ Non commenterò il codice qua sotto perché è inutile. Vi farò capire meglio più    ]

    [ giù. Questo codice guardatelo solo per avere una vaga idea di quello che fa :)    ]

 

               push 1Eh ; nMaxCount
               push offset unk_0_4031E6 ; lpString
               push 0BBAh ; nIDDlgItem
               push [ebp+hDlg] ; hDlg
               call GetDlgItemTextA    Questa è la chiamata su cui avevamo breakato per sbaglio

                                       la prima volta :)
               cmp eax, 0Eh
               jb short loc_0_4011CC
               cmp eax, 0Eh
               jbe short loc_0_4011E2

               loc_0_4011CC: ; CODE XREF: sub_0_401061+164 j
               push 10h ; uType
               push offset aA85kCrackme ; lpCaption
               push offset aInvalidNameOrS ; lpText
               push [ebp+hDlg] ; hWnd
               call MessageBoxA
               jmp short locret_0_401230
 

               loc_0_4011E2: ; CODE XREF: sub_0_401061+169 j
               mov esi, offset unk_0_403204
               mov edi, offset unk_0_4031E6
               push offset unk_0_4031E6 ; lpString2
               push offset unk_0_403204 ; lpString1
               call lstrcmpA
               or eax, eax
               jnz short loc_0_40121C
               push 40h ; uType
               push offset aA85kCrackme ; lpCaption
               push offset aGoodWorkYourSe ; lpText
               push [ebp+hDlg] ; hWnd
               call MessageBoxA
               push 0 ; nExitCode
               call PostQuitMessage
               jmp short locret_0_401230


               loc_0_40121C: ; CODE XREF: sub_0_401061+19C j
               push 10h ; uType
               push offset aA85kCrackme ; lpCaption
               push offset aInvalidNameOrS ; lpText
               push [ebp+hDlg] ; hWnd
               call MessageBoxA
 
               locret_0_401230: ; CODE XREF: sub_0_401061+17F j
               ; sub_0_401061+1B9 j
               leave
               retn 10h

 

Fffiù! Ragazzi che sudata! ma finalmente abbiamo finito! e naturalmente sto scherzando, perché c'è ancora da scrivere il generatore di chiavi! Dio santo, non volete proprio fare niente, eh? :). Dopo che il proggy ha messo uno zero alla fine del suo serial, possiamo facilmente capire che, finalmente lo ha generato tutto! Quindi, in teoria, il tutorial potrebbe finire qua... ma in questo modo dove sarebbe la bravura e la costanza del nostro bravo scrittore??? ;) Le righe di codice che non ho commentato servono solo per comparare il nostro seriale con quello generato dal programma. Subito dopo, il proggy deve vedere se mandarci a cagare oppure congratularsi con noi!. I due seriali vengono comparati con una lstrcmp: se sono uguali, allora il CrackMe si congratula con noi e poi si chiude per evitare di insultarci alla chiusura manuale del programma, atrimenti ci manda a cagare con una messagebox.

 

Adesso è arrivato il momento del Coding! Vi scrivo sotto il codice del generatore di chiavi per questo crackme. Logicamente questa volta non commento per due motivi: 1) perché ho già commentato sopra, 2) perché quello scritto da me è molto più comprensibile del listato di IDA ;). Vi riporto sotto il codice dell'intero KeyGen in masm32. Se lo prendete e lo compilate, funziona alla grande! ;)

 

    .386
    .model flat, stdcall
    option casemap: none
    include \masm32\include\windows.inc
    include \masm32\include\kernel32.inc
    include \masm32\include\user32.inc
    includelib \masm32\lib\kernel32.lib
    includelib \masm32\lib\user32.lib

    WinMain PROTO :DWORD, :DWORD, :DWORD, :DWORD

    .data
        AppName db "CrackMe 001 - KeyGen", 0
        ClassName db "MainWindowClass", 0
        EditClassName db "EDIT", 0
        ButtonClassName db "BUTTON", 0
        Button1Caption db "Create Key", 0
        Button2Caption db "Exit", 0
        Corto db "Il nome deve essere almeno di 10 caratteri!", 0
        Numb db "0123456789", 0
        Chars db "1QAZ2WSX3EDC4RFV5TGB6YHN7UJM8IK9OL0P ", 0

    .data?
        hInstance HINSTANCE ?
        Edit1 HWND ?
        Edit2 HWND ?
        Button1 HWND ?
        Button2 HWND ?
        UserName db 30 dup (?)
        MySerial db 30 dup (?)

    .code
    Start:
        invoke GetModuleHandle, NULL
        mov hInstance,eax
        invoke WinMain, hInstance,NULL,NULL, SW_SHOWDEFAULT
        invoke ExitProcess,eax

    WinMain PROC hInst: HINSTANCE, hPrevInst: HINSTANCE, CmdLine: LPSTR, CmdShow: DWORD
        LOCAL wc:WNDCLASSEX
        LOCAL msg:MSG
        LOCAL hwnd:HWND
        mov wc.cbSize,SIZEOF WNDCLASSEX
        mov wc.style, CS_HREDRAW or CS_VREDRAW
        mov wc.lpfnWndProc, OFFSET WndProc
        mov wc.cbClsExtra,NULL
        mov wc.cbWndExtra,NULL
        push hInstance
        pop wc.hInstance
        mov wc.hbrBackground,COLOR_WINDOW
        mov wc.lpszMenuName,NULL
        mov wc.lpszClassName,OFFSET ClassName
        invoke LoadIcon,NULL,IDI_APPLICATION
        mov wc.hIcon,eax
        mov wc.hIconSm,eax
        invoke LoadCursor,NULL,IDC_ARROW
        mov wc.hCursor,eax
        invoke RegisterClassEx, addr wc
        INVOKE CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,
            DS_MODALFRAME or WS_POPUP or WS_CAPTION or WS_SYSMENU,10, 15, 250, 130,NULL,NULL,\
            hInst,NULL
        mov hwnd,eax
        invoke ShowWindow, hwnd,SW_SHOWNORMAL
        invoke UpdateWindow, hwnd
        .WHILE TRUE
            invoke GetMessage, ADDR msg,NULL,0,0
            .BREAK .IF (!eax)
            invoke TranslateMessage, ADDR msg
            invoke DispatchMessage, ADDR msg
        .ENDW
        mov eax,msg.wParam
        ret
    WinMain Endp

    WndProc PROC hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
        .IF uMsg == WM_CLOSE
            invoke PostQuitMessage,NULL
        .ELSEIF uMsg == WM_CREATE
            invoke CreateWindowEx, WS_EX_CLIENTEDGE, ADDR EditClassName, NULL,
            WS_CHILD or WS_VISIBLE or WS_BORDER or ES_LEFT or ES_AUTOHSCROLL,
            10, 5, 220, 25, hWnd, NULL, hInstance, NULL
            mov Edit1, eax

            invoke CreateWindowEx, WS_EX_CLIENTEDGE, ADDR EditClassName, NULL,
            WS_CHILD or WS_VISIBLE or WS_BORDER or ES_LEFT or ES_AUTOHSCROLL,
            10, 35, 220, 25, hWnd, NULL, hInstance, NULL
            mov Edit2, eax

            invoke CreateWindowEx, NULL, ADDR ButtonClassName, ADDR Button1Caption,
            WS_CHILD or WS_VISIBLE, 15, 65, 100, 30, hWnd, 3001, hInstance, NULL
            mov Button1, eax

            invoke CreateWindowEx, NULL, ADDR ButtonClassName, ADDR Button2Caption,
            WS_CHILD or WS_VISIBLE, 120, 65, 100, 30, hWnd, 3002, hInstance, NULL
            mov Button2, eax

            invoke SetFocus, Edit1
            ret
        .elseif uMsg == WM_COMMAND
            .if wParam == 3002
                invoke SendMessage, hWnd, WM_CLOSE, NULL, NULL
                ret
            .elseif wParam == 3001
                mov eax, wParam
                shr eax, 16
                .if eax == BN_CLICKED
                    invoke SendMessage, Edit1, WM_GETTEXT, 30, ADDR UserName
                    .if eax < 10
                        invoke MessageBox, hWnd, ADDR Corto, ADDR AppName, MB_ICONERROR+MB_OK
                        ret
                    .endif

                    ;*********************************************
                    ;***** INIZIO CALCOLO DEL SERIALE VALIDO *****
                    ;*********************************************
                    xor ecx, ecx

                    @@INIZIO:
                        mov al, byte ptr [UserName+ecx]
                        cmp al, 00h
                        jz @@STEP1
                        .if al > "Z"
                            sub al, 20h
                        .endif
                        mov byte ptr [UserName+ecx], al
                        inc ecx
                        jmp @@INIZIO

                    @@STEP1:
                        xor ecx, ecx
                        xor ebx, ebx

                    @@STEP2:
                        mov al, byte ptr [UserName+ecx]
                        cmp cl, 04h
                        jz @@FINE1

                    @@STEP3:
                        cmp al, byte ptr [Chars+ebx]
                        jz @@STEP4
                        inc ebx
                        jmp @@STEP3

                    @@STEP4:
                        .while ebx > 9
                            sub ebx, 9
                        .endw
                        mov dl, byte ptr [Numb+ebx]
                        mov byte ptr [MySerial+ecx], dl
                        inc ecx
                        xor ebx, ebx
                        jmp @@STEP2

                    @@FINE1:
                        mov byte ptr [MySerial+ecx], "-"
                        inc ecx
                        mov esi, OFFSET UserName

                    @@JUMP1:
                        .if ecx == 14
                            jz @@JUMP2
                        .endif
                        mov al, byte ptr [esi]
                        xor al, cl
                        .if al > "Z"
                            sub al, 08
                        .elseif al < "0"
                            add al, 10
                        .endif
                        mov byte ptr [MySerial+ecx], al
                        inc esi
                        inc ecx
                        jmp @@JUMP1

                    @@JUMP2:
                        add cl, 20h
                        mov byte ptr [MySerial+ecx], cl
                        mov byte ptr [MySerial+ecx+1], 00h

                        invoke SetWindowText, Edit2, ADDR MySerial
                .endif
            .endif
        .else
            invoke DefWindowProc, hWnd, uMsg, wParam, lParam
            ret
        .ENDIF
        xor eax,eax
        ret
    WndProc endp
    end Start

 

Note finali

 

Ecco fatto! Abbiamo finito tutto! Compilate il vostro proggy, avviatelo, inserite un nome di almeno 10 caratteri, cliccate su "Create Key" e festeggiate come tanti animali impazziti! Beh, mi rendo conto che come prima volta... ho creato un crackme abbastanza rompiballe, bah... cercherò di migliorare. Spero che il mio lavoro vi sia piaciuto (anche perché ci ho messo tre notti di fila per farlo :P). Ringrazio di nuovo AndreaGeddon, Quequero e tutta la U.I.C. con la promessa di scrivere altri tutes ;).

 

Un saluto

[ active85k ]

 

Disclaimer

Vorrei ricordare che il software va comprato e  non rubato, dovete registrare il vostro prodotto dopo il periodo di valutazione. Non mi ritengo responsabile per eventuali danni causati al vostro computer determinati dall'uso improprio di questo tutorial. Questo documento è stato scritto per invogliare il consumatore a registrare legalmente i propri programmi, e non a fargli fare uso dei tantissimi file crack presenti in rete, infatti tale documento aiuta a comprendere lo sforzo immane che ogni singolo programmatore ha dovuto portare avanti per fornire ai rispettivi consumatori i migliori prodotti possibili.

Noi reversiamo al solo scopo informativo e di miglioramento del linguaggio Assembly.