File Carbon v1.0.36.2
Sha-1 keygenning o semplice serial sniffing :D

Data

by "ZaiRoN"

 

3o-12-2oo3

UIC's Home Page

Published by Quequero


L'ultima volta che sono entrato in una donna fu quando ho visitato la statua della Libertà.
  

Grazie tante zai!

Il sesso senza amore è un'esperienza vuota, ma tra le esperienze vuote è una delle migliori.

...

E-mail: zaironcrk(at)woodmann.net
ZaiRoN su irc (#crack-it) e su efnet (#cracking4newbies)

....

Difficoltà

(Z)NewBies (Z)Intermedio ( )Avanzato ( )Master

 

Oggi si parla di: Secure Hash Algorithm (SHA-1)


File Carbon v1.0.36.2
Sha-1 keygenning o semplice serial sniffing :D
Written by ZaiRoN

Introduzione

Eccomi qua costretto dal Que a scrivere un tutorial durante le vacanze di Natale... ma vi sembra giusto? :D 
Scherzi a parte, come vi ho già preannunciato parleremo dell'SHA-1. Questo tutorial seguirà più o meno lo stesso schema che ho adottato per il tutorial sull'MD5, prima un minimo di teoria e poi la pratica. Cercherò di farvi vedere come riconoscere e come comportarsi con questo particolare algoritmo; il programma che ho scelto non è compresso/packato/modificato in nessun modo per cui credo rappresenti una perfetta vittima per i nostri scopi. Ho dato due diverse valutazioni di difficoltà in quanto il programma è banalmente registrabile con un semplice serial sniffing ma la scrittura di un keygen non è così immediata e necessità la conoscenza dell'sha-1.

Tools usati

Un debugger (Ollydbg)
Un disassemblatore (Ida)
Damn hash calculator (http://www.damn.to/software/hashcalc.html)
Un qualsiasi compilatore per scrivere il keygen...

URL o FTP del programma

http://www.beescomputing.com/downloads/filecarbon/filecarbon_setup.exe

Notizie sul programma

Questo programma serve per... non lo so e non mi interessa! Ecco che cosa dicono gli autori: "FileCarbon is a file utility application for Windows(r), allows to set the modified time of files and to find CRC32 --also called CRC-32-- and MD5 checksum values of files. blablabla...."

Essay

Secure Hash Algorithm, un algoritmo sicuro!
Il Secure Hash Algorithm fu creato nel 1993 dalla National Security Agency (NSA) ed è oggi identificato, nella sua forma originale, con il nome sha-0. Questa primissimissima versione dell'algoritmo venne subito dopo (nel 1995) sostituita dalla Sha-1, una versione che andava a correggere una piccola falla presente nella vecchia versione; quelli dell'NSA non hanno mai rilasciato una dichiarazione ufficiale sul problema che affliggeva la sha-0 ma il fatto che nel 1998 due ricercatori siano riusciti ad attaccare con successo l'algoritmo fa pensare ad un bug che riduceva il livello di sicurezza. Di questo algoritmo esistono in realtà altre 3 versioni denominate sha-256, sha-384 e sha-512, create dal National Institute of Standards and Technology (NIST) e sviluppate seguendo le caratteristiche dell'sha-1. 
 
L'sha-1 prende in ingresso un file o una stringa (di lunghezza inferiore a 2^64 bit) e produce un 'digest' (riassunto) di 160 bit, 20 caratteri. Questo hash è molto più lento rispetto all'MD5 ma producendo un digest di dimensioni maggiori (160 bit contro i 128 bit dell'MD5) viene considerato più sicuro dell'MD5. Come l'MD5 però mantiene delle proprietà comuni a tutti gli hash:
- è computazionalmente impossibile sha-1-are due messaggi diversi ed ottenere lo stesso digest
- il processo non è invertibile, è cioè impossibile partire dal digest e trovare il messaggio originale
Come altri tipi di hash, anche l'sha-1 utilizza una serie di operazioni molto semplici per produrre il digest finale. L'algoritmo è abbastanza incasinato per cui non starò qui a spiegarvi per filo e per segno quali sono le operazioni svolte ma mi limiterò a farvi vedere come si può facilmente identificare l'algoritmo in pochissimi secondi. Se volete farvi del male date un'occhiata qua: http://www.faqs.org/rfcs/rfc3174.html :p
 
In generale, il modo più semplice per identificare un algoritmo di questo tipo consiste prima di tutto nello studio delle costanti di inizializzazione usate dall'algoritmo. L'sha-1 inizializza un buffer di 20 byte in questo modo:
 
dword_1 = 0x67452301
dword_2 = 0xEFCDAB89
dword_3 = 0x98BADCFE
dword_4 = 0x10325476
dword_5 = 0xC3D2E1F0 
 
Come potete vedere le prime 4 dword sono le stesse utilizzate dall'MD5 ma in più viene definita una ulteriore dword. In futuro, quando e se vi capiterà di incontrare una serie di istruzioni simili a queste:
 
MOV DWORD PTR DS:[reg],67452301
MOV DWORD PTR DS:[reg+4],EFCDAB89
MOV DWORD PTR DS:[reg+8],98BADCFE
MOV DWORD PTR DS:[reg+C],10325476
MOV DWORD PTR DS:[reg+10],C3D2E1F0
 
dovrete subito drizzare le orecchie e dire: "hmmm... sha-1". Ma siamo sicuri che sarà proprio sha-1? Potrebbe anche essere ripemd160... 
Nella sua forma originale, questo tipo di algoritmo si presenta come una serie di call dove la prima esegue l'inizializzazione del buffer con le 5 dword viste qua sopra mentre le altre si occupano di generare il digest. Per avere la certezza che siete di fronte all'sha-1 avete due possibilità:
1. steppare tutte le call successive a quella di inizializzazione cercando di identificare tutti i passi eseguiti dall'algoritmo
2. andare a spulciare i valori passati e ritornati alle/dalle varie call seguenti la call di inizializzazione
Io opterei per la seconda ipotesi sfruttando uno dei tanti hash-calculator che si trovano in rete. Perchè usare anche questo tipo di tool? Semplice, con questo tool possiamo sapere in anticipo quale sarà il digest calcolato per cui saremo facilitati nel nostro compito di ricerca del digest tra i vari valori ritornati dalle call.
Bene, questo è più o meno tutto quello che useremo per identificare l'sha-1. 
 
File Carbon, adesso ti registriamo!
Lanciamo il programma e vediamo un pò come possiamo approcciare la routine di registrazione. Inserendo una combinazione di name/password sbagliata appare un bel messaggio di errore: "License Code do not match to User Name, click OK to try again". Bene, andiamo in Ida e cerchiamo il messaggio di errore nella finestra denominata "Strings" (Shift-F12); appena trovate la stringa fate un bel doppio click sulla xref e vediamo a che indirizzo viene visualizzato il box di errore: 499E5C. La routine contenente la visualizzazione del box di errore parte all'indirizzo 499A08 e molto probabilmente questa routine contiene tutto l'algoritmo di registrazione. Torniamo in Ollydbg e mettiamo un breakpoint all'inizio della routine; proviamo a registrarci di nuovo e vediamo che Olly brecka. Steppate qualche linea di codice:
 
CODE:00499A3A call sub_444B64   <--------- legge il nome inserito
CODE:00499A3F mov eax, [ebp+var_100]   <-- eax punta al nome inserito
CODE:00499A45 call sub_404A50   <--------- ritorna in eax la lunghezza del nome inserito
CODE:00499A4A cmp eax, 7Eh   <------------ confronta la lunghezza del nome con il valore 7Eh
CODE:00499A4D jle short loc_499A7E   <---- se il nome è lungo più di 0x7E caratteri viene tagliato a 0x7E caratteri
...
CODE:00499A7E lea edx, [ebp+var_18]
CODE:00499A81 mov eax, [ebp+var_4]
CODE:00499A84 mov eax, [eax+308h]
CODE:00499A8A call sub_444B64   <--------- rilegge il nome
CODE:00499A8F lea edx, [ebp+var_C]
CODE:00499A92 mov eax, [ebp+var_18]   <--- eax punta al nome inserito
CODE:00499A95 call sub_40891C   <--------- mette il nome in maiuscolo 
CODE:00499A9A lea edx, [ebp+var_10C]
CODE:00499AA0 mov eax, [ebp+var_4]
CODE:00499AA3 mov eax, [eax+310h]
CODE:00499AA9 call sub_444B64   <--------- legge il serial inserito
CODE:00499AAE mov eax, [ebp+var_10C]   <-- eax punta al serial inserito
CODE:00499AB4 lea edx, [ebp+var_108]
CODE:00499ABA call sub_40891C   <--------- mette il serial in maiuscolo
CODE:00499ABF mov eax, [ebp+var_108]   <-- eax punta al serial maiuscolo
CODE:00499AC5 lea edx, [ebp+var_1C]
CODE:00499AC8 call sub_48D060   <--------- sposta il serial in un nuovo buffer
 
Ecco qua la prima semplicissima parte della routine di protezione che in sostanza legge il nome e il serial inseriti e li converte in maiuscolo... niente altro per ora. Proseguiamo con la routine:
 
CODE:00499AEF mov eax, [ebp+var_1C]   <-- eax punta al serial inserito
CODE:00499AF2 call sub_48D114   <-------- controlla che il serial contenga soltanto determinati caratteri
CODE:00499AF7 test al, al   <------------ se al=1 il serial è corretto
CODE:00499AF9 jnz short loc_499B25
CODE:00499AFB push 1
CODE:00499AFD mov ecx, offset dword_499EEC
CODE:00499B02 mov edx, offset aLicenseCodeMus   <-- "License Code must be containing only the letters a..f, A..F,digit(s), dash(es) and space(s), click OK to try again"
CODE:00499B07 mov eax, ds:off_4A1104
CODE:00499B0C mov eax, [eax]
CODE:00499B0E call sub_46533C   <-------- message box che ci comunica quali sono i caratteri validi per il serial
 
Un primo controllo sul serial, precisamente sul tipo di caratteri che compongono il serial inserito. Come potete vedere non c'è nessun bisogno di steppare la call a 499AF2 perché è il programma stesso a dirci quali caratteri non sono validi, meglio di così :)
 
CODE:00499B25 cmp [ebp+var_C], 0   <--- controlla che il nome sia presente
CODE:00499B29 jz short loc_499B31
CODE:00499B2B cmp [ebp+var_1C], 0   <-- controlla che il serial sia presente
CODE:00499B2F jnz short loc_499B4A
 
Controlla di nuovo la presenza del nome e del serial... mah.
 
CODE:00499B4A lea eax, [ebp+var_FC]   <-- eax punta ad un buffer
CODE:00499B50 xor ecx, ecx
CODE:00499B52 mov edx, 14h   <----------- edx = 20
CODE:00499B57 call sub_403110   <-------- pulisce (mettendo a 00h) 20 byte del buffer, vedremo tra poco per cosa viene usato questo buffer di 20 byte
   ...
CODE:00499B7B push offset aAedb3456   <-- pusha il puntatore alla stringa: "AEDB3456"
CODE:00499B80 lea edx, [ebp+var_110]
CODE:00499B86 mov eax, [ebp+var_C]   <--- eax punta al nome inserito (maiuscolo)
CODE:00499B89 call sub_40891C   <-------- ri-sposta il nome inserito (maiuscolo)
CODE:00499B8E push [ebp+var_110]   <----- pusha il puntatore al nome (maiuscolo)
CODE:00499B94 push offset aAebca386   <-- pusha il puntatore alla stringa: "AEBCA386"
CODE:00499B99 lea eax, [ebp+var_20]
CODE:00499B9C mov edx, 3
CODE:00499BA1 call sub_404B10   <-------- concatena le tre stringhe pushate qua sopra
 
Dopo una serie di operazioni semi-inutili iniziamo a vedere come si evolve la routine di protezione. Il nome inserito viene messo in mezzo alle due stringhe "AEDB3456" e "AEBCA386" ottenendo una cosa di questo tipo: AEDB3456<nome>AEBCA386
D'ora in poi farò riferimento a questa stringa unica come 'nome espanso'. Vediamo cosa combina con la stringa appena costruita:
 
CODE:00499BA6 mov eax, [ebp+var_8]
CODE:00499BA9 mov edx, [eax]
CODE:00499BAB call dword ptr [edx+40h]   <-- hmmm...
 
Fino ad ora non abbiamo steppato nessuna call perché si riusciva facilmente a capire cosa facevano prestando attenzione soltanto ai valori pushati e ritornati; stavolta il discorso è diverso. Non riuscendo a capire che cosa faccia dobbiamo per forza entrare nella call e vedere cosa succede:
 
CODE:0048CDC8 push ebx
...
CODE:0048CDD2 mov dword ptr [ebx+40h], 67452301h   <--- hm
CODE:0048CDD9 mov dword ptr [ebx+44h], 0EFCDAB89h   <-- hmm
CODE:0048CDE0 mov dword ptr [ebx+48h], 98BADCFEh   <--- hmmm
CODE:0048CDE7 mov dword ptr [ebx+4Ch], 10325476h   <--- hmmmm
CODE:0048CDEE mov dword ptr [ebx+50h], 0C3D2E1F0h   <-- hmmmmm... interessante!
...
CODE:0048CDFA retn
 
Vi si è accesa la lampadina sopra la testa in stile fumetti? Come no!?! Non vi ricorda niente l'inizializzazione di questo buffer con quelle particolarissime 5 dword? Forse abbiamo trovato l'inizializzazione di una hash. Che sia sha-1? Si fa presto a verificalo, basta usare un hash-calculator e dargli in pasto la stringa di input; quale sarà la stringa di input? Hmmm, vediamo:
 
CODE:00499BAE mov edx, [ebp+var_20]   <-- edx punta al nome espanso
CODE:00499BB1 mov eax, [ebp+var_8]
CODE:00499BB4 call sub_48A6FC
 
Che dire, appena dopo la inizializzazione dell'hash viene tirato in ballo il nome espanso, che sia l'input della hash? La risposta è semplice, basta provare usando l'hash-calculator. Nel mio caso, l'sha-1 sulla stringa 'AEDB3456ZAIRONAEBCA386' produce i seguenti 20 byte: 0x1C, 0x49, 0x9B, 0x8D, 0x2B, 0x59, 0x36, 0x97, 0xF4, 0x6F, 0xF0, 0x0E, 0xF8, 0xE9, 0xE6, 0xAD, 0x74, 0xFB. Vediamo se una delle call successive mi genera la stessa sequenza di byte: 
 
CODE:00499BB9 lea edx, [ebp+var_FC]   <-- edx = indirizzo del buffer *pulito* dalla call a 499B57, vi ricordate?
CODE:00499BBF mov eax, [ebp+var_8]
CODE:00499BC2 mov ecx, [eax]
CODE:00499BC4 call dword ptr [ecx+44h]   <-- mette nel buffer *pulito* una strana sequenza di byte
 
Date un'occhiata ai byte contenuti nel buffer, non notate niente di strano? Sono 20 e già questo dovrebbe farvi sorridere; in più c'è dell'altro. Provate a confrontare questi byte con quelli ritornati dall'sha-1... eccellente, abbiamo individuato l'sha-1! Semplice? Bhè, direi di si :D
Questa è un pò la limitazione offerta da questo tipo di funzioni hash... sono abbastanza prevedibili e di facile individuazione. 
Vediamo cosa succede nel resto della routine di protezione:
 
CODE:00499BF4 mov esi, 14h   <--------- esi = 20
CODE:00499BF9 lea ebx, [ebp-0FCh]   <-- ebx punta ai 20 byte dell'sha-1 applicato al nome espanso
CODE:00499BFF lea ecx, [ebp-114h]
CODE:00499C05 xor eax, eax
CODE:00499C07 mov al, [ebx]   <-------- prende il byte corrente dell'sha-1(nome_espanso)
CODE:00499C09 mov edx, 2
CODE:00499C0E call sub_408C84   <------ divide il byte in due byte. Se al=1C allora genera i caratteri '1' e 'C'
CODE:00499C13 mov edx, [ebp-114h]   <-- edx punta ai due caratteri appena generati
CODE:00499C19 lea eax, [ebp-24h]
CODE:00499C1C call sub_404A58   <------ concatena i nuovi caratteri in un nuovo buffer
CODE:00499C21 inc ebx
CODE:00499C22 dec esi
CODE:00499C23 jnz short loc_499BFF   <-- ripete il ciclo finchè non ha spezzato tutti i byte dell'sha-1(nome_espanso)
 
Ecco qua un semplice ciclo sul valore ritornato dall'sha-1. I 20 byte diventano 40, nel mio caso ottengo:
- sha-1: 0x1C, 0x49, 0x9B, 0x8D, 0x2B, 0x59, 0x36, 0x97, 0xF4, 0x6F, 0xF0, 0x0E, 0xF8, 0xE9, 0xE6, 0xAD, 0x74, 0xFB
- stringa generata dal ciclo qua sopra: "1C499B8D2B593697F46FF0460EF826E9E6AD74FB"
Non mi sembra ci sia molto altro da dire. Vediamo la parte finale della routine:
 
CODE:00499C4C lea ecx, [ebp-14h]
CODE:00499C4F mov eax, [ebp-10h]   <--------- eax punta al primo carattere del nome inserito
CODE:00499C52 movzx eax, byte ptr [eax]   <-- al = primo carattere del nome inserito, nel mio caso 0x5A (la 'Z')
CODE:00499C55 mov edx, 2
CODE:00499C5A call sub_408C84   <------------ questa l'abbiamo incontrata nel ciclo precedente (spezza un byte in due...)
CODE:00499C5F push offset aC499   <---------- pusha il puntatore alla stringa 'C499'
CODE:00499C64 push [ebp+var_14]   <---------- pusha il puntatore alla stringa formata dai due caratteri ottenuti dal primo carattere del nome, nel mio caso la stringa è '5A'
CODE:00499C67 lea eax, [ebp+var_118]
CODE:00499C6D push eax   <-- ---------------- pusha il puntatore ad un buffer vuoto; il buffer sarà riempito dalla seguente call
CODE:00499C6E mov ecx, 0Eh
CODE:00499C73 mov edx, 1
CODE:00499C78 mov eax, [ebp+var_24]   <------ eax punta all'sha-1(nome_espanso) esteso a 40 byte creata nel ciclo precedente
CODE:00499C7B call sub_404CA8   <------------ sposta i primi 14 (0x0E) byte puntati da eax in un nuovo buffer  
CODE:00499C80 push [ebp+var_118]   <--------- pusha il puntatore al buffer contenente i 14 caratteri
CODE:00499C86 lea eax, [ebp+var_28]
CODE:00499C89 mov edx, 3
CODE:00499C8E call sub_404B10   <------------ anche questa l'abbiamo già incontrate, vi ricordate cosa fa? Concatena le 3 stringhe pushate qua sopra: 'C499', '5A' e i primi 14 caratteri dell'sha-1(nome espanso)
CODE:00499C93 mov edx, [ebp+var_28]   <------ edx punta alla stringa risultante dal concatenamento delle 3 stringhe
CODE:00499C96 mov eax, [ebp+var_1C]  <------- eax punta al serial inserito
CODE:00499C99 call sub_4089E4   <------------ confronta le due stringhe e ritorna eax = 0 se sono uguali
CODE:00499C9E test eax, eax   <-------------- se eax=0 siamo registrati...
CODE:00499CA0 setz bl
CODE:00499CA3 mov eax, [ebp+var_4]
CODE:00499CA6 mov [eax+320h], bl
CODE:00499CAC test bl, bl
CODE:00499CAE jz loc_499E49   <-------------- se il serial è corretto non saltiamo
 
Tadaa, finito! Come vi avevo preannunciato la routine di protezione usata dal programma è molto semplice; in questo caso basterebbe un semplicissimo serial sniffing per trovare il serial esatto. Il problema però nasce nel momento in cui uno decide di provare a scrivere un keygen; sareste stati in grado di scrivere un keygen in 2 minuti senza sapere che la routine di protezione utilizza l'sha-1? Credo proprio di no... 
A proposito di keygen, scriviamone subito uno.
 
Come scrivere un keygen
Prima di partire col keygen vero e proprio facciamo un piccolo riassunto sul funzionamento della routine di protezione prendendo come nome la stringa 'ZaiRoN'. Ecco i passi effettuati dall'algoritmo:
1. lettura del nome: 'ZaiRoN'
2. conversione in maiuscolo: 'ZAIRON'
3. estenzione del nome con le stringhe "AEDB3456" e "AEBCA386": 'AEDB3456ZAIRONAEBCA386'
4. applicazione dell'sha-1 al nome esteso (ottenuto dal passo 3): 0x1C 0x49 0x9B 0x8D 0x2B 0x59 0x36 0x97 0xF4 0x6F 0xF0 0x46 0x0E 0xF8 0x26 0xE9 0xE6 0xAD 0x74 0xFB
5. sdoppiamento dei 20 byte risultanti dall'sha-1: "1C499B8D2B593697F46FF0460EF826E9E6AD74FB"
6. inizializzazione del serial che avviene prendendo la stringa "C499" e aggiungendoci i due caratteri derivanti dal valore in hex del primo carattere del nome: 'C4995A'
7. costruzione finale del serial valido appendendo al serial i primi 14 dei 40 caratteri ottenuti dallo sdoppiamento dell'sha-1 (applicato al nome esteso)
8. ...game over...
 
Nel file allegato troverete il keygen con il codice sorgente in c; il keygen è soltanto per xp/2000 perché ho usato delle funzioni non supportate dal buon vecchio w98 (potevo farlo anche per w98 ma... sono nato di Domenica e come dice mia madre "Sei nato stanco!" :D). Comunque sia, dato che ultimamente va molto di moda personalizzare le dialog ho deciso di aggiungere qualche linea in più al codice per creare una forma non standard e dare un pò di trasperenza alla dialog. Io sono amante delle cose classiche per cui una bella finestrella dos è già più che sufficiente per un keygen ma stavolta farò una eccezione :p. La creazione di questo tipo di dialog è decisamente semplice ed offre un discreto effetto quindi perché non usare un minimo di creatività per sviluppare le forme più strane? Non fate caso alla mia che è solo di esempio :D. 
Ecco la dialog sopra una immagine del sito della UIC:
 
 
Per far ciò basta utilizzare poche funzioni, per l'esattezza tre per definire la forma e un paio per dare la trasparenza. Vediamo come...  
 
Iniziamo dalla definizione della forma. L'idea è quella di creare varie regioni e combinarle assieme. Ogni regione viene definita usando una specifica funzione dipendente dalla forma della regione che si vuole creare; nel mio keygen ho utilizzato forme rettangolari e la funzione che definisce questo tipo di regioni si chiama CreateRectRgn; nella funzione si specificano i punti di definizione della regione rettangolare:
 
HRGN CreateRectRgn(
    int nLeftRect,    // Coordinata x relativa al punto in alto a sinistra 
    int nTopRect,     // Coordinata y relativa al punto in alto a sinistra 
    int nRightRect,   // Coordinata x relativa al punto in basso a destra 
    int nBottomRect   // Coordinata y relativa al punto in basso a destra 
);
 
Definite semplicemente i due estremi del rettangolo ed il gioco è fatto. Come potete vedere dall'immagine qua sopra ho creato due regioni rettangolari, una grande e una un pò più piccola. Adesso dobbiamo unire le due regioni create usando la funzione CombineRgn:
 
int CombineRgn(
    HRGN hrgnDest,      // Handle della regione ottenuta dall'unione delle altre due 
    HRGN hrgnSrc1,      // Handle della prima regione 
    HRGN hrgnSrc2,      // Handle della seconda regione
    int fnCombineMode   // Specifica come combinare le due regioni; ho usato l'OR perché le voglio entrambe 
);
 
Finito così? No, manca un ultimissimo passo; dobbiamo dire al programma quale regione visualizzare. Ovviamente la regione da visualizzare sarà quella ottenuta dalla combine fatta in precedenza:
 
int SetWindowRgn(
    HWND hWnd,     // Handle della finestra
    HRGN hRgn,     // Handle della regione da visualizzare 
    BOOL bRedraw   //
Opzione di redraw, TRUE se si desidera fare il redraw dopo la definizione della regione
);
 
Ok, adesso è davvero tutto. La nuova forma è pronta! 
 
Per quanto riguarda la trasparenza si usano altre due banali funzioncine: SetWindowLong e SetLayeredWindowAttributes. 
- SetWindowLong: è usata per cambiare gli attributi di una finestra. 
 
LONG SetWindowLong(
    HWND hWnd,       // Handle della finestra
    int nIndex,      // Offset relativo al valore da settare
    LONG dwNewLong   // Nuovo valore
);
 
Tra tutti i valori disponibili è importante settare il WS_EX_LAYERED perché necessario per la funzione seguente.
- SetLayeredWindowAttributes: definisce il livello di trasparenza della dialog
 
BOOL SetLayeredWindowAttributes( 
    HWND hwnd,        // Handle della finestra destinazione
    COLORREF crKey,   // Specifica il colore di trasparenza (in RGB)
    BYTE bAlpha,      // Valore che definisce l'opacità della finestra, il livello di trasparenza
    DWORD dwFlags     // LWA_COLORKEY per specificare il crKey e LWA_ALPHA per specificare il bAlpha
);
 
Adesso sapete anche come si creano dialog trasparenti ;) 
 
Rimane una ultimissima cosa da fare, muovere la dialog. Come faccio a muovere una dialog che non ha una title bar? Si usa un banalissimo trucchetto; quando si riceve un messaggio di tipo WM_LBUTTONDWON si manda un messaggio di tipo WM_NCLBUTTONDOWN con wParam HTCAPTION. In questo modo facciamo credere al nostro caro windows che il click sia stato fatto sulla title bar (che in realtà non è presente...) ed il gioco è fatto.
 
Non aggiungo altro su queste funzioni perché sono tutte di facile utilizzo e le specifiche che trovate alla msdn sono più che sufficienti come descrizione; usate liberamente il keygen per sperimentarle :D
 
Conclusioni:
Anche se il programma presenta una routine di protezione decisamente banale spero abbiate capito che spesso le parole crypto/hash/digest non sono così orribili come sembrano; anzi, spesso è molto più semplice lavorare con queste hash perché di loro conosciamo praticamente tutto. Con un minimo di conoscenza di background e con l'utilizzo di tool mirati vedrete che sarà relativamente semplice confrontarsi con routine che utilizzano questo tipo di algoritmi. Esistono addirittura dei tool che prendono in pasto un eseguibile e vi dicono se è presente un qualsiasi tipo di algoritmo crittografico famoso. Uno dei miei preferiti è il CryptTool di Christal che trovate qui. Personalmente non preferisco questo tipo di approccio perché potrebbe darvi indicazioni non corrette (un pò come accade con gli scanner per determinare il tipo di packer usato) ma può rappresentare un altra freccia nella vostra faretra ;)
 
ZaiRoN                              

Note finali

I saluti:
- il Que per l'idea del xmas pack e gli altri ragazzi che hanno lavorato al xmas pack Che e' diventato New Year Pack per colpa di andrea ;p NdQue
- tutti gli amici della UIC e gli amici di #crack-it
- gli amici dell'RCE forum 
 
Mi scuso in anticipo per eventuali errori commessi... è tutta colpa del Que, ci doveva dare più tempo :ppp 
 
Ha fnyhgb fcrpvnyr cre ryran.

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