EVC Trial Crackme 1.0
RiscCrackme: BruteForcer & KeyMaker

Data

by "AndreaGeddon"

 

20/12/2000

UIC's Home Page

Published by Quequero


R!sc

Qualche mio eventuale commento sul tutorial :))) :-P

Buone Feste :-).

Arrrr le tue maledette manie di protagonismo hihihihih :)...Vorresti magari pure un commento al tute vero? Col PIFFEROOOOOOOOOO :)))))))

cs!R

....

Home page: www.andreageddon.8m.com   /  www.andreageddon.com
E-mail: [email protected]
IRC:  irc.azzurra.it / irc.tin.it      #crack-it

....

Difficoltà

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

 

Ecco come risolvere questo banale crackme che tanto avete amato...


EVC Trial Crackme 1.0
RiscCrackme: BruteForcer & KeyMaker
Written by AndreaGeddon

Introduzione

Il trial crackme della Ebola Virus Crew. E pensare che il trial della Core implementa l'MD5! Quando si dice differenti punti di vista...

Tools usati

SoftIce
FrogSice
-  Compilatore VC++ (ma con qualche modifica il codice lo potete portare ovunque)

URL o FTP del programma

Nin so, l'ha postato LordSpectre in ML, rivolgetevi a lui.

Notizie sul programma

Semplice Nome/Serial con qualche trick AntiSoftice.

Essay

Causa un leggero errore di valutazione all'inizio credevo fosse impossibile trovare il seriale, invece è molto molto semplice. Vediamo che s'ha da fare.

 

PARTE PRIMA: CAPIAMO L'ALGORITMO

Per prima cosa se lanciamo il crackme ci appare la naggina "i dont like softice". Per questo basta usare il frogSice, e il crackme partirà. Vediamo che non c'è il pulsante "Register", quindi la funzione per prendere il testo si troverà sull'evento EN_CHANGE che viene postato ogni volta che fate qualcosa che può cambiare il testo dell'editbox (ad esempio se ci scrivete dentro).

Quindi scriviamo il nostro nome, scriviamo il seriale fasullo, settiamo il bpx GetDlgItemTextA, e scriviamo un altro carattere del serial. Il softice poppa, ma non solo una volta, quindi premete cinque volte F5 per arrivare all'ultima volta che il softice poppa (e quindi che viene richiamata GetDlgItemTextA), e ora possiamo iniziare. Ci ritroveremo qui:

 

:0040105A    call 00401423      call getdlgitemtexta

:0040105F    push 00000000      template

:00401061    push 00000080      attributi

:00401066    push 00000003      azione OPEN_EXISTING

:00401068    push 00000000      struttura di sicurezza

:0040106A    push 00000000      share mode

:0040106C    push 80000000      accesso GENERIC_READ

* Possible StringData Ref from Data Obj ->"\\.\SIWVID"

:00401071    push 00402000      pusha una strana stringa! Il nome del file da aprire!

:00401076    call 00401465      call CreateFile

:0040107B    cmp eax, FFFFFFFF  se l'handle del file è valido...

:0040107E    jne 004011BC       salta a "softice detected"

:00401084    call 0040109E      routine che ci interessa

:00401089    test eax, eax

:0040108B    jne 00401097

 

PS:

Non potete disassemblare il crackme così com'è perchè è compresso con UPx. Sarà sufficiente eseguire il programma, poi caricare il PEditor, e nella lista dei task fare il dump full del crackme. Avrete quindi un file decompresso e già perfettamente funzionante e disassemblabile.

Inoltre se mettete un breakpoint su una linea del programma vi apparirà un flood di messagebox  di errato checksum, quindi evitateli.

subito dopo il GetDlgItemTextA vediamo una chiamata a CreateFile. In particolare chiama l'apertura del file SIWID in OPEN_EXISTING, cioè va solo ad aprire il file di driver del softice, se riesce ad aprirlo, allora otterrà un handle valido (diverso da FFFFFFFF) e questo vuol dire che il softice è caricato. Se invece ottiene FFFFFFFF (INVALID_HANDLE_VALUE) allora non ha ottenuto un handle valido per l'apertura del file di driver del softice e quindi tale driver non è presente. Questo trick Anti Softice è noto come MeltIce. Comunque, se avevate il FrogSice attivo vedrete che il jnz non viene eseguito, e arriviamo alla call alla riga 00401084. Ci entriamo e ci ritroveremo in questo punto:

 

:0040109E    mov esi, 004020CA   metti in esi il puntatore alla stringa-serial

:004010A3    xor ecx, ecx        azzera il contatore

:004010A5    xor ebx, ebx        e pure ebx

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:

:004010A7    add bl, cl          aggiungi il char attuale a BL (numero-serial)

:004010A9    mov cl, byte ptr [esi]  metti in cl l'n-esimo char della stringa

:004010AB    inc esi              punta al char successivo

:004010AC    test cl, cl         controlla se cl è zero

:004010AE    je 004010EB         se lo è, abbiamo finito i char

:004010B0    shl ebx, 04         shifta a sinistra il numero-seriale in ebx

:004010B3    cmp cl, 30          controlla il char attuale con 0

:004010B6    jl 0040111A         salta se è minore

:004010B8    cmp cl, 66          controlla il char attuale con f

:004010BB    jg 0040111A         salta se è maggiore

:004010BD    sub cl, 30           sottrai 30 al char e ottieni il numero corrispondente

:004010C0    cmp cl, 09          controlla il numero con 9

:004010C3    jle 004010A7        se il numero è minore allora salta

:004010C5    sub cl, 07          altrimenti sottraigli altri 7

:004010C8    cmp cl, 09          ricontrolla se il numero è...

:004010CB    jle 0040111A        ... minore di 9

:004010CD    test cl, cl         controlla cl

:004010CF    js 0040111A         salta se è andato in negativo

:004010D1    cmp cl, 0F          controlla il numer ottenuto con 0F

:004010D4    jle 004010A7        salta se minore

:004010D6    sub cl, 20          sottrai 20h al numero ottenuto

:004010D9    test cl, cl         controlla il numero

:004010DB    js 0040111A         salta se è andato sotto zero

:004010DD    cmp cl, 09          controlla il numero ottenuto con 09

:004010E0    js 0040111A         salta se è negativo

:004010E2    je 0040111A         salta se è uguale a nove

:004010E4    cmp cl, 0F          controlla il numero con 0F

:004010E7    jle 004010A7        salta se è più piccolo

:004010E9    jmp 0040111A       

* Referenced by a (U)nconditionalor (C)onditional Jump at Address:

:004010EB    pop eax

:004010EC    mov dword ptr [004020E2], ebx  ebx contiene il numero-serial

:004010F2    sub esi, 004020D8   controlla se la lunghezza non è zero

:004010F8    test esi, esi

:004010FA    jge 00401130        se è maggiore di zero, salta

* Possible StringData Ref from Data Obj ->"0BADC0DE"

:004010FC    mov esi, 004020CA   metti in esi il puntatore alla stringa-serial

nota che 0BADC0DE c'è solo all'avvio del programma

:00401101    cmp dword ptr [esi], 2D435665  confronta i primi 4 char del seriale con la stringa: eVC-

:00401107    jne 00401130     se non sono uguali, salta alla terminazione precoce!

 

questa routine non fa altro che trasformare la stringa inserita nell'intero corrispondente. Ad esempio se abbiamo inserito la stringa "112233aa" come serial, i byte corrispondenti sono 31 31 32 32 33 61 61, quindi vengono presi uno alla volta, vengono trasformati nel numero corrispondente tramite le apposite sottrazioni, e poi vengono messe in ebx che di volta in volta viene shiftato, fino ad ottenere in ebx il numero 0x112233AA. Tale numero viene poi salvato nella locazione 004020E2. Dopo questo casino infine viene fatto il fatidico compare: vengono confrontati i primi quattro byte della stringa-serial inserita con la stringa 2D435665, che in ascii equivale a "eVC-". Se il confronto non è positivo, saltiamo all'uscita dalla routine. Quindi i primi quattro caratteri del seriale devono essere eVC-. Ora che lo sappiamo riscriviamo un seriale che abbia i primi 4 char appena visti, e ripoppiamo il softice così dovremmo arrivare alla routine di calcolo. Rifate la stessa strada di prima, e una volta arrivati alla riga 00401130 non salterete. Un paio di righe sotto, alla riga 0040110F c'è una call. Ci entriamo ed ecco cosa ci troviamo:

 

:0040117F    mov esi, 004020A0  metti in esi il puntatore al Nome

:00401184    mov ebx, 004020E2  metti in ebx il puntatore al numero-serial

:00401189    mov ecx, dword ptr [esi]  metti in ecx i primi 4 char del nome

:0040118B    imul ecx, 0BADC0DE  moltiplicali per 0x0BADCODE

:00401191    imul ecx, dword ptr [004020DE] moltiplica il risultato per la lunghezza del nome

:00401198    xor edi, edi       azzera edi

* Referenced by a (U)nconditional or (C)onditional Jump at Address:

:0040119A    xor eax, eax     ed eax

:0040119C    lodsb            carica l'n-esimo char del nome in eax

:0040119D    test eax, eax       controlla che non sia zero

:0040119F    je 004011AC      se lo è, vai al confronto finale

:004011A1    imul eax, ecx    motliplica il risultato di BADC0DE*Lungh*Pirmi4Char col carattere n-esimo

:004011A4    add ecx, edi     aggiungigli edi

:004011A6    mov edi, eax     salva il risultato in edi

:004011A8    xor dword ptr [ebx], edi  xora edi con il contenuto di ebx, che inizialmente è il numero-serial

:004011AA    jmp 0040119A     continua

* Referenced by a (U)nconditional or (C)onditional Jump at Address:

:004011AC    pop eax

:004011AD    pop esi

:004011AE    cmp edi, dword ptr [ebx]   controlla il risultato di tutti gli xor con il valore di edi

:004011B0    jne 00401130    se non sono uguali, ritorna al ciclo principale del programma

altrimenti scrive Registered sulla title bar.

 

e questa è la routine principale, quella che calcola se il seriale è esatto oppure no. As usual riassumiamo in pseudo codice l'algoritmo:

ecx = primi4char*lunghezza*0x0BADC0DE

eax = ecx*n-esimo char del nome

ecx = ecx + edi

edi = eax

[ebx] = [ebx]  xor edi

tutto qui. Alla fine del ciclo si arriva al compare, ed edi ed [ebx] devono risultare uguali. Ma proprio prima del compare, [ebx] veniva xorato con edi, quindi come faccio a xorare due numeri e poi averli uguali? E' questo che all'inizio mi aveva fuorviato e mi aveva fatto pensare che il serial non fosse calcolabile, ma poi ho ragionato megli sulle proprietà dello xor:

a xor b = c

a xor 0 = a

0 xor 0 = 0

quindi o arrivo alla fine in modo tale che sia [ebx] che edi siano 0 (improbabile), o faccio in modo che solo [ebx] sia 0: in tal caso

edi xor [ebx] = edi

il risultato dello xor verrà messo in [ebx], così mi ritroverò con lo stesso valore sia in edi che [ebx]. Ebx però arriva da una catena di xor, quindi devo ricalcolarmi all'indietro il seriale tramite tutti gli xor. Ad esempio per comodità di calcolo suppongo di avere come nome la stringa "ABCD" e il serial "11223344" devo prima calcolarmi   tutti i parametri "all'andata", quindi:

 

ABCD * BADC0DE = 34F1345E     inizio

34F1345E * 04 = D3C4D178         primi4char*lunghezza*BADC0DE

 

- A -

41 * D3C4D178 = C4F92F78                           eax = eax * ecx

0 + D3C4D178 = D3C4D178                           ecx = ecx + edi

C4F92F78  xor   11223344 = D5DB1C3C       xor [ebx], edi

 

- B -

42 * D3C4D178 = 98BE00F0

C4F92F78 + D3C4D178 = 98BE00F0

98BE00F0   xor  D5DB1C3C = 4D651CCC

 

- C -

43 * 98BE00F0 = F9BA3ED0

98BE00F0 + 98BE00F0 = 317C01E0

F9BA3ED0   xor  4D651CCC = B4DF221C

 

- D -

44 * 317C01E0 = 24F07F80

F9BA3ED0 + 317C01E0 = 2B3640B0

24F07F80   xor  B4DF221C = 902F5D9C

 

oki abbiamo i valori che ci interessano, ora facciamo il procedimento "al ritorno": prenderemo i valori bianchi da xorare, a partire da B4DF221C che dovremo porre a zero, quindi per ottenere zero dovrò supporre che alla iterazione precedente avevo lo xor di due valori uguali, cioè:

F9BA3ED0   xor  F9BA3ED0

ma per avere F9BA3ED0 al primo membro vuol dire che l'iterazione precedente doveva essere

98BE00F0   xor  61043E20  (61043E20 = 98BE00F0 xor F9BA3ED0)

ed ancora, per arrivare a 61043E20 l'iterazione ancora precedente doveva essere:

C4F92F78   xor  A5FD1158  (A5FD1158 = 61043E20 xor C4F92F78)

e qui mi fermo, perchè ho trovato A5FD1158, che è l'ultimo step della iterazione. E se proviamo a mettere come seriale eVC-a5fd1158 ? REGISTERED!!! Quindi abbiamo trovato un valido algoritmo per generarci seriali validi. Le lettere da inserire nel serial sono sempre minuscole, il perchè basta verlo nelle linee che convertono la stringa-serial in numero-serial. Ora viene la parte della sintesi dell'algoritmo: adesso vedremo di implementare prima un BruteForcer, poi il KeyMaker vero e prorio.

 

PARTE SECONDA: SCRIVIAMO IL BRUTE-FORCER

Il brute forcer l'ho scritto quando ancora credevo che il serial non fosse calcolabile, l'avevo scritto tanto per provare tutte le combinazioni e quindi dimostrare che il seriale non esisteva, cosa che poi non è puntualmente avvenuta! L'idea del BruteForce qui prende subito piede perchè come notate il seriale-stringa viene trasformato in un seriale-numero contenuto nel registro ebx. Essendo ebx un valore a 32 bit, ciò significa che in input possiamo avere un massimo di 2^32 = 4.294.967.296 combinazioni, che sono pochissime! Inoltre scrivere il brute forcer è molto semplice perchè copieremo la routine di controllo del seriale e la modificheremo un pochino. Il codice è il seguente, io ho usato le MFC, con qualche modifica potete usare questo codice su qualsiasi compilatore. Vi posto solo la routine principale, il resto è tutto pregenerato dalle MFC.

 

    char Nome[] = "AndreaGeddon";    //definiamo la stringa col nostro nome
    unsigned long Numero = 0;               //questo sarà il contatore
    char Result[8];                                  //questo sarà il buffer che conterrà il risultato
    int Lung = 12;                                   //qui ci va la lunghezza del nome

    __asm
    {
Next:
        inc Numero                                  //incrementa il contatore
        cmp Numero, 0xFFFFFFFF         //se il contatore è FFFFFFFF abbiamo provato tutte le combinazioni
        jz Fatto                                        // perciò esci
        push Numero                               // salva il contatore nello stack
        lea esi, byte ptr Nome                 // esi = puntatore al nome
        lea ebx, Numero                          // carica in ebx il contatore
        mov ecx, [esi]                             // carica in ecx i primi 4 char
        imul ecx, ecx, 0x0BADC0DE      // moltiplica i primi 4 char per BADC0DE
        imul ecx, Lung                             // e poi per la lunghezza
        xor edi, edi                                  //etc. uguale a prima
Continua:
        xor eax, eax
        lodsb
        test eax, eax
        jz Finito
        imul eax, ecx
        add ecx, edi
        mov edi, eax
        xor [ebx], edi
        jmp Continua
Finito:
        //pop eax                                   //questi pop non sono bilanciati da altrettanti push
        //pop esi                                   //quindi li leviamo per non andare in underflow
        cmp edi, [ebx]
        pop Numero
        jnz Next
Fatto:
    }
    ultoa(Numero, Result, 16);              //converti il numero esatto in una stringa in base 16
    SetDlgItemText(IDC_EDIT1, Result);   //metti il testo nell'editbox

 

le modifiche sono state minime, la routine è stata copiata interamente. Questo algoritmo prova tutte le chiavi in circa 20 minuti (sul mio P2 300), cmq non è ottimizzato per niente: se si divide il lavoro in più thread e si asseggnano delle priorità maggiori il tempo di calcolo dovrebbe scendere notevolmente. E questo è stato il primo passo. Ora vediamo di scrivere il KeyMaker.

 

PARTE TERZA: SCRIVIAMO IL KEYMAKER

Il keymaker richiede un pochino di impegno in più, ma poco. Non potremo copiare la routine come abbiamo appena fatto, però possiamo sintetizzare l'algoritmo di calcolo del seriale per esprimerlo in un programma, ed essendo l'algoritmo facile anche il programma sarà faicle. Anche ora posto la rotuine principale:

 

    char Nome[20] ;
    char Seriale[12];
    long Registri[96];
    int lungh;

    lungh = GetDlgItemText(IDC_EDIT2, Nome, 22);    // prende il nome inserito
    if ((lungh < 4) || (lungh > 20))     //se il nome è minore di 4 o maggiore di 20
    {
        MessageBox("Il nome deve avere da 4 a 20 char", "Errore", MB_ICONASTERISK);
        return;   errore
    }

    __asm
    {
        lea esi, byte ptr [Nome]    //calcolo iniziale
        mov ecx, [esi]
        imul ecx, ecx, 0x0BADC0DE
        imul ecx, lungh
        mov Registri[0], ecx    //in registri ci salviamo le dword che useremo per xorare
        xor edi, edi
        lea edx, byte ptr [Registri]   //carica la dword
        add edx, 4                             //aggiusta il puntatore
Continua:
        xor eax, eax
        lodsb                                    // carica l'n-esimo char
        test eax, eax
        jz Fine                                  //se è zero abbiamo finito
        imul eax, ecx                        // facciamo i dovuti calcoli
        add ecx, edi
        mov edi, eax
        mov [edx], edi                     // di volta in volta salviamo le dword per lo xor
        add edx, 4
        jmp Continua
Fine:
        lea esi, dword ptr [Registri]   //il calcolo "all'andata" è finito, ora passiamo a quello "a ritorno"
        sub esi, 04                               // aggiustiamo il puntatore al nome
        sub edx, 8                                 // e alle dword
        mov eax, dword ptr [edx]         //mettiamo una dword in eax
        sub edx, 4
        mov ebx, dword ptr [edx]         //e la dword precedente in ebx
Ciclo2:
        sub edx, 4
        cmp esi, edx                              //se sono uguali termina
        jz Fine2                               
        xor ebx, eax                                //altrimenti xorale
        mov eax, dword ptr [edx]           //carica la prossima dword e continua a xorare
        jmp Ciclo2
Fine2:
        mov dword ptr [Seriale], '-CVe'  //arrivati qui abbiamo trovato la giusta dword, non rimane che convertirla in stringa: innanzitutto aggiungiamo la pre-condizione "eVC-"
        lea edi, dword ptr [Seriale]      //carichiamo il puntatore a seriale
        add edi, 11                                //facciamolo puntare all'ultimo char
        xor ecx, ecx
Converti:
        mov al, bl
        and al, 0x0F          //semplice conversione
        add al, 0x30          //che prende la cifra più a destra
        cmp al, 0x39         //la trasforma in char
        jle NoLettera
        add al, 0x27
NoLettera:
        mov byte ptr [edi], al    // e la mette nel seriale
        dec edi
        shr ebx, 4
        inc ecx
        cmp ecx, 0x07
        jle Converti
        mov byte ptr [edi+9], 00
    }
    SetDlgItemText(IDC_EDIT1, Seriale);    //scrivi il seriale nell'editBox
   

anche questo è molto semplice, l'unico scoglio è quello di fare bene attenzione ai puntatori per farli puntare al giusto valore. Ecco risolto codesto arcano crackme che con tanta irruenza disturbò la quietanza della notra ML. O dolce donzella Mary, che questo tutoriale possa essere di tuo gradimento e possa allietare temporaneamente la tua brama di conoscenza! (domani apro la UIP, università italiana poeti :-P)

Ciauuuuz

AndreaGeddon

 

Note finali

Un saluto a LordSpectre che ha mandato questo crackme, poi un salutissimo a Mary-Rj che ha chiesto questo tutorial (altrimenti non l'avrei scritto :-P) e poi anche a Phobos. Poi tutta la ML e tutti quelli che ora non mi vengono in mente ma che ci sono. Aurevoir.

Disclaimer

Queste informazioni sono solo a scopo puramente didattico. Vi consiglio di non andare a farvi belli alla evc con i miei source. Byex.