Phoenix1 CrackMe
(Decrypt by Ida)

Data

by x86

 

14/01/2004

UIC's Home Page

Published by Quequero


Bighe dùre ... bighe sìgure!

Grazie xuxxo :)

E le famee? Dùt Ben? Buse i fruts!

....

E-mail: [email protected]
Nick x86, UIN, canale IRC/EFnet frequentato #Crack-it #asm

....

Difficoltà

( )NewBies (x)Intermedio ( )Avanzato ( )Master

 

Avevo letto letto sulle news della UIC “crackme incrackabile :>” e così un po’ preso dalla curiosità gli ho dato un’occhio


Phoenix1 CrackMe
Decrypt by Ida
Written by x86

Introduzione


Un CrackMe molto bello! E molto interessante per iniziare a capire molto cose sulle possibilità che ci può offrire il linguaggio l’assembler!

Tools usati

Tools usati:
 
Ida (basta e avanza)
 
OllyDbg (se volete controllare qlcosa)

URL o FTP del programma

Lo trovate sul sito della UIC, nella sezione CrackMe;

Notizie sul programma

Notizie ??? L’autore del programma ci dice di creare un KeyGen … quindi andiamo ad analizzare il codice …

Essay

La prima cosa che possiamo fare (io a dire la verità mi ero dimenticato…) è quella di prendere il PeEditor e caricare il crackme, per dare subito un'okkiata a quali stranezze possiammo trovarci di fronte! Una volta aperto, notiamo subito che la prima section ha come caratteristiche E0000020 mmmh la prima cosa dobbiamo pensare è … come mai ha anche la caratteristica "writable" ??? possiamo già supporre di trovarci di fronte a codice Smc o qualche sezione di codice criptato, atto a nascondere qlcosa, dato che dubito assai venga settato così a casaccio! (N.b. Non deve essere per forza settato la caratteristica "writable" per aspettarci questi tricks, il codice potrebbe essere settato "writable" direttamente con L'Api VirtualProtect.
Allora, a questo punto possiamo prendere Ida e caricare il crackme per dare un'okkiata al codice più da vicino! Un'altra cosa strana, che notiamo appena Ida termina di dissasemblare il crackme, che l'entrypoint punta all'indirizzo 004012A8, lasciandoci indietro una bella zona di codice! Mmmh le cose si fanno strane, anche perché se andiamo all'inizio del codice, noteremo una zona di codice senza senso, quindi è sicuramente cryptato, un'altra cosa che ci può dare conferma è la procedura proprio all'inzio del codice, che a una rapida okkiata ci fa notare il caricamento degli estremi della zona cryptata … quindi è la call di decript di quella zona! Prima di applicarci su questa zona, andiamo avanti da dove siamo arrivati con Ida, al caricamento del crackme e precisamente all'indirizzo 004012A8 dove vediamo la creazione della finestra del crackme:


.text:004012A8 call InitCommonControls
.text:004012AD push 0 ; lpModuleName
.text:004012AF call GetModuleHandleA
.text:004012B4 mov hInstance, eax
.text:004012B9 push 0 ; dwInitParam
.text:004012BB push offset DialogFunc ; lpDialogFunc
.text:004012C0 push 0 ; hWndParent
.text:004012C2 push 3E8h ; lpTemplateName
.text:004012C7 push hInstance ; hInstance
.text:004012CD call DialogBoxParamA
.text:004012D2 push eax ; uExitCode
.text:004012D3 call ExitProcess


subito sotto troviamo la Dialog Box procedure con la quale vengono gestiti i messaggi!


.text:004012F5 loc_4012F5: ; CODE XREF: DialogFunc+7j
.text:004012F5 cmp [ebp+arg_4], WM_INITDIALOG
.text:004012FC jnz loc_4013B9
.text:00401302 push 1 ; lpIconName
.text:00401304 push hInstance ; hInstance
.text:0040130A call LoadIconA
.text:0040130F push eax ; lParam
.text:00401310 push 0 ; wParam
.text:00401312 push WM_SETICON ; WM_SETICON
.text:00401317 push [ebp+hWnd] ; hWnd
.text:0040131A call SendMessageA
.text:0040131F call Algo_Decrypt_Zone
.text:00401324 call Return_Addrress_API


Andiamo a vedere cosa accade subito dopo la gestione del messaggio WM_INITDIALOG, viene settata l'icone della applicazione,e poi troviamo una serie di call (molto sospette!!! Per i miei gusti!, faccio notare che le call, sono state tutte rinominate con dei nomi che identificano la funzione che eseguono, così possiamo capire da subito cosa fanno o se vengono richiamate in altre zone del codice), iniziamo con entrare nella prima (call Algo_Decrypt_zone):


.text:00401000 Algo_Decrypt_Zone proc near ; CODE XREF: DialogFunc+47p
.text:00401000 xor ecx, ecx
.text:00401002
.text:00401002 loc_401002: ; CODE XREF: sub_401000+1Fj
.text:00401002 mov bl, ds:byte_401023[ecx]
.text:00401008 xor bl, 21h
.text:0040100B mov ds:byte_401023[ecx], bl
.text:00401011 inc ecx
.text:00401012 mov edi, offset byte_401023
.text:00401017 add edi, ecx
.text:00401019 cmp edi, offset byte_4011D7
.text:0040101F jbe short loc_401002
.text:00401021 retn
.text:00401021 Algo_Decrypt_Zone endp


eccoci qua, molto semplice, questa call nn fa altro che decriptare una zona di codice che guarda cosa lo si può trovare subito dopo questo codice!


.text:00401023 byte_401023 db 71h ; DATA XREF: sub_401000+2r
.text:00401023 ; sub_401000+Bw ...
.text:00401024 dd 4F444E49h, 21105948h
.text:0040102C dword_40102C dd 44534F74h, 55524846h, 45445344h
.text:0040102C ; DATA XREF: DialogFunc+96o
.text:00401038 db 21h
.text:00401039 dd 48464473h, 53445552h, 75214544h, 4F014449h, 1444C40h
.text:00401039 ; DATA XREF: sub_401483+C6o
.text:00401039 dd 48015248h, 4D40574Fh, 10F4548h, 40444D71h, 42014452h
.text:00401039 dd 4A424449h, 554801h, 44497521h, 53445201h, 14D4048h
.text:00401039 dd 48015248h, 534E424Fh, 55424453h, 4D71010Fh, 44524044h
....................
.................... codice tagliato
....................
.text:0040118F dd 4E4B0145h, 44530143h, 52534457h, 21005344h
.text:004011C7 dword_4011C7 dd 4D404865h, 4E63464Eh, 53407159h, 21604C40h
.text:004011C7 ; DATA XREF: sub_4011DA+B7o
.text:004011D7 byte_4011D7 db 3 dup(90h) ; DATA XREF: sub_401000+19o


Come potete vedere, ho riportato solo gli estremi della zona di codice criptato, come possiamo notare l'algo di decript del codice è molto semplice, non fa altro che prendere un byte alla volta partendo da 0x00401023, xorandolo con il valore 21h, e lo rimette a posto decriptato, incrementa il contatore ecx, per passare al byte successivo, mette in edi, il valore di partenza 0x00401023 e ci somma il valore di ecx dopo di che controlla che edi sia minore dell'indirizzo di fine della zona da decriptare (0x004011D7), se ciò non è vero continua a decriptare altrimenti significa che la zona è stata decriptata completamente e quindi ci fa uscire dalla call;
Quindi per vedere cosa c'è in questa zona direi di crearci un idc file in modo che Ida c'è lo decripti automaticamente, allora apriamo il notepad, e scriviamo il seguente codice :


static decrypt()
{
auto blocco_inizio, buffertemp, i;
blocco_inizio = 0x00401023;
for(i=0; i<436; i++)
{
buffertemp=Byte(blocco_inizio);
buffertemp = buffertemp ^ (0x21);
PatchByte(blocco_inizio, buffertemp);
blocco_inizio=blocco_inizio + 1;
}
}


Questo non è altro che il codice di decrypt scritto in C-like language, beh è molto semplice abbiamo definito alcune variabili tramite "auto", abbiamo definito poi blocco_inizio con il valore 0x00401023 che è l'inidirizzo di partenza della zona di codice da decriptare, abbiamo creato un ciclo con for dove 436 sono il numero di cicli che dovrà fare per decriptare tutta la zona criptata, per trovare questo valore non dobbiamo fare altro che aprire la calcolatrice di windows settarla su Hex calcolarci il delta tra l'inizio e la fine della zona, quindi facciamo 4011D7 - 401023 = 1B4 lo convertiamo in decimale = 436 (infatti in totale ci sono 436 da decriptare :) ), poi come si vede all'interno del ciclo for, si prende il primo byte lo si mette nel buffertemp lo si xora con il valore 0x21 il risultato viene rimesso in buffertemp, dopodiché viene rimesso a posto decriptato con la funzione PatchByte, e poi incrementiamo e andiamo avanti a decriptarci tutti i byte! Bene adesso che abbiamo capito il source in C-like salviamo il file con estensione .idc, io l'ho chiamato decrypt e lo salviamo nella cartella di ida chiamata Idc; Adesso la prima cosa che dobbiamo fare è selezionare tutta la zona di codice decriptato, clicchiamo il tasto destro del mouse e scegliamo la voce undefine vedremo che l'intero codice verra undefinito, adesso andiamo nel menu File -> Idc file .... e carichiamo il file appena creato da noi, il mio è decrypt.idc, comparirà uno nuovo menu in Ida:


Se clicchiamo sul tasto sinistro, ci comparirà notepad con il nostro codice in modo da permetterci di fare qualche correzione, invece con il tasto destro possiamo controllare che il codice da noi scritto sia corretto, che poi funzi è un altro paio di maniche (dato che potremmo avere sbagliato di reversare l'algo di decrypt ), infatti se clicchiamo sul tasto destro, nella finestra sotto di ida, uscirà scritto:
Compiling file 'C:\Ida430a\idc\decrypt.idc'...
Executing function 'main'...
Questo sta a significare che il codice è corretto, altrimenti ci uscira una finestrella indicandoci la riga in cui c'è l'errore! Adesso che abbiamo controllato che il nostro codice è corretto applichiamolo alla zona criptata, allora posizioniamoci con il cursore accanto al primo byte criptato e precisamente davanti a ai caratteri 'db', adesso andiamo nuovamente nel menu di ida File -> IDC command, si aprirà una finestra, scriviamo il nome del nostro file di decrypt e precisamente decrypt(); e diamo OK se abbiamo reversato correttamente l'algo ci usciranno dei caratteri molto famigliari, la prima cosa che noterete è una serie di caratteri che fanno comparire il nome del crackme Phoenix1!!! E poi via via tutte le stringhe delle messagebox di errore ecc!... noterete anche alcuni nomi di funzioni ;) hihihi;
Adesso possiamo mettere a posto il codice decriptato, ci posizioniamo davanti al db del primo byte decriptato e precisamente sul 50h clicchiamo con il tasto destro e scegliamo la voce string che nel menu è identificata come "s", infatti possiamo vedere accanto il nome per intero "Phoenix1", una volta cliccata, comparirà il nome come stringa Ascii per esteso e non indefinita! ..e così faremo per tutte le altre string, noteremo dopo questo che nel codice inizieranno a farsi vedere tutti i riferimenti!! Inoltre se andiamo nella finestra di Ida, chiamata Strings Window e facciamo un bel refresh ci compariranno tutte belle in chiaro!
Una volta fatto ciò usciamo da questa call ed entriamo in quella immediatamente dopo, che io ho chiamato Call Returt_Address_API, bene, adesso che siamo entrati noteremo subito questo!


.text:004011DA Return_Addrress_API proc near ; CODE XREF: DialogFunc+4Cp
.text:004011DA push offset aKernel32 ; lpLibFileName
.text:004011DF call LoadLibraryA
.text:004011E4 mov hModule, eax
.text:004011E9 push offset aUser32 ; lpLibFileName
.text:004011EE call LoadLibraryA
.text:004011F3 mov handle_User, eax
.text:004011F8 push offset aGetprocaddress ; lpProcName
.text:004011FD push hModule ; hModule
.text:00401203 call GetProcAddress
.text:00401208 mov GetProcAddress_imp, eax
.text:0040120D push offset aGetlocaltime ; "GetLocalTime"
.text:00401212 push hModule
.text:00401218 call GetProcAddress_imp
.text:0040121E mov GetLocalTime_im, eax
.text:00401223 push offset aKilltimer ; "KillTimer"
.text:00401228 push handle_User
.text:0040122E call GetProcAddress_imp
.text:00401234 mov KillTimer_imp, eax
.text:00401239 push offset aSettimer ; "SetTimer"
.text:0040123E push handle_User
.text:00401244 call GetProcAddress_imp
.text:0040124A mov SetTimer_imp, eax
.text:0040124F push offset aMessageboxa ; "MessageBoxA"
.text:00401254 push handle_User
.text:0040125A call GetProcAddress_imp
.text:00401260 mov MessageBoxA, eax
.text:00401265 push offset aSenddlgitemmessagea ; "SendDlgItemMessageA"
.text:0040126A push handle_User
.text:00401270 call GetProcAddress_imp
.text:00401276 mov SendDlgItemMessageA_imp, eax
.text:0040127B push offset aWsprintfa ; "wsprintfA"
.text:00401280 push handle_User
.text:00401286 call GetProcAddress_imp
.text:0040128C mov wsprintfA_imp, eax
.text:00401291 push offset aDialogboxparama ; "DialogBoxParamA"
.text:00401296 push handle_User
.text:0040129C call GetProcAddress_imp
.text:004012A2 mov DialogBoxParamA_imp, eax
.text:004012A7 retn
.text:004012A7 Return_Addrress_API endp


Ecco per prima cosa tramite l'API LoadLibraryA mappa il modulo, che nel nostro caso è Kernel32.dll e ritorna in eax l'handle del modulo, fa questo anche per la user.dll che sono i due moduli che gli serviranno poi, per importare le funzioni che verranno utilizzate nel corso del CrackMe! Una volta ottenuto i due handle, importa una ad una tramite l'API GetProcAddress tutte le funzioni, infatti GetProcAddress ci ritorna in eax l'indirizzo delle funzioni importate dalle Dll! Come si nota ogni funzione deve essere importata dalla sua specifica Dll! Una volta salvati gli indirizzi delle funzioni che ci servono, possiamo usare le funzioni tramite call inidirizzo_funzione_imp! Cmq questo lo noteremo andando avanti con lo studio del codice! Questo metodo aiuta il programmatore a nascondere ad occhi inesperti le funzioni che vengono utilizzate dal crackme o programma che sia! Infatti se dissasembliamo questo crackme senza decriptarci la zona delle stringhe e non mettendo un po' a posto il codice rinominando le chiamate agli indirizzi delle funzioni esportate, si vedrebbe questo:


.text:00401329 push offset unk_403000
.text:0040132E push 3
.text:00401330 push 404h
.text:00401335 push 3EAh
.text:0040133A push [ebp+hWnd]
.text:0040133D call dword_40326E
.text:00401343 push [ebp+hWnd]
.text:00401346 call sub_401684
.text:0040134B xor edx, edx
.text:0040134D xor dword_403048, 0Dh
.text:00401354 mov eax, dword_403048
.text:00401359 add eax, dword_40304C
.text:0040135F mov ebx, 2
.text:00401364 div ebx
.text:00401366 add eax, 5
.text:00401369 mov dword_403251, eax
.text:0040136E push offset dword_40102C
.text:00401373 push 2
.text:00401375 push 401h
.text:0040137A push 3EAh
.text:0040137F push [ebp+hWnd]
.text:00401382 call dword_40326E
.text:00401388 push 0
.text:0040138A push 101h
.text:0040138F push 401h
.text:00401394 push 3EAh
.text:00401399 push [ebp+hWnd]
.text:0040139C call dword_40326E
.text:004013A2 push offset sub_401624

infatti non si capisce che funzioni utilizzi! Si vede soltanto call dword_40xxxx ... infatti adesso andiamo avanti guardando il codice rinominato e messo a posto :

.text:00401329 push offset unk_403000
.text:0040132E push 3
.text:00401330 push WM_PSD_GREEKTEXTRECT
.text:00401335 push 3EAh
.text:0040133A push [ebp+hWnd]
.text:0040133D call SendDlgItemMessageA_imp
.text:00401343 push [ebp+hWnd]
.text:00401346 call TIME
.text:0040134B xor edx, edx
.text:0040134D xor dword_403048, 0Dh
.text:00401354 mov eax, dword_403048
.text:00401359 add eax, dword_40304C
.text:0040135F mov ebx, 2
.text:00401364 div ebx
.text:00401366 add eax, 5
.text:00401369 mov number, eax
.text:0040136E push offset aUnregistered ; "Unregistered"
.text:00401373 push 2
.text:00401375 push 401h
.text:0040137A push 3EAh
.text:0040137F push [ebp+hWnd]
.text:00401382 call SendDlgItemMessageA_imp
.text:00401388 push 0
.text:0040138A push 101h
.text:0040138F push 401h
.text:00401394 push 3EAh
.text:00401399 push [ebp+hWnd]
.text:0040139C call SendDlgItemMessageA_imp
.text:004013A2 push offset sub_401624
.text:004013A7 push 3E8h
.text:004013AC push 1
.text:004013AE push [ebp+hWnd]
.text:004013B1 call SetTimer_imp
.text:004013B7 jmp short loc_401433


Una bella differenza direi, adesso si nota chiaramente che funzioni vengono utilizzate!continuiamo la nostra strada ed entriamo nella prossima call che io ho rinominato, chiamandola Call TIME:


.text:00401684 TIME proc near ; CODE XREF: DialogFunc+6Ep
.text:00401684 push ebp
.text:00401685 mov ebp, esp
.text:00401687 push offset Struct_SystemTime
.text:0040168C call GetLocalTime_im
.text:00401692 xor eax, eax
.text:00401694 mov ecx, 0Bh
.text:00401699 mov ax, word_403040 ; Prende la word che contiene il valore hex dell'ora
.text:0040169F mov edi, offset loc_4016B6 ; Muove in Edi l'indirizzo dove poi andare a saltare
.text:004016A4
.text:004016A4 convert_esa_to_string: ; CODE XREF: .text:004016C6j
.text:004016A4 ; .text:004016DDj ...
.text:004016A4 aam ; Divide per 10, e mette il risulato in Ah, e il resto in Al
.text:004016A6 add ah, 30h
.text:004016A9 add al, 30h
.text:004016AB xchg ah, al
.text:004016AD mov word_40300C[ecx], ax
.text:004016B4 jmp edi
.text:004016B4 TIME endp
.text:004016B4
.text:004016B6 ; ---------------------------------------------------------------------------
.text:004016B6
.text:004016B6 loc_4016B6: ; DATA XREF: TIME+1Bo
.text:004016B6 mov ecx, 0Eh
.text:004016BB mov ax, word_403042 ; Prende la word contenente i dati dei minuti
.text:004016C1 mov edi, offset loc_4016C8
.text:004016C6 jmp short convert_esa_to_string
.text:004016C8 ; ---------------------------------------------------------------------------
.text:004016C8
.text:004016C8 loc_4016C8: ; DATA XREF: .text:004016C1o
.text:004016C8 mov ecx, 0
.text:004016CD mov ax, word_40303E ; Prende la word contenente i dati relativi al valore hex del giorno
.text:004016D3 mov Attenz_Valore_Giorni, eax ; Attenzione mette questo valore in questa locazione - cosa da tenere a mente
.text:004016D8 mov edi, offset loc_4016DF
.text:004016DD jmp short convert_esa_to_string
.text:004016DF ; ---------------------------------------------------------------------------
.text:004016DF
.text:004016DF loc_4016DF: ; DATA XREF: .text:004016D8o
.text:004016DF mov ecx, 3
.text:004016E4 mov ax, word_40303A ; Prende la word contenente i dati relativi al valore hex del mese
.text:004016EA mov Attenz_Valore_Mese, eax ; Attenzione mette questo valore in questa locazione - cosa da tenere a mente
.text:004016EF mov edi, offset loc_4016F6
.text:004016F4 jmp short convert_esa_to_string
.text:004016F6 ; ---------------------------------------------------------------------------
.text:004016F6
.text:004016F6 loc_4016F6: ; DATA XREF: .text:004016EFo
.text:004016F6 mov ax, Struct_SystemTime
.text:004016FC push eax
.text:004016FD push offset a4d
.text:00401702 lea esi, ds:403012h
.text:00401708 push esi
.text:00401709 call wsprintfA_imp
.text:0040170F mov ecx, 0Ah
.text:00401714 mov byte ptr word_40300C[ecx], 20h
.text:0040171B push offset word_40300C
.text:00401720 push 0
.text:00401722 push 401h
.text:00401727 push 3EAh
.text:0040172C push dword ptr [ebp+8]
.text:0040172F call SendDlgItemMessageA_imp
.text:00401735 leave
.text:00401736 retn 4


Eccoci qua, qui come si può notare c'è ben poco da dire, l'uniche cose degne di nota, sono il salvataggio dei due valori che ho rinominato Att_valore_giorni Att_valore_mese, valori che verranno utilizzati successivamente per il calcolo della lunghezza del seriale da inserire, per il resto l'altra cosa degna di nota, e che cmq secondo me è da ricordare, è la routine che ho chiamato convert_esa_to_string, una routine molto pulita e semplice che fa la conversione da hex a stringa! Per il resto, tutta questa routine non fa altro che prendere i valori dell'anno, mese, giorno, ora e minuti dalla struct SystemTime, convertirli in stringa per poi essere utilizzati per visualizzare i valori nella finestra del crackme stesso! (infatti se avviate il crackme noterete che abbiamo, la visualizzazione dell'anno, mese, ecc nella barra sotto) Una volta visto questo, possiamo uscire da questa procedura e ci troveremo di fronte al calcolo della lunghezza del seriale, che sarà calcolato come riportato qui sotto:


.text:0040134B xor edx, edx
.text:0040134D xor Attenz_Valore_Mese, 0Dh
.text:00401354 mov eax, Attenz_Valore_Mese
.text:00401359 add eax, Attenz_Valore_Giorni
.text:0040135F mov ebx, 2
.text:00401364 div ebx
.text:00401366 add eax, 5
.text:00401369 mov number, eax

Come potete notare prende i valori del mese e dei giorni, per calcolare la lunghezza del seriale, che una volta fatta alcune operazione mette nel buffer che io ho chiamato number, lo so, non è molto azzeccato come nomignolo, mah a me andava più che bene, voi lo potete benissimo rinominare lunghezza_seriale :D :D. continuiamo vendendo:

.text:0040136E push offset aUnregistered ; "Unregistered"
.text:00401373 push 2
.text:00401375 push 401h
.text:0040137A push 3Eah
.text:0040137F push [ebp+hWnd]
.text:00401382 call SendDlgItemMessageA_imp
.text:00401388 push 0
.text:0040138A push 101h
.text:0040138F push 401h
.text:00401394 push 3Eah
.text:00401399 push [ebp+hWnd]
.text:0040139C call SendDlgItemMessageA_imp
.text:004013A2 push offset Aggiorna_Ora_e_Minuti
.text:004013A7 push 3E8h
.text:004013AC push 1
.text:004013AE push [ebp+hWnd]
.text:004013B1 call SetTimer_imp
.text:004013B7 jmp short loc_401433

Beh qui c'è poco da dire, non fa altro che spedire il messaggio per la visualizzazione della stringa "Unregistered", la quale sarà visualizzata sulla finestra del crackme, e poi chiama la call SetTimer che serve ad aggiornare i valori dell'ora e dei minuti come possiamo vedere qui sotto, dato che pusha questo offset ( io l'ho chiamato Aggiorna_Ora_e_Minuti):

Aggiorna_Ora_e_Minuti proc near ; DATA XREF: DialogFunc+CAo
.text:00401624 push ebp
.text:00401625 mov ebp, esp
.text:00401627 push offset Struct_SystemTime
.text:0040162C call GetLocalTime_im
.text:00401632 mov ecx, 0Bh
.text:00401637 mov ax, word_403040 ; Valore dell'ora
.text:0040163D mov edi, offset loc_401654
.text:00401642
.text:00401642 loc_401642: ; CODE XREF: .text:00401664j
.text:00401642 aam
.text:00401644 add ah, 30h
.text:00401647 add al, 30h
.text:00401649 xchg ah, al
.text:0040164B mov word_40300C[ecx], ax
.text:00401652 jmp edi
.text:00401652 Aggiorna_Ora_e_Minuti endp
.text:00401652
.text:00401654 ; ---------------------------------------------------------------------------
.text:00401654
.text:00401654 loc_401654: ; DATA XREF: Aggiorna_Ora_e_Minuti+19o
.text:00401654 mov ecx, 0Eh
.text:00401659 mov ax, word_403042 ; Valore dei Minuti
.text:0040165F mov edi, offset loc_401666
.text:00401664 jmp short loc_401642

A questo punto, dobbiamo trovare i punti in cui il crackme va a prendere il valori del nick e del seriale, dato che dobbiamo vedere che algo usa per la generazione e il controllo del seriale! Dato che il crackme non usa le canoniche Api GetWindowTextA e GetDlgItemTextA e usa per mandare quello che vuole l'Api SendDlgItemMessageA, la prima cosa che ho pensato vado a vedere come si chiama il Msg che serve a prendere il testo da un editbox, il messaggio che deve usare è il WM_GETTEXT, che se andiamo a vedere sul sito di AndreaGeddon, dato che ha fatto un lavoraccio a scrivere tutte le costati, vedremo che la costante del Msg WM_GETTEXT è uguale a "D", adesso basterebbe andare a cercare nel crackme quale call di SendDlgItemMessageA usa come Msg "D", si sta un attimo a vedere dove stanno, dato che per arrivarci vicino basta andare a vedere le stringhe di errore di seriale sbagliato e di seriale corretto, dato che le abbiamo tutte in chiaro! Infatti andiamo nella finestra chiamata "String Reference" di Ida e cerchiamo la stringa "Application registered successfully! Good job reverser!" (Se non vi sono comparite tutte le stringhe in chiaro, basta andare dentro la finestra delle stringhe, click sul tasto destro del mouse e poi refresh... per chi nn lo sapesse ancora), ci clicchiamo sopra e una volta andati a finire nella zona dove vengono definite le stringhe, notiamo subito sotto la ; DATA XREF: sub_401483+B0o con il mouse ci portiamo davanti a sub, un piccolo click e andremo a finire proprio nella zona di codice dove troviamo le due chiamate a SendDlgItemMessageA che hanno come Msg "D"(le troviamo poco sopra!), se poi andiamo sopra il "D" e clicchiamo con il tasto destro del mouse, vediamo che sul menu a tendina con la voce "symbolic constant" --> ci esce pure WM_GETTEXT … adesso non dobbiamo fare altro che vedere che cosa fa questo algoritmo!

.text:004014CC mov ebx, offset aYouVeForgottenToDigitT ; "You've forgotten to digit the name!"
.text:004014D1 push offset Buffer_name_serial
.text:004014D6 push 3Ch
.text:004014D8 push WM_GETTEXT
.text:004014DA push 0BB9h
.text:004014DF push [ebp+nResult]
.text:004014E2 call SendDlgItemMessageA_imp
.text:004014E8 test eax, eax
.text:004014EA jz short loc_401568
.text:004014EC call Prima_parte

Allora, eccola qua la prima chiamata per prendere il nostro nickname, il quale viene messo in Buffer_name_serial (l'ho chiamato cosi, dato che lo usa sia per il nickname che per il serial), una volta eseguita l'Api, avremo il nostro nickname nel buffer , proseguiamo ed entriamo nella call Prima_parte:

.text:0040158D Prima_parte proc near ; CODE XREF: sub_401483+69p
.text:0040158D xor ecx, ecx
.text:0040158F xor eax, eax
.text:00401591 mov byte_403250, 0 ; Mette zero nella locazione 403250
.text:00401598
.text:00401598 loc_401598: ; CODE XREF: Prima_parte+20j
.text:00401598 movzx ebx, Buffer_name_serial[ecx]
.text:0040159F cmp bl, 0
.text:004015A2 jz short loc_4015AF
.text:004015A4 cmp bh, 0
.text:004015A7 jz short loc_4015AF
.text:004015A9 add ax, bx
.text:004015AC inc ecx
.text:004015AD jmp short loc_401598
.text:004015AF ; ---------------------------------------------------------------------------
.text:004015AF
.text:004015AF loc_4015AF: ; CODE XREF: Prima_parte+15j
.text:004015AF ; Prima_parte+1Aj
.text:004015AF xor al, ah
.text:004015B1 mov byte_403250, al ; il valore ottenuto lo mette nella locazione in cui prima la aveva azzerata mettendo 00
.text:004015B6 retn
.text:004015B6 Prima_parte end

Come possiamo vedere, azzera la locazione 403250, dopodiché prende i caratteri del nostro nick due a due, e li mette in ebx, io come nick avevo inserito x86x86x, quindi prenderà la prima volta "x8", la seconda "86", la terza "6x" ecc ecc controllando sempre che siano due a due, altrimenti esce dal ciclo! Ad ogni iterazione fa una somma dei valori di questi due caratteri presi, e li mette in ax! Una volta finito il nostro nick, fa uno xor tra al, e ah e mette il risultato nella locazione che prima aveva azzerato! Dopo ciò esce dalla call! E ci troveremo nella seconda chiamata, e precisamente in quella che prende il nostro serial:

.text:004014F1 mov ebx, offset aYouVeForgottenToDigi_0 ; "You've forgotten to digit the serial!"
.text:004014F6 push offset Buffer_name_serial
.text:004014FB push 3Ch
.text:004014FD push WM_GETTEXT
.text:004014FF push 0BBAh
.text:00401504 push [ebp+nResult]
.text:00401507 call SendDlgItemMessageA_imp
.text:0040150D test eax, eax
.text:0040150F jz short loc_401568
.text:00401511 mov ebx, offset aSorryTheSerialYouHaveE ; "Sorry! The serial you have entered is i"...
.text:00401516 cmp eax, number ;Il nostro seriale deve essere lungo quanto il valore calcolato prima mediante i calcoli del mese e dei giorni
.text:0040151C jnz short loc_401568
.text:0040151E call Seconda_parte

l'unico cambiamento è che stavolta controlla la lunghezza del nostro seriale, con il valore calcolato in precedenza con la routine descritta poco sopra, quella che fa i calcoli con il valore del mese e dei giorni, tanto per capirci!
Se il nostro seriale ha la lunghezza corretta entriamo nella call Seconda_parte:

.text:004015B7 Seconda_parte proc near ; CODE XREF: sub_401483+9Bp
.text:004015B7 xor eax, eax
.text:004015B9 mov ebx, 3Bh
.text:004015BE add ebx, number
.text:004015C4 mov al, byte_403250
.text:004015C9 mov cl, 4
.text:004015CB div cl
.text:004015CD add bl, ah
.text:004015CF mov Byte_Calc, bl ; Mette il byte calcolato in questa locazione
.text:004015D5 mov al, byte ptr Buffer_name_serial
.text:004015DA cmp al, Byte_Calc
.text:004015E0 jnz short loc_40161B
.text:004015E2 xor ecx, ecx
.text:004015E4
.text:004015E4 loc_4015E4: ; CODE XREF: Seconda_parte+62j
.text:004015E4 inc ecx
.text:004015E5 cmp byte ptr Buffer_name_serial[ecx], 0
.text:004015EC jz short loc_40161E
.text:004015EE mov al, Byte_Calc
.text:004015F3 xor al, byte_403250
.text:004015F9 dec byte_403250
.text:004015FF xor ah, ah
.text:00401601 mov bl, 1Ah
.text:00401603 div bl
.text:00401605 add ah, 'A'
.text:00401608 shr ax, 8
.text:0040160C cmp al, byte ptr Buffer_name_serial[ecx]
.text:00401612 jnz short loc_40161B
.text:00401614 mov Byte_Calc, al
.text:00401619 jmp short loc_4015E4
.text:0040161B ; ---------------------------------------------------------------------------
.text:0040161B
.text:0040161B loc_40161B: ; CODE XREF: Seconda_parte+29j
.text:0040161B ; Seconda_parte+5Bj
.text:0040161B xor eax, eax
.text:0040161D retn
.text:0040161E ; ---------------------------------------------------------------------------
.text:0040161E
.text:0040161E loc_40161E: ; CODE XREF: Seconda_parte+35j
.text:0040161E mov eax, 1
.text:00401623 retn
.text:00401623 Seconda_parte endp

Eccoci nella seconda parte della generazione del seriale, allora come prima cosa, prende il valore 3B lo mette in ebx, gli somma la lunghezza che deve avere il seriale, mette in al il valore calcolato in precedenza con i calcoli eseguiti sul nostro nick, muove in cl 4, divide al per 4, adesso in ah ci sarà il resto della divisione e in al il risultato, somma il valore di bl con ah, e mette il risultato nella locazione che io ho chiamato Byte_Calc, poi prende il primo carattere del nostro seriale, e lo confronta con il valore appena calcolato e situato in Byte_Calc. Se i valori coincidono,va avanti azzerando ecx, e poi incrementadolo di uno, infatti il primo valore del nostro seriale è già stato verificato! Poi c'è il cmp che serve a verificare che siano stati calcolati tutti i valori del nostro seriale e inizia il calcolo. Prende il valore del primo carattere del seriale calcolato, lo mette in al, lo xora con il valore contenuto nella locazione 403250, decrementa poi il valore contenuto in 403250 di uno, azzera ah, muove il valore 1Ah in bl e divide al per bl, mettendo il risultato in al e il resto in ah, poi ad ah aggiunge il valore di 'A' che poi non è altro che 41h. A questo punto il valore contenuto in ax viene diviso per 100h (l'istruzione Shr ax,8 fa esattamente questo ax / 2 ^ 8 , che poi in parole più spicciole fa un shift), il risultato che situato in al viene confrontato con il carattere del nostro seriale, se è corretto, prima salva il valore del carattere appena calcolato in Byte_Calc e poi continua il ciclo facendo nuovamente tutti i calcoli e verificando tutti i caratteri del nostro seriale inserito! Una volta finito salterà all'indirizzo 40161E, muovendo in eax, 1, significa che è tutto Ok infatti una volta usciti dalla Call e precisamente qui sotto:

.text:00401523 mov ebx, offset aSorryTheSerialYouHaveE ; "Sorry! The serial you have entered is i"...
.text:00401528 test eax, eax
.text:0040152A jz short loc_401568
.text:0040152C push 40h
.text:0040152E push offset aPhoenix1 ; "Phoenix1"
.text:00401533 push offset aApplicationRegisteredS ; "Application registered successfully! Go"...
.text:00401538 push [ebp+nResult] ; nResult
.text:0040153B call MessageBoxA
.text:00401541 push [ebp+nResult] ; hDlg
.text:00401544 call EndDialog
.text:00401549 push offset aRegistered ; "Registered"
.text:0040154E push 2
.text:00401550 push 401h
.text:00401555 push 3EAh
.text:0040155A push dword_403034
.text:00401560 call SendDlgItemMessageA_imp
.text:00401566 jmp short loc_401579
.text:00401568 ; ---------------------------------------------------------------------------
.text:00401568
.text:00401568 loc_401568: ; CODE XREF: sub_401483+67j
.text:00401568 ; sub_401483+8Cj ...
.text:00401568 push 30h
.text:0040156A push offset aPhoenix1 ; "Phoenix1"
.text:0040156F push ebx
.text:00401570 push [ebp+nResult]
.text:00401573 call MessageBoxA

dato che eax contiene 1 e non 0, non salteremo all'indirizzo 401568, (come sarebbe successo invece se il seriale sarebbe stato sbagliato, infatti se ciò accadeva in eax sarebbe stato messo 0), ma continueremo visualizzando la messagebox di Successo! Come si può notare chiramente! Inoltre dopo verrà visualizzata nella barra anche la scritta Registered! Finito, adesso non dobbiamo altro che scrivere il KeyGen ... io lo scritto semplicemente prendendomi le parti che mi servivano direttamente da Ida e modificandolo a mio piacere! Inoltre faccio presente che il mio KeyGen fa il calcolo del seriale, anche quando è già avviato e si apportano modifiche ai valori dei giorni e del mese, infatti non serve riavviarlo ogni volta! Cosa che invece il CrackMe, deve fare!
Vi allego qui di seguito il KeyGen funzionante e il source!

Download KeyGen

By x86

Note finali

Ok, adesso passiamo ai saluti! Per prima cosa saluto la mia Girl :), che poverina riesce ancora a sopportarmi! Poi il Que, AndreaGeddon (al quale rompo spesso, chiedendo le info più varie e strane!), Yado, _d3im0s_ (speriamo di vederci anche questa estate ...), albe, e a anche se nn lo vedo più sulla net Acid_Leo (fatti vedere ogni tanto), Spider, Nt e tutti quelli che frequentano la Uic!

Disclaimer

Vorrei ricordare che il software va comprato e  non rubato, dovete registrare il vostro prodotto dopo il periodo di valutazione. Non mi ritengo responsabile per eventuali danni causati al vostro computer determinati dall'uso improprio di questo tutorial. Questo documento è stato scritto per invogliare il consumatore a registrare legalmente i propri programmi, e non a fargli fare uso dei tantissimi file crack presenti in rete, infatti tale documento aiuta a comprendere lo sforzo immane che ogni singolo programmatore ha dovuto portare avanti per fornire ai rispettivi consumatori i migliori prodotti possibili.

Noi reversiamo al solo scopo informativo e di miglioramento del linguaggio Assembly.