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