Tutorial per Babylon
Babylon, per chi non lo conosce, è un traduttore simultaneo italiano-inglese molto utile. Come migliaia di altri programmi è però in versione shareware o precisamente in versione demo, completa e funzionante per 100 giorni. Dopo la scadenza di tale data il programma non funziona più.
Partirò dal presupposto che si abbia SoftIce installato.
Detto questo iniziamo il vero 'attacco':
Io mi diverto a vedere ciò che i programmi visualizzano una volta che il loro tempo di valutazione è scaduto, perciò mandiamo avanti l'orologio di qualche mesetto (un salto nel futuro fa sempre bene...) e notiamo che all'avvio il programma ci avverte con una MessageBox che la versione è scaduta, bla, bla, bla. Diamo l'Ok e notiamo che non è più possibile attivare il programma.
Ok, la protezione è stupida... ci avverte addirittura con una MessageBox che il programma è scaduto... Entriamo in SoftIce e settiamo un breakpoint (bpx) per la MessageBox. Avviamo babylon e ci ritroviamo nel bel mezzo della funzione desiderata. Usciamo con F11 e premiamo 'OK'. SoftIce ritorna visibile puntando alla linea del codice successiva alla funzione. Strano...
Se guardiamo in che modulo ci troviamo (sulla linea che delimita il codice dalla parte in cui si scrivono i comandi) notiamo di essere in CAPTLIB!CODE+105A.
Più chiaro di così... Apriamo W32Dasm e disassembliamo il file captlib.dll. Posizioniamoci nell'entrata del programma (Program Entry Point) e aggiungiamo all'indirizzo 105Ah (401000h + 105Ah = 40205Ah). Andiamo a quell'indirizzo e, magia delle magie, siamo proprio nel punto in cui viene chiamata la funzione MessageBoxA. Essendo in una DLL possiamo cercare di capire in che funzione esportata siamo. Risaliamo per qualche pagina e il codice e ci troviamo all'inizio della funzione che è OpenBabylonDLL.
Senza neanche farlo apposta cosa troviamo una manciata di righe più sotto? Ma GetSystemTime, ovviamente... Che questa funzione verifichi solamente se Babylon è scaduto? Noooo... Chi mai può dirlo...
Osserviamo il resto del codice:
- una chiamata a RegOpenKeyEx
- due chiamate a RegQueryValueEx
- due chiamate a RegSetValueEx
- una chiamata a RegCloseKey
- una chiamata a wsprintf
- una chiamata a GetForeGroundWindow
- una chiamata a MessageBox
Usciamo da Babylon e ritorniamo in SoftIce. Impostiamo un breakpoint per GetSystemTime e avviamo Babylon. SoftIce ci riporta alla funzione OpenBabylonDLL e precisamente nel suo entry point. Avanziamo fino ai due RegQueryValueEx e osserviamo ciò che viene immagazzinato nello stack (attraverso l'istruzione push).
:00401E03 52 push edx
:00401E04 683F000F00 push 000F003F
:00401E09 6A00 push 00000000
:00401E0B 6801254200 push 00422501 ; < -- Qui
:00401E10 6802000080 push 80000002
* Reference To: ADVAPI32.RegOpenKeyExA, Ord:0000h
|
:00401E15 E8A8CD0000 Call 0040EBC2
:00401E33 50 push eax
:00401E34 6A00 push 00000000
:00401E36 6A00 push 00000000
:00401E38 6828254200 push 00422528 ; < -- Qui
:00401E3D FF75E8 push [ebp-18]
* Reference To: ADVAPI32.RegQueryValueExA, Ord:0000h
|
:00401E40 E871CD0000 Call 0040EBB6
Nel codice qui riportato notiamo le due funzioni e in corrispondenza di 'Qui' un indirizzo che ci riporta alla chiave del registro che viene prima aperta e poi letta da RegQueryValueEx. La chiave è dunque la seguente:
HKLM\Software\Babylon\Babylon Translator\b1
La prima stringa richiesta è IndexByData mentre la seconda è IndexByExport.
Osservando il codice che è tra le due funzioni RegQueryValueEx ci rendiamo conto che è dove avviene il controllo dei giorni. Tra le molte linee ne evidenzio solo alcune che sono, in pratica, una seconda protezione che permette di mascherare in parte la data vera e propria:
:00401E51 0FBFD0 movsx edx, ax
:00401E54 8A4C15F8 mov cl, byte ptr [ebp+edx-8]
:00401E58 0FBFD0 movsx edx, ax
:00401E5B 308C1564FFFFFF xor byte ptr [ebp+edx-9C], cl
:00401E62 40 inc eax
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401E4F(U)
|
:00401E63 0FBFC8 movsx ecx, ax
:00401E66 3B4DEC cmp ecx, dword ptr [ebp-14]
:00401E69 72E6 jb 00401E51
Ebbene in queste poche linee avviene la decifrazione della data criptata e appena prelevata dalla chiave dall'innocente nome IndexByData.
Da notare che tutto il processo è legato unicamente all'xor di 401E5B i cui membri sono il valore prelevato dal registro (un byte alla volta) e dei valori prelevati da una tavola che è la seguente:
37 C2 9A 23 DC B3 41 F8
(valori in esadecimale)
Il codice seguente ricava i giorni totali (sfruttando la struttura SYSTEMTIME, consultate la guida API per ulteriori informazioni) e si porta alla chiamata successiva passando però per un punto rilevante:
:00401EC1 6685F6 test si, si
:00401EC4 7D0A jge 00401ED0
:00401EC6 BB01000000 mov ebx, 00000001
:00401ECB E956010000 jmp 00402026
in 401EC4 il programma decide infatti se è ancora entro i 100 giorni oppure no settando ebx se la versione è scaduta.
La seconda chiamata a RegQueryValueEx che ritorna il valore di IndexByExport è utile al programma stesso per calcolare quanti giorni rimangono alla fine della valutazione ma non ha importanza per quanto riguarda la protezione.
A questo punto possiamo applicare svariate patch per valutare il programma ancora un pò di giorni (settimane, mesi, anni, secoli...): possiamo sostituire a mov ebx, 1 mov ebx, 0 in 401EC6 e il programma funzionerà anche quando è scaduto oppure possiamo evitare di eseguire le funzioni RegSetValueEx che registrano i cambiamenti di data e fanno quindi avanzare l'orologio del programma...
Insomma, si possono trovare svariate soluzioni, dalle più semplici alle più complicate, il tutto è a piacere...
The_Dux