Zoom Icon

Soluzione Crackme 26

From UIC

Crackme n.26 : Un, due, Thread STELLA!!!

Contents


Soluzione Crackme 26
Author: ValerioSoft
Email: ValerioSoft(AT)tin.it
Website:
Date: 22/07/2007 (dd/mm/yyyy)
Level: Slightly hard
Language: Italian Flag Italian.gif
Comments: Sotto il sole di Luglio il cervello non è mai saggio. :-D



Introduzione

Ciaoooooooooo, benvenuti a questa ottava fatica!!! Impressionanteeeeee :-D


Tools


Notizie sul CrackMe N.26

Questo è il primo CrackMe scritto da Black Druid e come inizio non c'è niente male!!! :-)

Regolamento:
Bisogna trovare il Serial corrispondente al proprio nome e poi creare un Keygen.


Essay

Vi annuncio subito che questo CrackMe è molto difficile da spiegare data la presenza dei Thread, cercherò di essere il più chiaro possibile!!!

Apriamo il file con Ollydbg e come al solito apriamo la finestra All referenced text string e cerchiamo un punto di attacco. Credo che la stringa ASCII Great!!! You're a little DruiD possa andare bene, allora clicchiamoci su due volte con il tasto sinistro del mouse e raggiungiamo la riga 00401D60, se osserviamo bene la riga 00401D57 può essere raggiunta dal JUMP che si trova alla riga 00401CAE:


00401CAE JMP DS:[ECX*4+401E44]

Come si può notare questo è un JUMP particolare dato che il salto cambia a seconda del valore contenuto in ECX, ad esempio se ECX vale 0 otteniamo (0*4) + 401E44 = 00401E44 e quindi si va a prendere l'indirizzo contenuto alla locazione 00401E44. Quindi cosa dobbiamo fare??? Bhe dobbiamo piazzare un breakpoint alla riga 00401CAE, avviare l'applicazione, inserire il Nome e il Serial e confermare, in modo tale da raggiungere l'istruzione desiderata. Poi dobbiamo osservare la memoria a partire dalla locazione 00401E44 in cerca dell'indirizzo 00401D57 che in memoria è rappresentato come 57 1D 40 00. Trovato niente??? La locazione 00401E50 contiene proprio quanto cercato!!! Che valore deve contenere ECX per ottenere 00401E50??? Bhe se la matematica non ci inganna ECX dovrà contenere il valore 3.
Ora bisogna capire quali istruzioni gestiscono il valore di ECX prima del JUMP, sono proprio li vicino:


00401CA6 XOR ECX,ECX // azzera ECX

00401CA8 MOV CL,DS:[EAX+401E60] // mette in CL il valore contenuto alla
// locazione data da EAX+401E60

Osserviamo la locazione di memoria 00401E60 come abbiamo fatto in precedenza alla ricerca del valore 3 in modo tale da capire che valore dovrà contenere ECX per inizializzare in modo corretto CL. Il valore 03 si trova alla locazione di memoria 00401E64 quindi EAX dovrà contenere il valore 4. Ora non ci resta che capire chi inizializza EAX.


00401C90 MOV EAX,SS:[ESP+4] // inizializza EAX
00401C94 PUSH EBX
00401C95 ADD EAX,-2 // sottrae 2 a EAX

Bene da queste righe di codice si capisce che se vogliamo EAX = 4 allora EAX deve essere inizializzato con il valore 6!!! Piazziamo un breakpoint alla riga 00401C90 e osserviamo il valore che inizializza EAX. Spettacolare!!! Semplicemente Fantastico!!! :-D EAX viene inizializzata con tutti i valori da 1 a 7 fatta eccezzione del valore 6. La sfortuna si è abbattuta su questa casa come nella 720a puntata di Falcon Crest :-D
La cosa che ci si deve chiedere è chi inizializza la locazione 0012FBE0??? Piazziamo allora un bel Breakpoint on memory write sulla locazione 0012FBE0 dopo aver breakato alla riga 00401C90 e premiamo il tasto Play ma non funziona in quanto il programma genera un'eccezione che lo fa terminare bruscamente. Come mai??? Beh una possibile colpa la si può assegnare ai Thread!!!
Un thread è una parte del processo che ha sempre almeno un thread (sè stesso), ma in alcuni casi un processo può avere più thread che vengono eseguiti in parallelo. Per comprendere meglio questo concetto pensate ad una fune composta da vari fili attorcigliati: se la fune è il processo in esecuzione, allora i singoli fili che la compongono sono i thread.
Non ci resta che attivare una nuova funzionalità di Ollydbg, andiamo in Options->Debugging options e selezioniamo l'etichetta Events e mettiamo una spunta su Break on new thread e premiamo il pulsante OK. Grazie a questa funzione ogni qual volta viene creato un nuovo thread Ollydbg prende il controllo e quindi è possibile seguire il flusso delle istruzioni.
Ragazzi molto probabilmente vi sarà scomparsa la Command Bar di Ollydbg, per risolvere l'inconveniente riducete ad Icona Ollydbg e rivisualizzatelo!!! Adesso che abbiamo settato l'opzione dei Thread possiamo piazzare il Breakpoint on memory write sulla locazione 0012FBE0 come abbiamo fatto in precedenza e cerchiamo l'istruzione che modifica la locazione di memoria inserendo il valore 2. Eccola:


00419E39 PUSH [ARG.2] // 0012FC60 = 00000002

Il valore Pushato si trova nella locazione 0012FC60, quindi adesso diventa questa la locazione da tenere sotto controllo, togliamo il Breakpoint sulla memoria e ritorniamo nella nostra posizione iniziale ovvero quando Ollydbg si ferma sulla riga 00401C90 e la locazione 0012FBE0 contiene il valore 1. Controlliamo il contenuto della locazione 0012FC60, contiene il valore 1 quindi ciò significa che non è stata ancora inizializzata con il valore 2, piazziamo il Breakpoint on memory write su questa locazione e cerchiamo l'istruzione che la inizializza:


00419A11 PUSH [ARG.2] // 0012FC84 = 00000002

Torniamo al punto di partenza e settiamo il breakpoint on memory write sulla locazione 0012FC84.


00418974 PUSH DWORD PTR SS:[EBP+14] // 0012FCEC = 00000002

Torniamo al punto di partenza e settiamo il breakpoint on memory write sulla locazione 0012FCEC.


00418C25 PUSH [ARG.3] // 0012FD04 = 00000002

Torniamo al punto di partenza e settiamo il breakpoint on memory write sulla locazione 0012FD04.


77D1871B PUSH DWORD PTR SS:[EBP+14] // 0012FD34 = 00000002

Torniamo al punto di partenza e settiamo il breakpoint on memory write sulla locazione 0012FD34. Dopo la quinta riesecuzione del programma e relativo break dovremmo ottenere la seguente istruzione:


7E398807 PUSH DWORD PTR SS:[EBP+18] // 0012FDA0 = 00000002

Torniamo al punto di partenza e settiamo il breakpoint on memory write sulla locazione 0012FDA0. Dopo la ventritreesima riesecuzione del programma e relativo break dovremmo ottenere la seguente istruzione:


7E3989B8 PUSH DWORD PTR DS:[EDI] // 00432808 = 00000002

Torniamo al punto di partenza e settiamo il breakpoint on memory write sulla locazione 00432808. In questo caso se proviamo a riavviare l'esecuzione del programma dopo il primo break, il programma termina bruscamente quindi ci fermiamo al primo break.


7E3989E1 MOV DS:[EDI],EAX
7E3989E3 MOV EAX,ECX
7E3989E5 CALL 7E398600
7E3989EA RETN 8

Ora ci tocca steppare con F8 alla ricerca dell'istruzione che inserisce il valore 00000002 all'interno della locazione 00432808, steppiamo quindi tenendo d'occhio tale locazione, bene abbiamo trovato la funzione che carica il valore:


0041B21A PUSH EDI // /RemoveMsg
0041B21B PUSH EDI // |MsgFilterMax
0041B21C PUSH EDI // |MsgFilterMin
0041B21D PUSH EDI // |hWnd
0041B21E PUSH EBP // |pMsg
0041B21F CALL DS:[<&USER32.PeekMessageA>] // \PeekMessageA

PeekMessage può leggere un messaggio senza rimuoverlo dalla coda, a rigor di logica se ci sono messaggi da leggere, allora c'è una funzione che li crea!!! Questa funzione si chiama SendMessageA e ci conviene dare un'occhiata, quindi riavviamo il CrackMe, poi apriamo il menù con con le Intermodular Calls, ordiniamo i nomi delle funzioni utilizzate nel programma e cerchiamo la suddetta funzione. Ve ne sono molte, quindi per non sbagliare settiamo un breakpoint su tutte tramite il comando Set breakpoint on every call to. Ecco qui il primo esempio di messaggio, ogni qual volta tentiamo di utilizzare il CrackMe per poter premere il pulsante di OK, Olly prende il controllo dato che viene attivata la SendMessageA seguente:


00418DD3 PUSH EAX // /lParam
00418DD4 PUSH [ARG.2] // |wParam
00418DD7 PUSH 36E // |Message = MSG(36E)
00418DDC PUSH DWORD PTR DS:[EBX+1C] // |hWnd
00418DDF CALL DS:[<&USER32.SendMessageA>] // \SendMessageA

A noi comunque proprio questa SendMessage non serve perchè abbiamo capito che serve per il focus della finestra, quindi per poter premere il nostro pulsante di OK dobbiamo togliere il breakpoint solo su di essa lasciando inalterati gli altri. Possiamo farlo premendo F2 oppure scegliendo Breakpoint->Toggle. Come si può notare, adesso è possibile utilizzare il CrackMe, allora premiamo il pulsante Ask the Druid. Lo facciamo...........guardate un pò cosa c'è nel registro???? Il nostro username inserito (ValerioSoft)...bene segnamoci la locazione 00940AFC. Premiamo il tasto Play e così compare anche il nostro serial inserito (123456789), anche in questo caso segnamoci la locazione in cui è contenuto 00940BAC. Adesso che conosciamo queste locazioni, riavviamo l'intero CrackMe, inseriamo i dati e premiamo il pulsante, poi andiamo alla locazione 00940AFC e piazziamo un bel breakpoint on memory access e premiamo Play:


004015F5 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]
004015F7 MOV ECX,EAX
004015F9 AND ECX,3
004015FC REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]

Cosa succede??? Beh il nostro username viene copiato nella locazione 00F10000 che una volta inizializzata va tenuta sotto controllo, quindi piazziamo sul nostro username alla locazione appena menzionata un breakpoint on memory access e premiamo il Play.


00401794 XOR ESI,ESI // azzera ESI
00401796 TEST EDI,EDI
00401798 JBE SHORT 004017B6
0040179A MOV ECX,DS:[4328B0] // mette in ECX l'indirizzo che contiene il nome inserito
004017A0 MOV BL,DS:[EAX+EDX] // mette in BL la prima lettera di 00F00000
004017A3 MOV CL,DS:[ESI+ECX] // mette in CL la prima lettera del nome
004017A6 XOR BL,CL // li Xora
004017A8 INC ESI // incrementa ESI
004017A9 MOV DS:[EAX+EDX],BL // memorizza BL in 00F00000
004017AC MOV EDX,DS:[4328AC]
004017B2 CMP ESI,EDI
004017B4 JB SHORT 0040179A // salta se non sono state elaborate tutte le lettere
004017B6 CMP EBP,EDI
004017B8 JNZ SHORT 004017BC
004017BA XOR EBP,EBP
004017BC MOV ECX,DS:[4328B0]
004017C2 MOV BL,DS:[EAX+EDX]
004017C5 MOV CL,SS:[EBP+ECX]
004017C9 XOR BL,CL // Xora nuovamente con la prima lettera del nome (poi la succ)
004017CB INC EBP // incrementa EBP
004017CC MOV DS:[EAX+EDX],BL
004017CF MOV EDX,DS:[4328AC]
004017D5 INC EAX // incrementa EAX
004017D6 CMP BYTE PTR DS:[EAX+EDX],0
004017DA JNZ SHORT 00401794

Cosa fà??? Beh trasforma la frase contenuta alla locazione 00F00000:


00F00000 54 68 69 73 20 69 73 20 6D 79 20 66 69 72 73 74 This is my first
00F00010 20 43 72 61 63 6B 4D 65 2C 20 45 6E 6A 6F 79 21 CrackMe, Enjoy!
00F00020 20 65 2D 6D 61 69 6C 3A 20 62 6C 61 63 6B 5F 64 e-mail: black_d
00F00030 72 75 69 64 40 68 6F 74 6D 61 69 6C 2E 63 6F 6D ruid@hotmail.com

in questa maniera:


00F00000 66 6D 61 72 36 64 78 17 66 7B 30 54 6C 7A 72 62 fmar6dx.f{0Tlzrb
00F00010 2D 48 45 6A 61 7B 7F 60 24 21 53 63 61 58 72 23 -HEja{.`$!ScaXr#
00F00020 30 57 28 65 60 7F 61 31 17 69 6E 71 51 6E 57 65 0W(e`.a1.inqQnWe
00F00030 64 78 62 53 4B 6A 7F 46 68 69 68 7A 23 68 58 66 dxbSKj.Fhihz#hXf

Ora facciamo lo stesso per quanto riguarda la locazione del serial inserito, ci accorgiamo che il serial viene copiato nella locazione 00F20000.


004015C5 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]
004015C7 MOV ECX,EDX
004015C9 PUSH 0
004015CB AND ECX,3
004015CE PUSH 0
004015D0 REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]

Ora mettiamo un bel breakpoint on memory access alla locazione 00F20000 su tutto il serial inserito. Premiamo Play diverse volte fino ad ottenere questo pezzo di codice, abbiamo trovato senza neanche volerlo l'ultima parte dell'algoritmo che si occupa di confrontare il serial inserito con il vero serial generato sulla base dello username inserito. Il vero serial si trova alla locazione 00EF0000 (ecco perchè BlackDruid diceva che era semplice trovare il serial) e vi dico di più, se guardate ancora meglio in ESI c'è il valore 6 e se il confronto non va a buon fine viene messo il valore 7.


00401C2F REPNE SCAS BYTE PTR ES:[EDI] // Conta il numero di lettere che compongono il serial
// esatto decrementando ECX<br />
00401C31 NOT ECX // Nega ECX e ottiene il numero di caratteri + 1 del
// Serial esatto<br />
00401C33 DEC ECX // Decrementa ECX
00401C34 MOV ESI,6 // Mette il valore 6 (desiderato) in ESI
00401C39 TEST CX,CX
00401C3C JBE SHORT 00401C61 // Non salta perchè CX contiene il numero di caratteri
00401C3E MOV EAX,DS:[4328A8] // Mette in EAX l'indirizzo del serial inserito da me
00401C43 PUSH EBX
00401C44 SUB EDX,EAX // sottrae all'indirizzo di EDX il valore di EAX
00401C46 AND ECX,0FFFF
00401C4C MOV EDI,ECX // mette il numero di caratteri in EDI
00401C4E MOV CL,DS:[EDX+EAX] // Mette in CL la prima lettera del serial giusto (al primo giro)
00401C51 MOV BL,DS:[EAX] // Mette in BL la prima lettera del serial inserito (al primo giro)
00401C53 CMP CL,BL // Confronta le due lettere
00401C55 JE SHORT 00401C5C // Salta se sono uguali
00401C57 MOV ESI,7 // Se uno dei confronti non va bene allora in ESI
// mette il valore 7<br />
00401C5C INC EAX // Incrementa EAX
00401C5D DEC EDI // Decrementa EDI
00401C5E JNZ SHORT 00401C4E // Salta per controllare tutto il serial

Nel nostro caso il confronto tra i serial non va a buon fine, guardate qualche riga più sotto cosa c'è:


00401C6D PUSH 0 // /lParam = 0
00401C6F PUSH ESI // |wParam = 7
00401C70 PUSH 464 // |Message = MSG(464)
00401C75 PUSH EDX // |hWnd => 1304DA
00401C76 CALL DS:[<&USER32.PostMessageA>] // \PostMessageA

Questa è la funzione che crea i messaggi, in questo caso con il parametro 7, quindi cosa si deduce???? Beh è semplice, basta mettere un bel breakpoint su questo tipo di funzione per trovare le restanti parti dell'algoritmo che generano il serial. Allora riavviamo l'intero CrackMe, inseriamo i dati e chiediamo al Druido, poi apriamo le Intermodular Calls e piazziamo i breakpoint sulla funzione PostMessageA e continuiamo a premere Play fino a quando Olly si ferma sulla funzione, questa è la prima e si riferisce al pezzo di codice che trasforma la frase contenuta alla locazione 00F00000.


004017EE PUSH 0 // /lParam = 0
004017F0 PUSH EAX // |wParam = 1
004017F1 PUSH 464 // |Message = MSG(464)
004017F6 PUSH EDX // |hWnd => 1504DA
004017F7 MOV DS:[4328B8],EAX // |
004017FC CALL DS:[<&USER32.PostMessageA>] // \PostMessageA

Possiamo quindi proseguire:


004018C6 PUSH 0 // /lParam = 0
004018C8 MOV DS:[4328B8],EAX // |
004018CD PUSH EAX // |wParam = 2
004018CE MOV EAX,DS:[432890] // |
004018D3 PUSH 464 // |Message = MSG(464)
004018D8 PUSH EAX // |hWnd => 1504DA
004018D9 CALL DS:[<&USER32.PostMessageA>] // \PostMessageA

Se osserviamo le righe di codice precedenti a queste troviamo un'altro pezzo di algoritmo:


00401866 XOR EAX,EAX
00401868 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI] // mette in 00EE0000 la stringa
// da convertire<br />
0040186A MOV EDX,DS:[4328B0] // mette in EDX la locazione della nome inserito
00401870 MOV CL,DS:[EDX+3] // mette in CL la quarta lettera del nome
00401873 MOV EDX,DS:[432894] // EDX = 00EE0000
00401879 MOV BL,DS:[EAX+EDX] // mette in BL la prima lettera della memoria
0040187C XOR BL,7B // la Xora con 7B
0040187F MOV DS:[EAX+EDX],BL
00401882 MOV EDX,DS:[432894]
00401888 MOV BL,DS:[EAX+EDX]
0040188B ADD BL,12 // somma 12 a BL
0040188E MOV DS:[EAX+EDX],BL
00401891 MOV EDX,DS:[432894]
00401897 MOV BL,DS:[EAX+EDX]
0040189A XOR BL,CL // Xora BL con CL
0040189C ADD CL,6 // alla lettera del nome aggiunge 6
0040189F MOV DS:[EAX+EDX],BL // salva BL in memoria
004018A2 INC EAX // incrementa EAX
004018A3 CMP EAX,20 // confronta EAX con 20
004018A6 JB SHORT 00401873 // salta se EAX è più piccolo

Cosa fa??? Mette alla locazione 00EE0000 la stringa:


00EE0000 42 6C 61 63 6B 44 72 75 69 44 20 69 73 20 63 61 BlackDruiD is ca
00EE0010 73 74 69 6E 67 20 61 20 73 70 65 6C 6C 21 21 21 sting a spell!!!

e la converte sulla base del nome inserito:


00EE0000 2E 42 5D 5D 5F D2 92 AF B1 CA CC 83 B7 DE 93 93 .B]]_ò.¯±Êì..Þ..
00EE0010 DF EA F5 F0 F3 8E C5 82 EF E6 31 2E 24 7F 75 73 ßêõðóŽÅ.ïæ1.$.us

Proseguiamo: (1a conversione)


00401961 MOV ESI,DS:[4328A4] // mette in ESI l'indirizzo 00EF0000 che contiene il nome inserito
00401967 MOV EDX,DS:[4328AC] // mette in EDX l'indirizzo 000F0000
0040196D MOV DL,DS:[ECX+EDX] // mette un carattere in DL preso da 000F0000 e successivi
00401970 MOV BL,DS:[EAX+ESI] // mette in BL un carattere del nome inserito
00401973 XOR BL,DL // li Xora e mette il risultato in BL
00401975 MOV DS:[EAX+ESI],BL // mette il risultato a posto del carattere del nome
00401978 MOV EDX,DS:[4328A4] // EDX = 00EF0000
0040197E MOV BL,DS:[EAX+EDX]
00401981 ADD BL,2B // somma al risultato 2B
00401984 MOV DS:[EAX+EDX],BL // mette il risultato a posto del carattere del nome
00401987 MOV ESI,DS:[4328A4] // ESI = 00EF0000
0040198D MOV EDX,DS:[4328AC] // EDX = 000F0000
00401993 MOV BL,DS:[EAX+ESI]
00401996 MOV DL,DS:[ECX+EDX] // mette un carattere in DL preso da 000F0000 e successivi
00401999 XOR BL,DL // li Xora e mette il risultato in BL
0040199B INC ECX // incrementa ECX
0040199C CMP ECX,40 // Confronta ECX con 40
0040199F MOV DS:[EAX+ESI],BL // salva il valore di BL in 00EF000 e successivi
004019A2 JB SHORT 00401961 // Salta se ECX è minore 40
004019A4 INC EAX // incrementa EAX per prendere l'altro carattere del nome inserito
004019A5 CMP EAX,EBP // confronta EAX con il numero di caratteri del nome
004019A7 JB SHORT 0040195F // salta finchè non ha controllato tutti i caratteri del nome
004019A9 POP EBX
004019AA MOV EAX,DS:[4328B8]
004019AF PUSH 0 // /lParam = 0
004019B1 INC EAX // |
004019B2 MOV DS:[4328B8],EAX // |
004019B7 PUSH EAX // |wParam = 3
004019B8 MOV EAX,DS:[432890] // |
004019BD PUSH 464 // |Message = MSG(464)
004019C2 PUSH EAX // |hWnd => 1504DA
004019C3 CALL DS:[<&USER32.PostMessageA>] // \PostMessageA

Proseguiamo: (2a conversione)


00401A25 MOV ESI,DS:[4328B0] // ESI = 00F10000 nome inserito
00401A2B MOV EDI,DS:[43289C] // EDI = 1030000
00401A31 MOV ECX,8
00401A36 XOR EAX,EAX
00401A38 TEST EBP,EBP
00401A3A REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI] // mette il nome inserito in 1030000
00401A3C JBE SHORT 00401A8A
00401A3E PUSH EBX
00401A3F XOR ECX,ECX // azzera ECX
00401A41 MOV ESI,DS:[43289C] // ESI = 1030000
00401A47 MOV EDX,DS:[432894] // EDX = 00EE0000
00401A4D MOV DL,DS:[ECX+EDX] // mette in DL il valore preso da 00EE0000 e successivi
00401A50 MOV BL,DS:[EAX+ESI] // mette in ESI la prima lettera del nome inserito
00401A53 XOR BL,DL // Li Xora e mette il risultato in BL
00401A55 MOV DS:[EAX+ESI],BL // memorizza il risultato in 1030000 e successivi
00401A58 MOV EDX,DS:[43289C] // EDX = 1030000
00401A5E MOV BL,DS:[EAX+EDX]
00401A61 ADD BL,0D1 // somma a BL il valore D1
00401A64 MOV DS:[EAX+EDX],BL // memorizza il risultato in 1030000 e successivi
00401A67 MOV ESI,DS:[43289C]
00401A6D MOV EDX,DS:[432894] // EDX = 00EE0000
00401A73 MOV BL,DS:[EAX+ESI]
00401A76 MOV DL,DS:[ECX+EDX] // mette in DL il valore preso da 00EE0000 e successivi
00401A79 XOR BL,DL // li Xora e mette il risultato in BL
00401A7B INC ECX // incrementa ECX
00401A7C CMP ECX,20 // confronta ECX con 20 Hex
00401A7F MOV DS:[EAX+ESI],BL // memorizza il valore di BL in 1030000
00401A82 JB SHORT 00401A41 // itera ogni volta per 32 volte
00401A84 INC EAX // incrementa EAX
00401A85 CMP EAX,EBP // confronta EAX con il numero di caratteri del serial
00401A87 JB SHORT 00401A3F // itera in base al numero di caratteri del serial
00401A89 POP EBX
00401A8A MOV EAX,DS:[4328B8]
00401A8F PUSH 0 // /lParam = 0
00401A91 INC EAX // |
00401A92 MOV DS:[4328B8],EAX // |
00401A97 PUSH EAX // |wParam = 4
00401A98 MOV EAX,DS:[432890] // |
00401A9D PUSH 464 // |Message = MSG(464)
00401AA2 PUSH EAX // |hWnd => 1504DA
00401AA3 CALL DS:[<&USER32.PostMessageA>] // \PostMessageA

Proseguiamo: (3a conversione)


00401AD1 MOV EDX,DS:[4328A4] // mette l'indirizzo 00EF0000 in EDX
00401AD7 MOV EDI,EBP
00401AD9 AND EDI,0FFFF
00401ADF MOV ECX,DS:[43289C] // ECX = 1030000
00401AE5 MOV BL,DS:[EDX+EAX] // mette in BL la prima lettera
// (e successive) del nome crittata (00EF0000)<br />
00401AE8 MOV CL,DS:[ECX+EAX] // mette in CL la prima lettera
// (e successive) della locazione 1030000<br />
00401AEB XOR BL,CL // li Xora e mette il risultato in BL
00401AED MOV DS:[EDX+EAX],BL // salva il risultato in 00EF0000
00401AF0 MOV EDX,DS:[4328A4]
00401AF6 MOV CL,DS:[EDX+EAX] // mette in CL la lettera alla locazione 00EF0000
00401AF9 LEA ESI,DS:[EDX+EAX] // ESI = 00EF0000
00401AFC CMP CL,30 // confronta CL con 30
00401AFF JNB SHORT 00401B19 // salta se CL è più grande
00401B01 ADD CL,7 // somma 7 a CL
00401B04 MOV DS:[ESI],CL // salva CL a 00EF0000 (e succesive)
00401B06 MOV EDX,DS:[4328A4]
00401B0C MOV CL,DS:[EDX+EAX]
00401B0F LEA ESI,DS:[EDX+EAX]
00401B12 CMP CL,30 // confronta CL con 30
00401B15 JB SHORT 00401B01 // salta se CL è più piccolo
00401B17 JMP SHORT 00401B34 // salta per analizzare le prossime lettere
00401B19 CMP CL,39 // confronta CL con 39
00401B1C JBE SHORT 00401B34 // salta se CL è più piccolo o uguale
00401B1E SUB CL,5 // sottrae 5 a CL
00401B21 MOV DS:[ESI],CL // memorizza il risultato in 00EF0000
00401B23 MOV EDX,DS:[4328A4]
00401B29 MOV CL,DS:[EDX+EAX]
00401B2C LEA ESI,DS:[EDX+EAX]
00401B2F CMP CL,39 // confronta CL con 39
00401B32 JA SHORT 00401B1E // salta se CL è più grande
00401B34 INC EAX // incrementa EAX
00401B35 DEC EDI // decrementa EDI
00401B36 JNZ SHORT 00401ADF
00401B38 MOV ESI,DS:[4328B0]
00401B3E TEST BP,BP
00401B41 JBE SHORT 00401BAA
00401B43 MOV EDI,EBP
00401B45 XOR EAX,EAX
00401B47 AND EDI,0FFFF
00401B4D MOV DL,DS:[EDX+EAX] // mette in DL la prima lettera di 00EF0000
00401B50 MOV CL,DS:[ESI+EAX] // mette in CL la prima lettera del nome
00401B53 XOR CL,DL // li Xora e mette il risultato in CL
00401B55 MOV DS:[ESI+EAX],CL // salva il risultato in 00F10000
00401B58 MOV ESI,DS:[4328B0]
00401B5E MOV CL,DS:[ESI+EAX]
00401B61 LEA EDX,DS:[ESI+EAX] // EDX = 00F10000
00401B64 CMP CL,30 // Confronta CL con 30
00401B67 JNB SHORT 00401B85 // salta se CL è più grande
00401B69 MOV BL,CL
00401B6B MOV ECX,EDX
00401B6D ADD BL,6
00401B70 MOV DS:[ECX],BL
00401B72 MOV ESI,DS:[4328B0]
00401B78 MOV BL,DS:[ESI+EAX]
00401B7B LEA ECX,DS:[ESI+EAX]
00401B7E CMP BL,30
00401B81 JB SHORT 00401B6D
00401B83 JMP SHORT 00401BA0
00401B85 CMP CL,39 // confronta CL con 39
00401B88 JBE SHORT 00401BA0 // non salta se CL è più grande
00401B8A SUB CL,4 // sottrae 4 a CL
00401B8D MOV DS:[EDX],CL // salva CL in 00F10000 e successivi
00401B8F MOV ESI,DS:[4328B0]
00401B95 MOV CL,DS:[ESI+EAX]
00401B98 LEA EDX,DS:[ESI+EAX]
00401B9B CMP CL,39 // confronta CL con 39
00401B9E JA SHORT 00401B8A // salta se CL è più grande
00401BA0 MOV EDX,DS:[4328A4] // EDX = 00EF0000
00401BA6 INC EAX // incrementa EAX
00401BA7 DEC EDI // decrementa EDI
00401BA8 JNZ SHORT 00401B4D // itera per tutte le lettere del nome in 00F10000
00401BAA MOV ECX,EBP
00401BAC AND ECX,0FFFF
00401BB2 TEST BP,BP
00401BB5 MOV BYTE PTR DS:[EDX+ECX],0
00401BB9 JBE SHORT 00401BF7
00401BBB MOV EDX,DS:[4328A4]
00401BC1 XOR EAX,EAX
00401BC3 MOV ESI,ECX // mette in ECX il numero dei caratteri del nome
00401BC5 MOV ECX,DS:[4328B0] // ECX 00F10000
00401BCB MOV BL,DS:[EDX+EAX] // mette in BL la prima lettera (e succ.) di 00EF0000
00401BCE MOV CL,DS:[ECX+EAX] // mette in CL la prima lettera (e succ.) di 00F10000
00401BD1 SUB CL,30 // sottrae 30 a CL
00401BD4 ADD BL,CL // somma BL e CL
00401BD6 MOV DS:[EDX+EAX],BL // salva BL in 00EF0000
00401BD9 MOV EDX,DS:[4328A4]
00401BDF MOV CL,DS:[EDX+EAX] // mette in CL la lettera in 00EF0000
00401BE2 CMP CL,39 // confronta CL con 39
00401BE5 JBE SHORT 00401BF3 // salta se CL è più piccolo o uguale
00401BE7 SUB CL,0A // sottrae 0A a CL
00401BEA MOV DS:[EDX+EAX],CL // salva CL in 00EF0000
00401BED MOV EDX,DS:[4328A4]
00401BF3 INC EAX // incrementa EAX
00401BF4 DEC ESI // decrementa ESI
00401BF5 JNZ SHORT 00401BC5 // itera per tutte le lettere in 00EF0000
00401BF7 MOV EDX,DS:[432890]
00401BFD PUSH 0 // /lParam = 0
00401BFF PUSH 5 // |wParam = 5
00401C01 PUSH 464 // |Message = MSG(464)
00401C06 PUSH EDX // |hWnd => 1504DA
00401C07 CALL DS:[<&USER32.PostMessageA>] // \PostMessageA

Ora che tutti i pezzi del Puzzle sono al proprio posto non ci resta che editare un bel KeyGen.


Il KeyGen

Il Keygen in linguaggio C.


&#35;include <stdio.h>
&#35;include <string.h>
&#35;include <stdlib.h>

int registroAL; // Serve per ottenere il valore contenuto in AL
// NB: Deve essere GLOBALE

int main()
{
char nome[20];
char mappa1[] = "This is my first CrackMe, Enjoy! e-mail: black_druid@hotmail.com";
char mappa2[] = "BlackDruiD is casting a spell!!!";

int * nomeCrit1; // Nome Crittato dopo la prima conversione
int * nomeCrit2; // Nome Crittato dopo la seconda conversione

unsigned int i,j,k=0;
int lettera;

printf("\n\n\nKeygen CrackMe N.27 coded by ValerioSoft\n\n");
printf("Inserisci il nome: ");
scanf("%s", nome);

printf("\nnumero lettere: %d\n ",strlen(nome));

// Prepara Mappa1[]
for(i=0; i<64; i++) {
for(j=0; j<strlen(nome); j++) {
mappa1[i] = mappa1[i] ^ nome[j];
}
if (k == strlen(nome)) k = 0;
mappa1[i] = mappa1[i] ^ nome[k];
k++;
}

// Prepara Mappa2[]
lettera = nome[3];
for(i=0; i<32; i++) {
mappa2[i] = mappa2[i] ^ 0x7B;
mappa2[i] += 0x12;
mappa2[i] = mappa2[i] ^ lettera;
lettera += 0x06;
}

// alloca dinamicamente la memoria per nomeCrit1
nomeCrit1 = (int *) malloc((strlen(nome)) * sizeof(int));
if (!nomeCrit1) {
printf("\nErrore di allocazione della memoria\n");
exit(1);
}

// Prima Conversione
for(i=0; i<strlen(nome); i++) {
*(nomeCrit1 + i) = nome[i];
for(j=0; j < 64; j++) {
*(nomeCrit1 + i) = *(nomeCrit1 + i) ^ mappa1[j]; // Xor
*(nomeCrit1 + i) = *(nomeCrit1 + i) + 0x2b;
*(nomeCrit1 + i) = *(nomeCrit1 + i) ^ mappa1[j]; // Xor
}
registroAL = *(nomeCrit1 + i);
__asm("mov _registroAL,%al"); // sintassi AT&T, non INTEL
// DIPENDE DAL COMPILATORE
// mette il valore in AL
registroAL = 0;
__asm("mov %al,_registroAL"); // sintassi AT&T, non INTEL
// DIPENDE DAL COMPILATORE
// mette nella variabile il
// valore di AL
*(nomeCrit1 + i) = registroAL;
}

// alloca dinamicamente la memoria per nomeCrit2
nomeCrit2 = (int *) malloc((strlen(nome)) * sizeof(int));
if (!nomeCrit2) {
printf("\nErrore di allocazione della memoria\n");
exit(1);
}

// Seconda Conversione
for(i=0; i<strlen(nome); i++) {
*(nomeCrit2 + i) = nome[i];
for(j=0; j < 32; j++) {
*(nomeCrit2 + i) = *(nomeCrit2 + i) ^ mappa2[j]; // Xor
*(nomeCrit2 + i) = *(nomeCrit2 + i) + 0xD1;
*(nomeCrit2 + i) = *(nomeCrit2 + i) ^ mappa2[j]; // Xor
}
registroAL = *(nomeCrit2 + i);
__asm("mov _registroAL,%al"); // sintassi AT&T, non INTEL
// DIPENDE DAL COMPILATORE
// mette il valore in AL
registroAL = 0;
__asm("mov %al,_registroAL"); // sintassi AT&T, non INTEL
// DIPENDE DAL COMPILATORE
// mette nella variabile il
// valore di AL
*(nomeCrit2 + i) = registroAL;
}
// mostra la prima conversione
printf("\n\n1a conversione: ");
for(i=0;i < strlen(nome); i++) printf("%d ",*(nomeCrit1+i));

// mostra la seconda conversione
printf("\n\n2a conversione: ");
for(i=0;i < strlen(nome); i++) printf("%d ",*(nomeCrit2+i));

// Terza Conversione (3A)
for(i=0; i<strlen(nome); i++) {
*(nomeCrit1 + i) = *(nomeCrit1 + i) ^ *(nomeCrit2 + i); // Xor
if (*(nomeCrit1 + i) < 0x30) {
do {
*(nomeCrit1 + i) += 0x07;
} while(*(nomeCrit1 + i) < 0x30);
}
else {
if (*(nomeCrit1 + i) > 0x39) {
do {
*(nomeCrit1 + i) -= 0x05;
} while(*(nomeCrit1 + i) > 0x39);
}
}
}

// Inizializzo il vettore nomeCrit2 per la conversione 3B
for(i=0; i<strlen(nome); i++) {
*(nomeCrit2 + i) = *(nomeCrit1 + i);
}

for(i=0; i<strlen(nome); i++) {
*(nomeCrit2 + i) = *(nomeCrit2 + i) ^ nome[i]; // Xor
if (*(nomeCrit2 + i) < 0x30) {
do {
*(nomeCrit2 + i) += 0x06;
} while(*(nomeCrit2 + i) < 0x30);
}
else {
if (*(nomeCrit2 + i) > 0x39) {
do {
*(nomeCrit2 + i) -= 0x04;
} while(*(nomeCrit2 + i) > 0x39);
}
}
}

// mostra la terza conversione
printf("\n\n3a conversione A: ");
for(i=0;i < strlen(nome); i++) printf("%d ",*(nomeCrit1+i));

printf("\n\n3a conversione B: ");
for(i=0;i < strlen(nome); i++) printf("%d ",*(nomeCrit2+i));

// Conversione finale
for(i=0; i<strlen(nome); i++) {
*(nomeCrit2 + i) -= 0x30;
*(nomeCrit1 + i) += *(nomeCrit2 + i);
if (*(nomeCrit1 + i) > 0x39) {
*(nomeCrit1 + i) -= 0x0A;
}
*(nomeCrit1 + i) -= 0x30;
}

printf("\n\nIl serial : ");
for(i=0;i < strlen(nome); i++) printf("%d ",*(nomeCrit1+i));
printf("\n\n");

// attende un carattere qualsiasi in ingresso
scanf("%s", nome);

// libera la memoria allocata
free(nomeCrit1);
free(nomeCrit2);
return 0;
}

Anche questa fatica è fatta!!! :-D

Visto che abbiamo il KeyGen non ci resta che trovare il serial giusto per Quequero che è 37337365

Complimenti a BlackDruid per il CrackMe che ci ha fatto veramente sudare ma ne è valsa la pena!!! :-D



Note Finali

Un saluto caloroso a va a tutta la UIC!!!
Ciaoooooooooooooooooo alla proximaaaaaaaaa

ValerioSoft


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 malevole 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.