Lindo 6.1
From UIC
Lindo 6.1 Cracking
Contents |
| Lindo 6.1 | |
|---|---|
| Author: | Spider |
| Email: | spider_xx87 (AT) hotmail (DOT) com |
| Website: | http://bigspider.has.it/ |
| Date: | 19/11/2002 (dd/mm/yyyy) |
| Level: |
|
| Language: | Italian |
| Comments: | Lei: "Da quanto tempo non fai sesso?" Lui: "Dunque... lunedì, martedì, mercoledì... ...ah! ma tu dici con una donna??" Ezio Greggio in "Killer per caso" |
Un esempio di target in cui la difficoltà non è insita nella protezione ma nella immane quantità di codice su cui essa è imperniata :)
Introduzione
Una tra le cose che più scoraggiano un reverser intento a far saltare la protezione di un programma è il cosiddetto jungle. Il termine "jungle", com'è evidente, significa "giungla", e fa riferimento al corposo e intricato codice su cui alcune protezioni si basano, addobbando le più banali procedure con quantità inimmaginabili di codice e call inutili. Sapersi districare in tali situazioni non è sempre facile, ma con un po' di intuito e, perché no, un po' di culo :) si può senz'altro riuscire a superare protezioni di questo tipo, senza uscir di senno studiando centinaia di kb di righe di codice assembler senza senso (o quasi).
Tools
- Softice 4.05 - IL debugger :)
- IDA Pro 4.15 o superiore - IL disassembler :)
- Un qualunque editor esadecimale (io uso Hex Workshop)
Si presume nel lettore una conoscenza base dei comandi di IDA; in caso contrario si rimanda al tutorial di Quequero.
Link e Riferimenti
h++p://www(dot)lindo(dot)com
Notizie sul programma
A che serve? Boh, non mi sono posto il problema... ad ogni modo il programma, una volta installato ed eseguito, mostra una finestra in cui permette di digitare una password o di eseguire il programma in licenza demo... Infatti esso dispone di 6 tipi di licenze: demo, trial, Super, Hyper, Industrial, Extended; ogni licenza ha le sue limitazioni:
Constraints: 150 | ? | 1000 | 4000 | 16000 | unlimited |
Variables: 300 | ? | 2000 | 8000 | 32000 | unlimited |
Int variables: 50 | ? | 200 | 800 | 3200 | unlimited |
Nonzeros: 2000000 | ? | N/A | N/A | N/A | N/A |
Perché riporto tutti questi dettagli? Lo scoprirete a tempo debito.
Essay
Ok, cominciamo. Installiamo il programma, eseguiamo (nel frattempo iniziamo a disassemblare da IDA), ed ecco la finestrella che ci avverte che se non inseriamo una password corretta si avvierà il programma in versione demo. Mettiamo una password a caso, settiamo gli usuali breakpoint su GetWindowTextA e GetDlgItemTextA, e clicchiamo su OK. SoftICE poppa diligentemente per via del bpx sulla seconda API; premiamo F11 per tornare al codice del programma (il codice è preso da IDA):
BEGTEXT:0041B998 mov eax, offset byte_4AD6A4
BEGTEXT:0041B99D push eax ; lpString
BEGTEXT:0041B99E push 1AAh ; nIDDlgItem
BEGTEXT:0041B9A3 push [ebp+hDlg] ; hDlg
BEGTEXT:0041B9A6 call cs:GetDlgItemTextA
BEGTEXT:0041B9AD mov [ebp+var_8], eax ;<---noi siamo qui
BEGTEXT:0041B9B0 mov eax, [ebp+var_8]
BEGTEXT:0041B9B3 mov ds:byte_4AD6A4[eax], 0
BEGTEXT:0041B9BA push 1 ; nResult
BEGTEXT:0041B9BC push [ebp+hDlg] ; hDlg
BEGTEXT:0041B9BF call cs:EndDialog
Osservando il codice, vediamo che il serial da noi immesso viene salvato all'offset 004AD6A4 (che in IDA rinominiamo Serial); subito dopo la dialog viene chiusa, quindi è chiaro che il controllo del serial avviene da qualche altra parte. Osservando le xreferences a quella locazione, vediamo che ci sono ben 9 punti di codice che fanno riferimento ad essa; impiegheremmo 5 minuti a controllarle tutte, ma siccome noi siamo pigroni preferiamo impiegare 4 minuti usando softice (con il considerevole risparmio di 1 minuto!)... Torniamo dunque in softice, rimettiamo il breakpoint su GetDlgItemTextA, e quando softice poppa sullo schermo mettiamo un bel "bpm 4AD6A4 rw", ovvero chiediamo al softice di intercettare qualunque accesso in lettura/scrittura alla locazione in cui sappiamo che è contenuto il nostro serial. Premiamo F5 e softice riappare subito dopo. Un'occhiata veloce al codice e ci accorgiamo di trovarci su una piccola procedura che fa qualche manipolazione di stringhe, per cui premiamo F11 per giungere a questo codice:
BEGTEXT:0042C24F call strlen
BEGTEXT:0042C254 test eax, eax
BEGTEXT:0042C256 ja short loc_42C273
BEGTEXT:0042C258 mov eax, [ebp+var_18]
BEGTEXT:0042C25B mov dword ptr [eax], 0
BEGTEXT:0042C261 mov eax, [ebp+var_14]
BEGTEXT:0042C264 mov dword ptr [eax], 0
BEGTEXT:0042C26A mov [ebp+var_10], 0FFFFFFFEh
BEGTEXT:0042C271 jmp short loc_42C2B2
BEGTEXT:0042C273 ; ---------------------------------------------------------------------------
BEGTEXT:0042C273
BEGTEXT:0042C273 loc_42C273: ; CODE XREF: sub_42C1E4+72�j
BEGTEXT:0042C273 mov eax, offset Serial
BEGTEXT:0042C278 call strlen
BEGTEXT:0042C27D mov [ebp+var_1C], eax
BEGTEXT:0042C280 mov eax, [ebp+var_1C]
BEGTEXT:0042C283 mov edx, [ebp+var_14]
BEGTEXT:0042C286 cmp eax, [edx]
BEGTEXT:0042C288 jle short loc_42C296
BEGTEXT:0042C28A mov eax, [ebp+var_14]
BEGTEXT:0042C28D mov eax, [eax]
BEGTEXT:0042C28F mov ds:Serial[eax], 0
BEGTEXT:0042C296
BEGTEXT:0042C296 loc_42C296: ; CODE XREF: sub_42C1E4+A4�j
BEGTEXT:0042C296 mov edx, offset Serial
BEGTEXT:0042C29B mov eax, [ebp+var_18]
BEGTEXT:0042C29E mov eax, [eax]
BEGTEXT:0042C2A0 call strcpy_
BEGTEXT:0042C2A5 mov eax, offset Serial
BEGTEXT:0042C2AA call strlen
BEGTEXT:0042C2AF mov [ebp+var_10], eax
Qui non troviamo nulla di interessante... A parte qualche chiamata a strlen e strcpy non troviamo niente di strano. Risaliamo perciò il codice e andiamo alla procedura chiamante:
BEGTEXT:0042BFE2 jnz loc_42C05C
BEGTEXT:0042BFE8 lea edx, [ebp+var_14]
BEGTEXT:0042BFEB lea eax, [ebp+var_20]
BEGTEXT:0042BFEE call sub_42C1E4 ;noi veniamo da qui
BEGTEXT:0042BFF3 mov [ebp+var_C], eax
BEGTEXT:0042BFF6 mov [ebp+var_18], 1
BEGTEXT:0042BFFD cmp [ebp+var_C], 0FFFFFFFEh
BEGTEXT:0042C001 jnz short loc_42C00F ; salta se il serial non è nullo
BEGTEXT:0042C003 mov [ebp+var_28], 0FFFFFFFEh ; chiusura dialog e avvio demo
BEGTEXT:0042C00A jmp loc_42C108
BEGTEXT:0042C00F ; ---------------------------------------------------------------------------
BEGTEXT:0042C00F
BEGTEXT:0042C00F loc_42C00F: ; CODE XREF: sub_42BF51+B0�j
BEGTEXT:0042C00F cmp [ebp+var_C], 0FFFFFFFDh
BEGTEXT:0042C013 jnz short loc_42C021
BEGTEXT:0042C015 mov [ebp+var_28], 0FFFFFFFDh
BEGTEXT:0042C01C jmp loc_42C108
BEGTEXT:0042C021 ; ---------------------------------------------------------------------------
BEGTEXT:0042C021
BEGTEXT:0042C021 loc_42C021: ; CODE XREF: sub_42BF51+C2�j
BEGTEXT:0042C021 mov eax, [ebp+var_C]
BEGTEXT:0042C024 mov byte ptr [eax+ebp-1CCh], 0
BEGTEXT:0042C02C lea eax, [ebp+var_1CC]
BEGTEXT:0042C032 push eax
BEGTEXT:0042C033 push [ebp+var_30]
BEGTEXT:0042C036 lea eax, [ebp+var_8]
BEGTEXT:0042C039 push eax
BEGTEXT:0042C03A push [ebp+var_2C]
BEGTEXT:0042C03D lea eax, [ebp+var_4]
BEGTEXT:0042C040 push eax
BEGTEXT:0042C041 call ValidatePassword ;cooosa???
BEGTEXT:0042C046 mov [ebp+var_1C], eax
BEGTEXT:0042C049 cmp [ebp+var_1C], 0
BEGTEXT:0042C04D jle short loc_42C05C
BEGTEXT:0042C04F mov eax, [ebp+var_1C]
BEGTEXT:0042C052 call sub_42C183
BEGTEXT:0042C057 jmp loc_42BFD7
BEGTEXT:0042C05C ; ---------------------------------------------------------------------------
BEGTEXT:0042C05C
BEGTEXT:0042C05C loc_42C05C: ; CODE XREF: sub_42BF51+91�j
BEGTEXT:0042C05C ; sub_42BF51+FC�j
BEGTEXT:0042C05C push [ebp+arg_24]
BEGTEXT:0042C05F push [ebp+arg_20]
BEGTEXT:0042C062 mov eax, [ebp+arg_34]
BEGTEXT:0042C065 push dword ptr [eax]
BEGTEXT:0042C067 push [ebp+arg_1C]
BEGTEXT:0042C06A push [ebp+arg_18]
BEGTEXT:0042C06D push [ebp+arg_14]
BEGTEXT:0042C070 push [ebp+var_30]
BEGTEXT:0042C073 lea eax, [ebp+var_8]
BEGTEXT:0042C076 push eax
BEGTEXT:0042C077 push [ebp+var_2C]
BEGTEXT:0042C07A lea eax, [ebp+var_4]
BEGTEXT:0042C07D push eax
BEGTEXT:0042C07E call VerifyLicense ;coooooosaaaa???
BEGTEXT:0042C083 mov [ebp+var_1C], eax
BEGTEXT:0042C086 cmp [ebp+var_1C], 0
BEGTEXT:0042C08A jnz short loc_42C094 ; deve saltare
BEGTEXT:0042C08C mov eax, [ebp+arg_10]
BEGTEXT:0042C08F cmp dword ptr [eax], 1
BEGTEXT:0042C092 jz short loc_42C096
BEGTEXT:0042C094
BEGTEXT:0042C094 loc_42C094: ; CODE XREF: sub_42BF51+139�j
BEGTEXT:0042C094 jmp short loc_42C09F
Davvero gentili i programmatori... Hanno lasciato in chiaro i nomi di due chiamate di libreria che si trovano nella dll Lindolm.dll; oltre a queste due simpatiche funzioni della dll, IDA ce ne comunica altre:
.idata:00FA088C ; Imports from LINDOLM.dll
.idata:00FA088C ;
.idata:00FA088C extrn __imp_CheckSentry:dword ; DATA XREF: CheckSentry�r
.idata:00FA0890 extrn __imp_CreateDemoLicense:dword
.idata:00FA0890 ; DATA XREF: CreateDemoLicense�r
.idata:00FA0894 extrn __imp_StartSentry:dword ; DATA XREF: StartSentry�r
.idata:00FA0898 extrn __imp_ValidatePassword:dword
.idata:00FA0898 ; DATA XREF: ValidatePassword�r
.idata:00FA089C extrn __imp_VerifyLicense:dword ; DATA XREF: VerifyLicense�r
Disassemblando la lindolm.dll si trovano decine di nomi interessanti, ma provate ad analizzare una qualunque di queste procedure... C'è da perdersi in mezzo a tutte le call e subroutines varie. Il reversing si prospetta lungo e noioso, per cui optiamo per un molto più rilassante e rapido cracking :-)
Dobbiamo però trovare il modo di evitare il fastidioso jungle e trovare un solido punto di attacco del programma. Ricordate lo schema che riguardava le limitazioni per i vari tipi di licenza del programma? È chiaro che il programma deve tenere in memoria una variabile per ricordarsi in quale licenza il programma debba girare, e regolarsi di conseguenza per gestire le limitazioni. Inoltre, dovrà anche ricordarsi da qualche parte le limitazioni per ogni tipo di licenza:
Constraints: 150 | ? | 1000 | 4000 | 16000 | unlimited |
Variables: 300 | ? | 2000 | 8000 | 32000 | unlimited |
Int variables: 50 | ? | 200 | 800 | 3200 | unlimited |
Nonzeros: 2000000 | ? | N/A | N/A | N/A | N/A |
Trovare questa tabella tra i dati del programma potrebbe essere un ottimo punto di partenza. Ancora una volta il softice ci semplifica il lavoro. Avviamo il programma, digitiamo "MAP32 lindow32", ottenendo il seguente output:
Owner Obj Name Obj# Address Size Type LINDOW32 BEGTEXT 0001 017F:00410000 00000000 CODE RO LINDOW32 DGROUP 0002 0187:004A0000 00000000 IDATA RW LINDOW32 .bss 0003 0187:00580000 00000000 UDATA RW LINDOW32 .idata 0004 0187:00FA0000 00000000 IDATA RW LINDOW32 .edata 0005 0187:00FB0000 00000000 IDATA R0 LINDOW32 .reloc 0006 0187:00FC0000 00000000 IDATA R0 LINDOW32 .rsrc 0007 0187:00FD0000 00000000 IDATA R0
La tabella che stiamo cercando è presumibilmente contenuta nella seconda sezione, che è appunto la sezione dei dati. Stranamente i campi Size sono azzerati, cmq è chiaro che la sezione DGROUP sia ampia 580000-4A0000=E0000 bytes. Dobbiamo adesso cercare in memoria i dati interessati basandoci su ciò che ci è noto di essi, ad esempio il valore 32000 (decimale) = 00007D00 (hex) che è sicuramente contenuto nella tabella. Per cui da softice digitiamo:
A cui softice replica con:
Pattern found at 0187:0057E104 (000DE104)
Bingo! Andando a vedere con IDA a quella locazione e alle locazioni circostanti scopriamo di incontrare proprio i valori che avevamo nella tabella (ho già rinominato le locazioni, raccolto ad array, e convertito tutto in decimale):
DGROUP:0057E0DC ; DATA XREF: sub_487CE0+19F�r
DGROUP:0057E0F4 Variables dd 300, 500, 2000, 8000, 32000, 200000
DGROUP:0057E0F4 ; DATA XREF: sub_487CE0+1AC�r
DGROUP:0057E10C Int_variables dd 50, 50, 200, 800, 3200, 20000
DGROUP:0057E10C ; DATA XREF: sub_487CE0+1BF�r
Abbiamo dunque tre array di sei elementi ciascuno, e guarda caso proprio 6 sono i tipi di licenza consentiti... Adesso che sappiamo dov'è la tabella possiamo analizzare le xref. Andiamo alla prima:
BEGTEXT:00487E7A ; sub_487CE0+B7�j ...
BEGTEXT:00487E7A mov eax, ds:dword_57E0C8
BEGTEXT:00487E7F mov edx, ds:Constraints[eax*4] ;l'xref proviene da qui
BEGTEXT:00487E86 mov ds:dword_5801F4, edx
BEGTEXT:00487E8C mov edx, ds:Variables[eax*4]
BEGTEXT:00487E93 mov ds:dword_58025C, edx
BEGTEXT:00487E99 mov ds:dword_E793F0, edx
BEGTEXT:00487E9F mov eax, ds:Int_variables[eax*4]
BEGTEXT:00487EA6 mov ds:dword_580264, eax
Il programma prende la dword all'offset 57E0C8 e la utilizza come indici per i tre array visti prima... È chiaro che se poniamo questa variabile a 5 il programma si autoconvincerà di essere registrato con la massima licenza possibile :-)
Rinominiamo la dword_57E0C8 con il nome di current_mode, e osserviamo le sue xreferences:
Up o BEGTEXT:00487D5A push offset current_mode Up o BEGTEXT:00487DEB push offset current_mode Up w BEGTEXT:00487E14 mov ds:current_mode,eax Up r BEGTEXT:00487E7A mov eax,ds:current_mode
L'ultima xref la conosciamo già; osservando il codice della penultima si nota anche ad una rapida occhiata che si occupa solo di azzerare un po' di variabili. Ci resta solo da analizzare il codice delle prime 2 xref:
BEGTEXT:00487D34 push offset off_4B5420
BEGTEXT:00487D39 push offset dword_57E0D8
BEGTEXT:00487D3E push offset dword_57E0D4
BEGTEXT:00487D43 push offset dword_57E0D0
BEGTEXT:00487D48 lea eax, [ebp+var_14]
BEGTEXT:00487D4B push eax
BEGTEXT:00487D4C lea eax, [ebp+var_10]
BEGTEXT:00487D4F push eax
BEGTEXT:00487D50 push offset dword_57E088
BEGTEXT:00487D55 push offset dword_57E0CC
BEGTEXT:00487D5A push offset <font color=blue>current_mode</font> ;prima xref
BEGTEXT:00487D5F mov edx, [ebp+var_8]
BEGTEXT:00487D62 push edx
BEGTEXT:00487D63 push esi
BEGTEXT:00487D64 mov ecx, offset dword_57E084
BEGTEXT:00487D69 mov ebx, offset off_4B5418
BEGTEXT:00487D6E mov edx, offset unk_57E080
BEGTEXT:00487D73 mov eax, offset off_4B5450
BEGTEXT:00487D78 call sub_42BF51
BEGTEXT:00487D7D mov ebx, [esi]
BEGTEXT:00487D7F cmp ebx, 1
BEGTEXT:00487D82 jnz loc_487E64
BEGTEXT:00487D88 cmp eax, 0FFFFFFFDh
BEGTEXT:00487D8B jnz short loc_487D94
BEGTEXT:00487D8D mov [edi], ebx
BEGTEXT:00487D8F jmp loc_487E7A ;questo ci rimanda alla quarta xreference
BEGTEXT:00487D94 ; ---------------------------------------------------------------------------
BEGTEXT:00487D94
BEGTEXT:00487D94 loc_487D94: ; CODE XREF: sub_487CE0+AB�j
BEGTEXT:00487D94 cmp eax, 0FFFFFFFEh
BEGTEXT:00487D97 jnz loc_487E7A
BEGTEXT:00487D9D mov ecx, offset dword_57E084
BEGTEXT:00487DA2 mov ebx, offset off_4B5418
BEGTEXT:00487DA7 mov edx, offset unk_57E080
BEGTEXT:00487DAC mov eax, offset off_4B5450
BEGTEXT:00487DB1 call sub_42C113
BEGTEXT:00487DB6 mov edx, offset off_4B5460
BEGTEXT:00487DBB mov eax, offset off_4B5420
BEGTEXT:00487DC0 call loc_48AE1D
BEGTEXT:00487DC5 push offset off_4B5420
BEGTEXT:00487DCA push offset dword_57E0D8
BEGTEXT:00487DCF push offset dword_57E0D4
BEGTEXT:00487DD4 push offset dword_57E0D0
BEGTEXT:00487DD9 lea eax, [ebp+var_14]
BEGTEXT:00487DDC push eax
BEGTEXT:00487DDD lea eax, [ebp+var_10]
BEGTEXT:00487DE0 push eax
BEGTEXT:00487DE1 push offset dword_57E088
BEGTEXT:00487DE6 push offset dword_57E0CC
BEGTEXT:00487DEB push offset <font color=blue>current_mode</font> ;seconda xref
BEGTEXT:00487DF0 mov edi, [ebp+var_8]
BEGTEXT:00487DF3 push edi
BEGTEXT:00487DF4 push esi
BEGTEXT:00487DF5 mov ecx, offset dword_57E084
BEGTEXT:00487DFA mov ebx, offset off_4B5418
BEGTEXT:00487DFF mov edx, offset unk_57E080
BEGTEXT:00487E04 mov eax, offset off_4B5450
BEGTEXT:00487E09 call sub_42BF51
BEGTEXT:00487E0E test eax, eax
BEGTEXT:00487E10 jz short loc_487E73 ; questo ci rimanda alla quarta xref,
BEGTEXT:00487E10 ; quindi al momento del salto la variabile
BEGTEXT:00487E10 ; current_mode DEVE essere stata settata.
BEGTEXT:00487E12 xor eax, eax
BEGTEXT:00487E14 mov ds:<font color=blue>current_mode</font>, eax
[etc etc...]
In particolare poniamo l'attenzione alla prima parte di codice: contiene una serie di push (tra cui il push offset current_mode che costituiva la prima xreference) e una call; entrando dentro di essa scopriamo che è la stessa precedura che avevamo visto all'inizio e in cui sono contenute le chiamate a ValidatePassword e VerifyLicense!! Eravamo praticamente a due passi dalla soluzione e non ce n'eravamo accorti!... Gli scherzi del jungle ;)
Adesso mi sembra fin troppo chiaro quello che dobbiamo fare: facciamo saltare la call sub_42BF51 all'indirizzo 00487D78, aggiungiamo due righe di codice per settare a 5 la variabile current_mode, e noppare i due jnz dopo la call per essere certi di andare dove vogliamo noi :)
Questo:
BEGTEXT:00487D7D mov ebx, [esi]
BEGTEXT:00487D7F cmp ebx, 1
BEGTEXT:00487D82 jnz loc_487E64
BEGTEXT:00487D88 cmp eax, 0FFFFFFFDh
BEGTEXT:00487D8B jnz short loc_487D94
BEGTEXT:00487D8D mov [edi], ebx
BEGTEXT:00487D8F jmp loc_487E7A
Deve diventare così:
push 5 ;\
pop dword ptr[57E0C8] ;|impostiamo la licenza al massimo ;)
jmp 487E7A ;saltiamo il resto del codice "pericoloso" ;))
Effettuiamo le modifiche, eseguiamo e andiamo nell'aboutbox: nessuna traccia di time limit, limitazioni (Contraints, Variables, Int_Variables) sparite.
Spider
Note Finali
Saluti a Delfina con cui ho condiviso il cracking di questo programma.
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.