Cd-Edit
Serial Fishing e Keygen Reversing


26/03/00

by "AndreaGeddon"

 

 

UIC's Home Page

Published by Quequero



Ecco che vi presento il primo tutorial della sezione dedicata ai corsi per i NewBies totali, tutorial leggero ed esauriente, ottimo per iniziare a capire come funziona una protezione basata sul nome/serial che troverete molto spesso :)

 
UIC's form
Home page: www.andreageddon.8m.com
E-mail: AndreaGeddon@hotmail.com
UIC's form

Difficoltà

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

 

Eccovi la spiegazione di come viene generato il serial per Cd-Edit. Cercherò di spiegare tutti i vari punti in modo che anche il più newbie lo possa capire.


Cd-Edit
Serial fishing e Keygen Reversing
Written by AndreaGeddon

Introduzione

Trovare il serial di questo programma è un lavoro di 5 secondi, la routine di generazione del serial pensavo fosse difficile, invece no.

Tools usati

-  SoftIce

 

URL o FTP del programma

E' il Quarto Corso Newbies sul sito di Quequero

Notizie sul programma 

A dire la verità non so manco a che cacchio serve sto programma del cavolo. Comunque la protezione è la solita solfa   Nome/Serial.

Essay

Come trovare il serial lo sapete già, visto che Quequero ha postato la soluzione. Io vi dico un trucchetto più semplice e veloce: avviate il programma, non andate su "register" nella schermata iniziale, ma entrate nel programma, andate sul menù  ?->Register CdEdit, inserite un nome e numero casuale. Adesso settate col SoftIce un bpx su getwindowtexta, e premete "Register". Il Sice popperà. Non fatevi ingannare, la Api getwindowtexta in questo caso non serve proprio a nulla. Comunque, adesso che siete ne Sice, premete F12 per tornare al processo di CD-Edit e fate una ricerca di stringa sul serial falso che avete inserito. Lo potete fare col comando:

s 0 l FFFFFFFF '111222333444'

okay, il SoftIce troverà la stringa in memoria, e se guardate bene qualche riga sotto il serial finto c'è un trafiletto di numerelli strani.... segnateli ed inseriteli come serial. Funziona? Certo. Il serial fishing è tutto qui. Ora passiamo alle cose serie, al reversing della routine di generazione del serial.

Iniziamo inserendo un nome e serial a caso. Nel mio caso inserisco:

AndreaGeddon

111222333444

vado a cercare il serial e trovo:

284681884195

che guarda caso è lo stesso numero di caratteri del mio nome. Allora facciamo un pò di prove: se inserisco Andrea come nome, il serial generato non è di 6 char ma di 12. Se inserisco AndreaG il serial è di 14, insomma, dopo un pò di tentavi si arriva facilmente alla conclusione che se il nome è da 9 lettere in su, allora il serial avrà lo stesso numero di char, mentre se i char del nome sono meno di 9, il programma raddoppia il nome e ci calcola il serial. Questo vuol dire che il serial per Andrea sarà lo stesso che per AndreaAndrea. Comunque, ora abbiamo una minima idea di quello che succede: il prog prende il nome lettera per lettera e ci calcola il serial. Ora ci resta solo da scoprire come viene calcolato. Bene, settiamo un BPX HMEMCPY e registriamoci. il softice popperà. Disabilitate il breakpoint appena inserito (con   bd *), premiamo sette volte F12 per tornare al processo del CD-Edit ed iniziamo a steppare. Ci troveremo un pò di RET vari, eseguiamoli tutti fino a che arriviamo alla linea 004757FC. Da qui inizierà la nostra indagine. Dobbiamo individuare dove viene generato il serial. Allora, fate come vi ho scritto sopra, cioè il break su Getwindowtexta e trovate il serial. Ora, la locazione dove si trovano il serial finto e quello vero è 00CAD134. Lasciate la data window su questa locazione, uscite da softice, levate il break su getwindowtexta e mettetene uno su hmemcpy. Prima di registrarvi inserite Nome e Serial diversi da quelli che c'erano prima, altrimenti in memoria vi ritroverete gli stessi numeri e non vedrete cambiamenti. Inserite entrambi da 12 caratteri, come me, così ritroverete le stesse cose che vi scrivo. Ecco un pò di codice:

 

:004757FC   cmp dword ptr [ebp-04], 00000000      dopo i RET arriviamo qui

:00475800   je 00475816

:00475802   lea edx, dword ptr [ebp-08]

:00475805   mov eax, dword ptr [ebx+000001EC]

:0047580B   call 0041EA00                          prende il numero finto

:00475810   cmp dword ptr [ebp-08], 00000000

:00475814   jne 0047584E

......

:0047584E   lea edx, dword ptr [ebp-04]

:00475851  mov eax, dword ptr [ebx+000001E4]

:00475857  call 0041EA00                           prende il nome

:0047585C  mov eax, dword ptr [ebp-04]

:0047585F  mov edx, 004759B0

:00475864  call 00403C38

:00475869  jne 004758A5

......

:004758A5   lea edx, dword ptr [ebp-04]

:004758A8  mov eax, dword ptr [ebx+000001E4]

:004758AE  call 0041EA00                           prende dei puntatori

:004758B3  mov eax, dword ptr [ebp-04]

:004758B6  lea edx, dword ptr [ebp-0C]

:004758B9  call 00473B40                           al posto del nome compare il serial giusto!

:004758BE  mov eax, dword ptr [ebp-0C]

:004758C1  push eax

 

Stavate eseguendo questo codice con la data window su 00CAD134? Allora avrete visto che in corrispondenza delle call che ho commentato sono comparsi prima il numero inserito, poi il nome, poi dei valori e alla fine sparisce il nome e compare il serial giusto. Quindi il serial viene generato nella CALL alla linea 004758B9. usciamo da SoftIce, registriamoci di nuovo, ma stavolta quando arriviamo alla linea 004758B9 invece di premere F10 premiamo F8 per tracciare la call, e arriveremo alla linea 00473B40. Da qui iniziamo a steppare fino ad arrivare al seguente codice.

 

:00473BD7  mov eax, dword ptr [ebp-1C]     mette in eax il puntatore al nome

:00473BDA  xor ebx, ebx                    azzera ebx

:00473BDC  mov bl, byte ptr [eax]          mette in BL il primo char del nome

:00473BDE  mov eax, dword ptr [ebp-04]     in eax un altro puntatore al nome

:00473BE1  call 00403B28                   con questa call ottiene il numero di volte da elaborare il char (all'inizio 0Ch)

:00473BE6  sub eax, dword ptr [ebp-0C]     decrementa il numero di volte da elaborare il char

:00473BE9  lea eax, dword ptr [eax+8*eax] [eax| = numero di volte * 8 + numero di volte

:00473BEC  mov ecx, 00000003               |

:00473BF1  cdq                             |  estende la dword eax in una quadword eax+edx

:00473BF2  idiv ecx                        |  divide il numero contenuto in eax+edx per ecx (cioè per 3)

:00473BF4  add ebx, eax                    aggiunge il risultato della divisione al relativo char del serial

:00473BF6  cmp ebx, 00000009               controlla se il risultato di tutto questo calcolo è <= 9

:00473BF9  jle 00473C82                    se lo è salta

:00473BFF  lea edx, dword ptr [ebp-14]

:00473C02  mov eax, ebx                    muovi il risultato in eax

:00473C04  call 004070C4                   trasforma il risultato da esadecimale a decimale

:00473C09  jmp 00473C82                    eax adesso punta alla locazione dove ci sarà il numero decimale

:00473C0B  xor edi, edi

:00473C0D  lea eax, dword ptr [ebp+FFFFFDE4]

:00473C13  mov edx, dword ptr [ebp-14]

:00473C16  call 004075A0

:00473C1B  xor edx, edx

:00473C1D  push ebp

:00473C1E  push 00473C71

:00473C23  push dword ptr fs:[edx]

:00473C26  mov dword ptr fs:[edx], esp

:00473C29  mov eax, dword ptr [ebp-14]

:00473C2C  call 00403B28

:00473C31  mov esi, eax

:00473C33  dec esi

:00473C34  test esi, esi

:00473C36  jl 00473C5D

:00473C38  inc esi

:00473C39  lea ebx, dword ptr [ebp+FFFFFDE4]

:00473C3F  lea eax, dword ptr [ebp+FFFFFDE0]

:00473C45  mov dl, byte ptr [ebx]

:00473C47  call 00403A50

:00473C4C  mov eax, dword ptr [ebp+FFFFFDE0]

:00473C52  call 004070F4

:00473C57  add edi, eax

:00473C59  inc ebx

:00473C5A  dec esi

:00473C5B  jne 00473C3F

:00473C5D  lea edx, dword ptr [ebp-14]

:00473C60  mov eax, edi

:00473C62  call 004070C4

:00473C67  xor eax, eax

:00473C69  pop edx

:00473C6A  pop ecx

:00473C6B  pop ecx

:00473C6C  mov dword ptr fs:[eax], edx

:00473C6F  jmp 00473C82

:00473C71  jmp 00403154

:00473C76  call 004033F8

:00473C7B  jmp 00473CB8

:00473C7D  call 004033F8

:00473C82  mov eax, dword ptr [ebp-14]

:00473C85  call 004070F4

:00473C8A  cmp eax, 00000009

:00473C8D  jg 00473C0B

:00473C93  lea eax, dword ptr [ebp-10]

:00473C96  mov edx, dword ptr [ebp-14]

:00473C99  call 00403B30

:00473C9E  inc [ebp-0C]

:00473CA1  inc [ebp-1C]

:00473CA4  dec [ebp-18]

:00473CA7  jne 00473BD7

 

questa è la routine in cui viene generato il seriale. La parte che mi è servita è stata solo quella che ho commentato. Dopo un pò di stepping mi sono segnato i primi quattro valori che ottengo da quei calcoli. Per capire meglio questi calcoli, eccovi un pò di formule comprensibili:

inizio:

    valore_1 = lunghezza_del_nome * 8 + lunghezza_del_nome

    dividi  valore_1 per 3

    valore_2 = valore_2 + char

    converti valore_2 da esadecimale a decimale

   decrementa   lunghezza_del_nome

   char = prossimo char del nome

vai a inizio  

beh, mica tanto più comprensibili.... eccovi un pò di esempi per chiarire del tutto i vostri dubbi:

facciamo i calcoli per le prime 4 lettere del mio nome

 

-- A   0Ch * 8 + 0Ch  = 6Ch

          6Ch /  3 = 24h

          24 + A =  65h       (con A = 41)

          65 hex = 101 dec

 

-- n    0B * 8 + 0B  =   63

          63 / 3 = 21

          21 + n = 8F      (con n = 6E)

          8F hex = 143 dec

 

-- d    0A * 8 + 0A = 5A

          5A / 3 = 1E

          1E + d = 82      (con d = 64)

          82 hex = 130 dec

 

-- r     9 * 8 + 9 = 51

          51 / 3 = 1B

          1B + r = 8D     (con r = 72)

          8D hex = 141 dec

 

okay, visto che ora è tutto molto più semplice? Però ancora non abbiamo finito. Io per le lettere   A n d r    ho ottenuto i rispettivi valori decimali 101, 143, 130, 141. Adesso invece di rimettermi a steppare come uno scemo mi sono soffermato a pensare un attimo: "che relazione può esserci tra questi numeri e quelli del serial giusto"? Mentre stavo per ricominciare a steppare mi è venuta in mente la soluzione:

101 = 1+0+1 =  2

143 = 1+4+3 =  8

130 = 1+3+0 =  4

141 = 1+4+1 =  6

ed ottengo 2846 che guarda un pò sono i primi 4 char del serial! Ho rifatto i conti per tutti i char del nome e mi è venuto fuori il serial completo. Certo che qui sapendo il serial in anticipo, abbiamo potuto "barare" in questo modo, se non avessi saputo il serial avrei dovuto steppare e steppare per trovare la routine che sommava le varie cifre decimali. Ma dopotutto sono pigro, e se il programma lo permette, io baro! Ecco così svelato il mistero del serial di Cd-Edit.

 

Ps. se qualcuno di voi si è registrato per sbaaglio e vuole tornare non registrato, andate nel registro alla chiave:

HKEY_LOCAL_MACHINE\Software\Stefano Falda\CD Edit\

e cancellate il valore "Code".

 

Eccovi il keygen che ho scritto in asm (cioè, in TASM). Siccome non avevo tempo, l'ho scritto senza mettere alcuni accorgimenti, tipo il controllo dei caratteri digitati, quindi scrivete solo le lettere del nome e battete invio (non premete tasti funzione, spazio e altre cagate, altrimennti il serial verrà fuori errato). Non fate caso al fatto che è scritto di merda, l'importante è che funziona.

BEGIN OF CODE_________________________________________________________________________________

;direttive del compilatore

.386
.MODEL SMALL
.STACK 400h
.DATA

;definisco le stringhe e variabili che userò
Titolo                      DB 13,10,'KeyGenerator per CDedit ',13,10,'$'
Titolo2                    DB 13,10,'Scritto da AndreaGeddon ',13,10,'$'
Prompt                   DB 13,10,'Inserisci il nome: (tra 9 e 14 char) ',13,10,'$'
Serial                      DB 13,10,'00000000000000000000',13,10
SerialTitle               DB 13,10,'Il tuo serial: ',13,10,'$'
Buffer                     DB 13,10,'0000',13,10
Nome                     DB 13,10,'Abcdefghijklmnopq',13,10
Num                       DB 13,10,'00',13,10
Errore                     DB 13,10,'Il nome deve essere tra 9 e 14 caratteri!',13,10,'$'

.CODE
start:
    mov    ax, @data                              ;metto nel registro DS (data segment)
    mov    ds, ax                                     ;il puntatore alla sezione DATA
;ora scriviamo le due righe di titolo iniziale
    mov    dx, OFFSET Titolo                ;metti in dx il puntatore alla 1° stringa
    mov    ah, 9                                      ;indica la funzione 9...
    int    21h                                           ;per l'INT 21h, cioè "Print String"
    mov    dx, OFFSET Titolo2             ;adesso printiamo la seconda stringa
    mov    ah, 9
    int    21h
;ora scriviamo il prompt di richiesta nome
    mov dx, OFFSET Prompt
    mov ah, 9
    int 21h
;adesso dobiamo prendere il nome
    xor    ecx, ecx                                  ;azzero ecx che userò come contatore
PrendiNome:
    mov    ah, 1                                       ;qui chiamo la funzione 1...
    int    21h                                            ;dell' INT 21, cioè "Prendi Char"
    cmp    al, 0Dh                                   ;controllo se è stato battuto invio
    je    Calcoli                                       ;se si, il nome l'abbiamo preso
    mov    [Nome+ecx], al                      ;mette il carattere preso nella variabile Nome
    inc    ecx                                           ;incrementa il contatore
    jmp    PrendiNome                            ;prendi un altro char
;adesso inizia la sezione dei calcoli:
Calcoli:
;restrizioni sul nome
    mov    [Nome+ecx], 00                    ;aggiunge 00 alla fine del nome (termiatore)
    cmp    ecx, 09                                 ;il nome è maggiore di 9 char?
    jl    BadExit                                     ;se no, termina con errore
    cmp    ecx, 0Eh                               ;il nome è minore di 14 char?
    jg    BadExit                                    ;se no, termina con errore
    xor    edi, edi                                   ;azzero edi che userò come contatore
    mov    byte ptr [Num], cl                  ;salvo il contatore nella variabile Num
;inizia calocli veri e propri
Inizio:
    xor    eax, eax                                  ;azzero eax che mi servirà presto
    mov    al, byte ptr [Num]                  ;metto il contatore in al
    mov    ecx, 08                                  ;metto in ecx 8...
    imul    ecx                                        ;e moltiplico eax per ecx
    add    al, byte ptr [Num]                  ;aggiungo al risultato il contatore
    mov    ecx, 03                                 ;metto il divisore (3) in ecx...
    cdq                                                 ;azzero edx
    idiv    ecx                                         ;...divido eax per ecx (per 3)
    add    al, byte ptr [Nome+edi]         ;aggiungo il char corrente del nome al risultato
    call    Converti                                 ;chiamo la procedura che converte il numero da hex a dec
    xor    esi, esi                                    ;azzero esi e lo uso come contatore generale
    xor    eax, eax                                  ;ormai lo sapete
Confr:
    cmp    byte ptr [Buffer+esi], 0FFh   ;controlla se la cifra corrente del numero decimale appea convertito è FF
    je    Decimal                                    ;se lo è, abbiamo finito di sommare
    add    al, byte ptr [Buffer+esi]          ;se non lo era, sommiamola ad eax
    inc    esi                                           ;incrementa il contatore
    jmp    Confr                                     ;ripeti
Decimal:
    cmp    eax, 09                                  ;vediamo se la somma delle cifre è minore di 9
    jle    Finito                                       ;se lo è, abbiamo finito il calcolo per la cifra e quindi salta
    call    Converti                                 ;se non lo è, riconverti (sarà 0A, 0B, 0C etc.)
    xor    eax, eax                                 ;riazzera l'accumulatore
    xor    esi, esi                                    ;e il contatore
    jmp    Confr                                    ;torna a sommare le cifre
Finito:
    add    eax, 30h                                ;se la cifra è quella finale (da 0 a 9) aggiungigli 30 ed otterremo il corrispondente ascii
    mov    byte ptr [Serial+edi], al         ;muovi il numero del serial attuale nella variabile Serial
    dec    byte ptr [Num]                      ;decrementa il numero di cifre da calcolare ancora
    inc    edi                                          ;incrementa il contatore generale
    cmp    byte ptr [Num], 00               ;se il numero di cifre rimaste da calcolare non è zero...
    jne    Inizio                                     ;torna all'inizio e calcola la prossima cifra
    mov    byte ptr [Serial+edi], '$'        ;altrimenti aggiungi il terminatore al serial ($)
    mov    dx, OFFSET SerialTitle        ;così lo possiamo mostrare
    mov    ah, 9
    int 21h
    mov    dx, OFFSET Serial
    mov    ah, 9
    int    21h
    mov ah,4ch                             ;Fuzione: termina programma
    mov al,0                                 ;return code = 0
    int 21h                                    ;termina il programma

;uscita in caso di errore
BadExit:
    mov    dx, OFFSET Errore     ;mostra l'errore
    mov    ah, 9
    int    21h
    mov    ah, 4Ch                          ;e termina il programma
    mov    al, 0
    int    21h
;---------------------------------------------------------------

;procedura che converte da hex a dec

;richiede in eax il numero da convertire, e restituisce

;nella variabile Buffer il valore dec.

;nota che questa routine non va bene per le normali coversioni perchè

;vi restituirà il numero decimale con le cifre invertite, ma siccome qui ci

;interessa la somma delle singole cifre del numero decimale, non ha importanza

;se sono dritte o invertite
Converti proc
    xor    ebx, ebx       ;ebx lo uso come contatore
    mov    ecx, 0Ah     ;metto in ecx il divisore (10 decimale)
Riconv:
    cdq                        ;estendi la dword eax in una quadword eax+edx (praticamente azzero edx)
    idiv    ecx               ;dividi eax per 10dec
    mov    byte ptr [Buffer+ebx], dl         ; muovi il resto della divisione in buffer
    inc    ebx                                          ; incrementa il contatore
    cmp    eax, 00                                  ; la cifra in eax è 0?
    jne Riconv                                        ; se non lo è continua la conversione
    mov    byte ptr [Buffer+ebx], 0FFh  ; metti FF alla fine di Buffer (lo uso come terminatore)
    ret                                                    ; ritorna
Converti endp
;----------------------------------------------------------------
End Start
END OF CODE____________________________________________________________________

 

AndreaGeddon rules again   !!!!

 

Note Finali

Spero che questo tute sia alla portata di tutti, newbies e non. Saluto tutti gli amici della mailing list.

Disclaimer

Queste informazioni sono solo a scopo puramente didattico. Non mi ritengo responsabile dell'uso che ne verrà fatto.

 
UIC's page of reverse engineering, scegli dove andare:

Home   Anonimato   Assembly    CrackMe   ContactMe   Forum   Iscrizione      
       Lezioni    Links   Linux   NewBies   News   Playstation        
  Tools   Tutorial   Search   UIC Faq

UIC