EVC Trial Crackme 1.0 |
||
Data |
by "AndreaGeddon" |
|
20/12/2000 |
Published by Quequero |
|
|
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 |
.... |
|
.... |
Difficoltà |
(X)NewBies ( )Intermedio ( )Avanzato ( )Master |
Ecco come risolvere questo banale crackme che tanto avete amato...
Introduzione |
Tools usati |
URL o FTP del programma |
Nin so, l'ha postato LordSpectre in ML, rivolgetevi a lui.
Notizie sul programma |
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 programmaaltrimenti 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.