Soluzione Crackme 33
From UIC
Crackme n.33 : Il primo CrackMe scritto da blAAd!
Contents |
| Infos | |
|---|---|
| Author: | ValerioSoft |
| Email: | ValerioSoft(AT)tin.it |
| Website: | |
| Date: | 03/10/2007 (dd/mm/yyyy) |
| Level: |
|
| Language: | Italian |
| Comments: | Chi semina in ottobre sotto la pioggia, raccoglie in giugno sotto il sole. :-D |
Introduzione
Ciaoooooooooo, benvenuti al mio decimo tuteeeee!!! :-D
Tools
- OllyDBG
- AXE
- Un compilatore C
Notizie sul CrackMe N.33
Regolamento:
Patching solo per Anti_SICE. No Brute Forcing.
Essay
Dato che questo CrackMe risulta essere molto semplice darò molte cose per scontate!!!Premiamo il pulsante Check it in modo tale da far comparire il messaggio No No Try Again.
Controlliamo se compare nelle Referenced Text String.
Bene eccolo qui insieme alle congratulazioni:
00401236 JNZ SHORT 0040125E
00401238 CMP DWORD PTR DS:[ESI+4],5E5730C0
0040123F JNZ SHORT 0040125E
00401241 CMP DWORD PTR DS:[ESI+8],58BD5040
00401248 JNZ SHORT 0040125E
0040124A PUSH 0 // Style = MB_OK|MB_APPLMODAL
0040124C PUSH 00403009 // Title = "tOo MuCh blAAd! #1"
00401251 PUSH 004030F7 // Text = "G r e a t , Y o u W i n ! ! ----GAME OVER----"
00401256 PUSH 0 // hOwner = NULL
00401258 CALL USER32.MessageBoxA // MessageBoxA
0040125D RETN
0040125E PUSH 0 // Style = MB_OK|MB_APPLMODAL
00401260 PUSH 00403009 // Title = "tOo MuCh blAAd! #1"
00401265 PUSH 00403143 // Text = "No No Try Again!!"
0040126A PUSH 0 // hOwner = NULL
0040126C CALL USER32.MessageBoxA // MessageBoxA
A quanto pare per far si che compaia la scritta G r e a t , Y o u W i n ! ! ----GAME OVER---- occorre superare i tre JNZ senza saltare alla locazione 0040125E. Viene confrontato il contenuto della locazione 00403155 con dei valori fissi, cerchiamo adesso la funzione che acquisisce i caratteri inseriti da tastiera, quindi piazziamo un breakpoint sulla funzione USER32.GetDlgItemTextA. Osserviamo l'indirizzo di memoria che funge da buffer:
004010E1 PUSH 00403155 // Buffer = blaadme.00403155
004010E6 PUSH 0BB8 // ControlID = BB8 (3000.)
004010EB PUSH [ARG.1] // hWnd
004010EE CALL USER32.GetDlgItemTextA // GetDlgItemTextA
Come si può notare il buffer coincide con l'indirizzo trovato in precedenza, adesso ci basta tenerlo sotto controllo con un breakpoint on memory access.
00401217 MOV CL,DS:[EDI+8] // mette in CL il 9o byte del codice
0040121A MOV EBX,DS:[EDI+EAX*4] // mette in EBX i primi 4 byte del codice
0040121D XOR DS:[ESI+EAX*4],EBX // xora i primi 4 byte del serial inserito con i primi
// 4 byte del codice<br />
00401220 ADD DS:[ESI+EAX*4],CL // somma al primo byte del serial l'ottavo byte
// del codice<br />
00401223 AND WORD PTR DS:[ESI+EAX*4],0F0F0 // fa un AND tra i primi 2 byte del serial e 0F0F0
00401229 INC EAX // incrementa EAX
0040122A CMP BYTE PTR DS:[EDI+EAX*4],0 // confronta il quarto byte del codice con 00
0040122E JNZ SHORT 0040121A
L'algoritmo che modifica il serial inserito è molto semplice ma non basta infatti osservate l'istruzione 00401217 e 0040121D. Esiste un altro serial (io lo chiamo codice per differenziarlo dal serial inserito). Vediamo di che si tratta, piazziamo un breakpoint on memory access in modo tale da osservare quando ed in che modo viene creato.
004011C7 MOV ECX,6 // mette 6 in ECX
004011CC MOV BL,DS:[EDI] // mette in BL il primo byte della locazione 00403356
004011CE ADD BL,CL // somma 6 con 0 e memorizza il risultato in BL
004011D0 XOR BL,DS:[EDI] // xora BL con 0
004011D2 MOV DS:[EDI],BL // salva il valore di BL nella locazione 00403356
004011D4 INC EDI // incrementa EDI
004011D5 DEC ECX // decrementa ECX
004011D6 JNZ SHORT 004011CC
004011D8 MOV AX,DS:[EDI-6] // mette in AX i primi 2 byte del codice
004011DC SUB AX,0A // sottrae 0A ad AX
004011E0 XOR DS:[EDI],AX // xora AX con il 7o e l'8o byte del codice
004011E3 MOV AX,DS:[EDI-4] // mette in AX il 3o e 4o byte del codice
004011E7 SUB AX,9 // sottrae 9 ad AX
004011EB XOR DS:[EDI+2],AX // xora AX con il 9o e 10o byte del codice
004011EF MOV AX,DS:[EDI-2] // mette in AX il 5o ed il 6o byte del codice
004011F3 SUB AX,5 // sottrae 5 ad AX
004011F7 XOR DS:[EDI+4],AX // xora AX con l'11o e il 12o byte del codice
004011FB XOR EAX,EAX // azzera EAX
004011FD LEA ESI,DS:[403155] // mette in ESI la locazione del serial inserito
00401203 LEA EDI,DS:[403356] // mette in EDi la locazione del codice
00401209 INC EAX // incrementa EAX
0040120A CMP EAX,0D // confronta EAX con 13d
0040120D JE SHORT 00401215 // salta se sono uguali
0040120F XOR DS:[EAX+EDI-1],AL // xora il primo byte del codice con AL
00401213 JMP SHORT 00401209
Non è ancora finita, prima di questo pezzo di codice ci sarebbe la lettura del file blaad.xxx, poi questi valori vengono assegnati alla locazione 00403356. I byte assegnati vengono elaborati dall'algoritmo precedente. Nel caso in cui non esiste nessun file da leggere, la locazione 00403356 conterrà solo zeri. L'algoritmo elabora il codice ed alla fine in memoria si ottengono questi valori che vengono utilizzati per l'elaborazione del serial:
Facciamo una prova, riavviamo tutto ed inseriamo come serial ValerioSoftX (12 caratteri), nella locazione 00403155 avremo:
Ora osserviamo cosa si ottiene tramite il piccolo algoritmo:
07 07 07 07 07 07 FB 0C F2 08 F6 0C
----------------- ----------------- -----------------
51 66 6B 62 + 75 6E 94 5F + 9D 6E 82 54 +
F2 F2 F2
---------------- ----------------- -----------------
43 66 6B 62 and 67 6E 94 5F and 8F 6E 82 54 and
F0 F0 F0 F0 F0 F0
---------------- ----------------- -----------------
40 60 6B 62 60 60 94 5F 80 60 82 54
Dopo l'elaborazione in memoria compare:
00403155 40 60 6B 62 - 60 60 94 5F - 80 60 82 54 @`kb``._.`.T
Come si può intuire:
62 6B 60 40 è diverso da 34 35 20 D0 (primo CMP)
5F 94 60 60 è diverso da 5E 57 30 C0 (secondo CMP)
54 82 60 80 è diverso da 58 BD 50 40 (terzo CMP)
Facendo il discorso a ritroso possiamo individuare il serial corretto:
F0 F0 F0 F0 F0 F0
----------------- ----------------- -----------------
D0 20 35 34 - C0 30 57 5E - 40 50 BD 58 -
F2 F2 F2
---------------- ----------------- -----------------
DE 20 35 34 xor CE 30 57 5E xor 4E 50 BD 58 xor
07 07 07 07 07 07 FB 0C F2 08 F6 0C
---------------- ----------------- -----------------
D9 27 32 33 C9 37 AC 52 BC 58 4B 54
Questo è il serial esatto nel caso in cui non utilizziamo il file blaad.xxx:
00403155 D9 27 32 33 C9 37 AC 52 BC 58 4B 54 Ù'23É7.R¼XKT
Questo serial ha l'inconveniente di essere difficile da ricordare e da scrivere, quindi vorrei continuare l'analisi del codice in modo tale da poter inserire nel keyfile Valerio-Soft (dato che occorrono 12 byte). Dopo la lettura del keyfile Valerio-Soft comparirà in memoria alla locazione 00403356 per poi essere elaborato.
Guardiamo la prima elaborazione:
004011C7 MOV ECX,6 // mette 6 in ECX
004011CC MOV BL,DS:[EDI] // mette in BL il primo byte della locazione 00403356
004011CE ADD BL,CL // somma 6 con 0 e memorizza il risultato in BL
004011D0 XOR BL,DS:[EDI] // xora BL con 0
004011D2 MOV DS:[EDI],BL // salva il valore di BL nella locazione 00403356
004011D4 INC EDI // incrementa EDI
004011D5 DEC ECX // decrementa ECX
004011D6 JNZ SHORT 004011CC
;( valore 1 + 6h ) xor ( valore 1 ) = memorizza in pos 1 =>(56h + 6h) xor ;(56h) = 0Ah
;( valore 2 + 5h ) xor ( valore 2 ) = memorizza in pos 2
;( valore 3 + 4h ) xor ( valore 3 ) = memorizza in pos 3
;( valore 4 + 3h ) xor ( valore 4 ) = memorizza in pos 4
;( valore 5 + 2h ) xor ( valore 5 ) = memorizza in pos 5
;( valore 6 + 1h ) xor ( valore 6 ) = memorizza in pos 6
;alla fine in memoria avremo:
;00403356 0A 07 1C 0D 06 03 6F 2D 53 6F 66 74 ......o-Soft
Guardiamo la secconda elaborazione:
004011DC SUB AX,0A // sottrae 0A ad AX
004011E0 XOR DS:[EDI],AX // xora AX con il 7o e l'8o byte del codice
004011E3 MOV AX,DS:[EDI-4] // mette in AX il 3o e 4o byte del codice
004011E7 SUB AX,9 // sottrae 9 ad AX
004011EB XOR DS:[EDI+2],AX // xora AX con il 9o e 10o byte del codice
004011EF MOV AX,DS:[EDI-2] // mette in AX il 5o ed il 6o byte del codice
004011F3 SUB AX,5 // sottrae 5 ad AX
004011F7 XOR DS:[EDI+4],AX // xora AX con l'11o e il 12o byte del codice
(secondo-primo valore) - (quarto-terzo valore) - (sesto-quinto valore) -
0A 9 5
------------------------ ----------------------- -----------------------
risultato xor risultato xor risultato xor
(ottavo-settimo valore) (decimo-nono valore) (dodicesimo-undicesimo valore)
alla fine in memoria avremo:
00403356 0A 07 1C 0D 06 03 6F 2A 40 62 67 77 ......o*@bgw
Guardiamo la terza elaborazione:
0040120A CMP EAX,0D // confronta EAX con 13d
0040120D JE SHORT 00401215 // salta se sono uguali
0040120F XOR DS:[EAX+EDI-1],AL // xora il primo byte del codice con AL
00401213 JMP SHORT 00401209
valore 1 xor 1 = risultato in prima posizione
valore 2 xor 2 = risultato in seconda posizione
e così via fino alla dodicesima, alla fine in memoria avremo:
00403356 0B 05 1F 09 03 05 68 22 49 68 6C 7B ......h"Ihl{
Abbiamo ottenuto il keyfile elaborato, adesso però per risalire al serial tocca a noi elaborarlo con il metodo già utilizzato in precedenza:
F0 F0 F0 F0 F0 F0
----------------- ----------------- -----------------
D0 20 35 34 - C0 30 57 5E - 40 50 BD 58 -
49 49 49
---------------- ----------------- -----------------
87 20 35 34 xor 77 30 57 5E xor F7 50 BD 58 xor
0B 05 1F 09 03 05 68 22 49 68 6C 7B
---------------- ----------------- -----------------
8C 25 2A 3D 74 35 3F 7C BE 38 D1 23
Ora riproviamo con il nuovo serial:
Ecco che compare la scritta G r e a t , Y o u W i n ! ! ----GAME OVER----
Il KeyGen
Ecco il keygen il linguaggio C:
#include <string.h>
#define DIM 24
// converte un'array di interi in esadecimale
void esadecimale(int nome[]);
// visualizza il numero convertito nella base scelta
void visualizza();
// vettore per memorizzare le cifre del numero convertito
int vet[DIM];
int risultato;
int AL,AH;
int main()
{
// vettore che contiene il nome inserito dall'utente
char nomeC[12];
// vettore di interi per il nome inserito dall'utente
int nome[12];
// Codice da ottenere per le congratulazioni
int mappa[12] = {0xD0,0x20,0x35,0x34,0xC0,0x30,0x57,0x5E,0x40,0x50,0xBD,0x58};
unsigned int i;
int valore1,valore2;
int primo,secondo;
printf("\n\n\n Keygen CrackMe N.34 coded by ValerioSoft\n\n");
printf("--------------------------------------------------------------------------\n");
printf("ATTENZIONE: Inserisci il codice del keyfile!!!\n");
printf("ATTENZIONE: Il sistema crea il keyfile con il nome inserito e ci mostra\n");
printf(" il Serial da inserire nel box di registrazione!!!\n");
printf("--------------------------------------------------------------------------\n\n");
printf("Inserisci un codice di 12 caratteri: ");
scanf("%s", nomeC);
while (strlen(nomeC) != 12) {
printf("\nAttenzione il nome inserito non rispetta la lunghezza attesa!\n\n");
printf("Inserisci un nome: ");
scanf("%s", nome);
}
// Creo il keyfile con il nome inserito
FILE *handle = fopen("blaad.xxx","w");
fprintf(handle,"%s",nomeC);
fclose(handle);
// passo i caratteri del nome in un array di interi
for(i=0; i<12; i++) {
nome[i] = nomeC[i];
}
// Prima elaborazione
for(i=0; i<6; i++) {
nome[i] = (nome[i] + (6 - i)) ^ nome[i];
}
// Seconda elaborazione
primo = nome[1] * 0x100;
valore1 = nome[0] + primo;
valore1 -= 0x0A;
secondo = nome[7] * 0x100;
valore2 = nome[6] + secondo;
risultato = valore1 ^ valore2;
__asm("mov _risultato,%ax");
__asm("mov %al,_AL");
__asm("mov %ah,_AH");
nome[6] = AL;
nome[7] = AH;
primo = nome[3] * 0x100;
valore1 = nome[2] + primo;
valore1 -= 0x09;
secondo = nome[9] * 0x100;
valore2 = nome[8] + secondo;
risultato = valore1 ^ valore2;
__asm("mov _risultato,%ax");
__asm("mov %al,_AL");
__asm("mov %ah,_AH");
nome[8] = AL;
nome[9] = AH;
primo = nome[5] * 0x100;
valore1 = nome[4] + primo;
valore1 -= 0x05;
secondo = nome[11] * 0x100;
valore2 = nome[10] + secondo;
risultato = valore1 ^ valore2;
__asm("mov _risultato,%ax");
__asm("mov %al,_AL");
__asm("mov %ah,_AH");
nome[10] = AL;
nome[11] = AH;
// Terza elaborazione
for(i=0; i<12; i++) {
nome[i] = nome[i] ^ i+1;
}
printf("\n\nCodice del keyfile elaborato: ");
esadecimale(nome);
visualizza();
printf("\n\n Codice congratulazioni: ");
esadecimale(mappa);
visualizza();
risultato = mappa[0] - nome[8];
__asm("mov _risultato,%al");
__asm("mov %al,_AL");
mappa[0] = AL;
risultato = mappa[4] - nome[8];
__asm("mov _risultato,%al");
__asm("mov %al,_AL");
mappa[4] = AL;
risultato = mappa[8] - nome[8];
__asm("mov _risultato,%al");
__asm("mov %al,_AL");
mappa[8] = AL;
// Ultima elaborazione
for(i=0; i<12; i++) {
mappa[i] = nome[i] ^ mappa[i];
}
printf("\n\n Serial: ");
esadecimale(mappa);
visualizza();
// attende un carattere qualsiasi in ingresso
scanf("%s", nome);
return 0;
}
// converte un'array di interi in esadecimale
void esadecimale(int nome[]) {
int i,j=0,temp;
for (i = 0;i<12; i++) {
temp = nome[i];
// resto della divisione
vet[j+1] = temp % 16;
// quoziente della divisione
temp = temp / 16;
vet[j] = temp;
j += 2;
}
}
/* Funzione che visualizza il contenuto del vettore 'vet'
*/
void visualizza() {
int j; // contatore
for(j=0;j<DIM;j++) {
// visualizzazione dei valori esadecimali
if (vet[j]>=10) {
printf("%c",vet[j]+ 'A' - 10);
}
else printf("%c",vet[j]+'0');
if (j % 2 == 1) {printf(" ");}
}
// visualizza anche gli zeri a sinistra del numero convertito
printf("\n\n");
}
Per accertarmi del suo funzionamento ho inserito come codice del keyfile --quequero-- ed il risultato esadecimale del serial è stato 4F 3D 32 3D 47 35 31 2C 40 39 66 7B ovvero O=2=G51,@9f{ che ci permette di ottenere le congratulazioni!!!
Note Finali
Un saluto caloroso a va a tutta la UIC!!!
Ciaoooooooooooooooooo alla proximaaaaaaaaa
Disclaimer
I documenti qui pubblicati sono da considerarsi pubblici e liberamente distribuibili, a patto che se ne citi la fonte di provenienza. Tutti i documenti presenti su queste pagine sono stati scritti esclusivamente a scopo di ricerca, nessuna di queste analisi è stata fatta per fini commerciali, o dietro alcun tipo di compenso. I documenti pubblicati presentano delle analisi puramente teoriche della struttura di un programma, in nessun caso il software è stato realmente disassemblato o modificato; ogni corrispondenza presente tra i documenti pubblicati e le istruzioni del software oggetto dell'analisi, è da ritenersi puramente casuale. Tutti i documenti vengono inviati in forma anonima ed automaticamente pubblicati, i diritti di tali opere appartengono esclusivamente al firmatario del documento (se presente), in nessun caso il gestore di questo sito, o del server su cui risiede, può essere ritenuto responsabile dei contenuti qui presenti, oltretutto il gestore del sito non è in grado di risalire all'identità del mittente dei documenti. Tutti i documenti ed i file di questo sito non presentano alcun tipo di garanzia, pertanto ne è sconsigliata a tutti la lettura o l'esecuzione, lo staff non si assume alcuna responsabilità per quanto riguarda l'uso improprio di tali documenti e/o file, è doveroso aggiungere che ogni riferimento a fatti cose o persone è da considerarsi PURAMENTE casuale. Tutti coloro che potrebbero ritenersi moralmente offesi dai contenuti di queste pagine, sono tenuti ad uscire immediatamente da questo sito.
Vogliamo inoltre ricordare che il Reverse Engineering è uno strumento tecnologico di grande potenza ed importanza, senza di esso non sarebbe possibile creare antivirus, scoprire funzioni malevoli e non dichiarate all'interno di un programma di pubblico utilizzo. Non sarebbe possibile scoprire, in assenza di un sistema sicuro per il controllo dell'integrità, se il "tal" programma è realmente quello che l'utente ha scelto di installare ed eseguire, né sarebbe possibile continuare lo sviluppo di quei programmi (o l'utilizzo di quelle periferiche) ritenuti obsoleti e non più supportati dalle fonti ufficiali.