|
- UNIVERSITA'
ITALIANA CRACKING - |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
KEY FILE |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| INTRODUZIONE: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Eccoci qui' al quarto appuntamento con la UIC. Il programma in oggetto non e' molto complicato da crackare, ma presenta una novita' rispetto ai precedenti, e' stato scritto con un linguaggio ad alto livello, e non in ASM come eravate abituati fino ad adesso, in pratica si avvicina di piu' al mondo reale della programmazione, con un sacco di routine aggiunte dal compilatore ed inutili, il programma e' funzionante (solamente crea un piccolo file su disco e lo legge) per controllare se e' stato registrato correttamente premete su Register e successivamente su About, quindi ritornate al programma principale e chiudetelo. Lo scopo di questo corso e' di creare un key generator, attenzione che i nomi non possono superare i 30 caratteri, quindi tenetene conto. Avrei voluto aggiungerci un piccolo PE cripter, ma per cause estranee non ho avuto il tempo di lavorarci su', arrivera' in un secondo tempo. Consideratelo un relax in attesa del corso 5 di Quequero, che mi sa' non sara passeggiata come questa. Buon divertimento, per eventuali informazioni o critiche: Tin Man |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Salve a tutti e ben tornati o ben arrivati alla UIC, dunque Tin_Man si è fatto veramente il mazzo per creare questo programmetto dati alcuni gravi problemi che gli sono piombati addosso, quindi mi scuso con voi per questo scarno corso ma data la semplicità del programma potete davvero considerarlo un break prima del 5° corso che credo sarà il riassunto di varie tecniche oltre ad alcune piccole implementazioni :) insomma stavolta cercherò di rendervi la vita difficile e sarò abbastanza cattivello, ci avvicineremo in questo modo ad un target ben più evoluto. Dunque, torniamo a noi, il programma è scritto come detto prima in alto livello, ha un simpatico trucchetto e dovete creare il vostro bravo keyfile, ma prima di cominciare mi sembra doveroso spiegare come funziona questo tipo di protezione. Tutti i programmi protetti da key-file devono fare una cosa all'avvio cioè, aprire il keyfile e conseguentemente andare a leggerlo, il key-file non deve necessariamente essere letto all'avvio ma può anche essere decifrato alla pressione di un particolare pulsante come ad esempio il tasto "save", in questo modo se il programma trova il key-file e lo vede come valido allora ci da accesso a questa funziona altrimenti...nada. Per leggere un file possiamo utilizzare svariate funzioni, sotto dos potremmo utilizzare l'int 21h ma visto che siamo sotto W32 possiamo avvalerci di due tecniche principali cioè: 1) L'utilizzo della funzione _lopen e _lread 2) L'utilizzo di una struttura che usi OpenFile, SetFilePointer e poi ReadFile
Ora ve le spiego tutte e due altrimenti non sapreste cosa fare. Prendiamo il come esempio il primo punto, per leggere un file abbiamo bisogno come prima cosa di aprirlo e per questo possiamo usare _lread, ecco la sintassi:
HFILE _lopen(
Come vedete non bisogna far altro che specificare un puntatore al file da aprire e poi scegliere un tipo di modalità di accesso al file stesso, le più usate sono generalmente:
Il modo d'uso è molto semplice: Extrn _lopen: Proc .data OF_READ = 0000H
Nome db 'File.exe',0 .code Start:
push offset Name push OF_READ
Call _lopen Una volta aperto il file con questa funzione lo dobbiamo andare a leggere con _lread che ha questa semplice sintassi:
UINT _lread(
dobbiamo conoscere per prima cosa l'handle del file che però viene riportato in eax dopo la chiamata a _lopen, ci serve un puntatore ad un buffer che conterrà i dati letti dal file e poi dobbiamo sapere quanti byte vogliamo leggere da questo file, in sostanza il nostro mini programma diventerebbe così: Extrn _lopen:Proc .data OF_READ = 0000H Nome db 'File.exe',0 Handle dd ? Buffer dd ? .code Start: push offset Name push OF_READ Call _lopen mov handle, eax push handle push offset Buffer push 20 call _lread Come vedete prendiamo il file di nome File.exe, lo apriamo, pushamo l'handle nello stack, pushiamo l'offset di un buffer dove finiranno i dati letti e decidiamo di leggere 20 bytes, quindi basterebbe guardare queste poche istruzioni per vedere cosa viene letto dal file, basta ricordare solo che le istruzioni non vengono pushate nello stesso ordine in cui le mettiamo noi.
Bene, adesso esaminiamo il secondo modo di aprire e leggere un file, se non vogliamo usare i vecchi _lopen e _lread possiamo utilizzare la struttura OpenFile, SetFilePointer, ReadFile, dunque iniziamo vedendo la sintassi del comando OpenFile: HFILE OpenFile(
Bene, bene, per prima cosa abbiamo bisogno di un puntatore al nome del file, poi di un puntatore ad un buffer che conterrà le informazioni e si deve infine specificare la modalità di lettura, come vedete tranne che per l'ultimo parametro la funzione presenta le stesse caratteristiche di _lopen, quindi iniziamo:
Extrn OpenFile:Proc
.data OF_READ = 0000H Nome db 'File.exe',0 Buffer dd ? Handle dd ? .code Start: push offset Nome Come vedete pushiamo il file nello stack, pushiamo poi l'offset dek buffer che conterrà i nostri cari dati e quindi la modalità di apertura cioè, OF_READWRITE, in pratica apriamo il file in modalità read & write, slaviamo l'handle e poi facciamo un cmp eax, -1, perchè? È presto detto, perchè se eax riporta come valore dopo la chiamata -1 (in hex FFFFFFFF) vuol dire che c'è stato un errore, se non ci sono allora saltiamo ad una nuova routine. Adesso sappiamo come si apre, ma dobbiamo settare un puntatore a questo file, a questo scopo sfrutteremo l'API SetFilePointer che presenta questa sintassi:
DWORD SetFilePointer(
lo so che può sembrarvi un tantino ostica ma in realtà è molto semplice, innanzitutto abbiamo la necessità di settare un puntatore per far sapere alla successiva API ReadFile cosa e dove andare a leggere, questa struttura è molto comoda perchè se in un programma abbiamo bisogno di leggere più volte un file in diverse posizioni, non dobbiamo far altro che specificare l'offset che ci interessa, così velocizziamo enormemente il nostro programma perchè basterebbe mettere in un buffer di volta in volta l'offset che ci serve e richiamare sempre la stessa routine di lettura, cosa che invece non possiamo fare con _lopen e _lread pechè non ci danno la possibilità di specificare offsets. Allora vediamo come funziona questa API, come prima cosa abbiamo bisogno dell'handle del file ma come già detto questo viene riportato in eax dopo la chiamata a OpenFile, poi abbiamo bisogno di inserire il numero di byte che vogliamo muovere nel puntatore, ma questo valore è spesso settato a NULL. Adesso si presenta uno strano parametro, a cosa serve? Dunque se il parametro è NULL allora la funzione SetFilePointer opererà solo su file grandi al massimo 2^32-2 bytes, se invece specifichiamo qualche parametro allora il SetFilePointer può operare al massimo su file di ben 2^64-2 bytes, e poi dobbiamo specificare una Dword nella quale andremo a salvare l'offset, l'offset serve ad indicare al puntatore a quanti byte dall'inizio dobbiamo iniziare la lettura del file, bene bene, il nostro programma diventerà allora così: Extrn OpenFile:Proc Extrn ExitProcess:Proc Extrn SetFilePointer:Proc OF_READ = 0000H OF_WRITE = 0001H OF_READWRITE = 0002H Nome db 'File.exe',0 Buffer dd ? Handle dd ? NULL equ 0 Read_Point dd 00001234H .code Start:
push offset Nome call ExitProcess okkei: push Handle push NULL push NULL push Dword Ptr[Read_Point] call SetFilePointer
Come vedete in questo caso apriamo il file e saltiamo alla label "okkei" se non ci sono errori, quindi pushiamo l'handle del file, settiamo il secondo e terzo parametro a NULL e quindi pushiamo l'offset al quale vogliamo leggere (che è 1234h) e quindi chiamiamo la nostra agognata API SetFilePointer. Adesso possiamo andare a leggere il filuzzo, la sintassi di ReadFile è:
BOOL ReadFile(
Di cosa abbiamo bisogno stavolta? Presto detto, ci serve come sempre l'Handle del file che a questo punto è già stato salvato, poi dobbiamo avere l'indirizzo di un buffer che rivecerà i dati letti , il numero di bytes che vogliamo leggere, un altro buffer nel quale finirà il numero di bytes letti ed infine l'indirizzo di una struttura per i dati, quest'ultimo parametro sarà settato a NULL perchè per il momento non vogliamo avvalerci di strutture, ecco quindi come si trasforma il nostro programma ormai finito:
Extrn OpenFile:Proc Extrn ExitProcess:Proc Extrn SetFilePointer:Proc Extrn ReadFile:Proc
.data OF_READ = 0000H Nome db 'File.exe',0 Buffer dd ? Handle dd ? NULL equ 0 Read_Point dd 00001234H Read_Buffer dd ? Bytes_Letti dd ? Numero_bytes dd 00000012H .code Start:
push offset Nome call ExitProcess okkei: push Handle push NULL push NULL push Dword Ptr[Read_Point] call SetFilePointer push Handle call ReadFile
Se vi capitasse una routine del genere non dovreste far altro che sgamare l'offset ed andare a vedere cosa trovate, oppure potreste semplicemente spulciare nel Read_Buffer, dopo questa routine i dati inizieranno a venire manipolati. Ma non è finito, infatti spesso i linguaggi ad alto livello utilizzano un altro modo per leggere i file cioè tramite l'API CreateFile, che ha una sintassi piuttosto complicata:
HANDLE CreateFile(
Allora il programma stavolta non lo faccio perchè tanto è come il ReadFile, la stessa cosa in pratica, abbiamo bisogno di un puntatore al nome del file, ci serve sapere il tipo di accesso che vogliamo (rea, write o tutti e due) il tipo di condivisione che vogliamo avere, in pratica qua specifichiamo se il file può venir letto anche da programmi esterni a quello che l'ha aperto (FILE_SHARE_READ), oppure se può essere scritto anche da programmi esterni (FILE_SHARE_WRITE). Il 4° parametro serve per determinare se l'handle può essere utilizzato anche da processi figli o meno, poi dobbiamo sapere la modalità con la quale creare il filuzzo, qui si possono usare cinque parametri, uno per la creazione di un nuovo file (CREATE_NEW) uno per sovrascrivere un file (CREATE_ALWAYS) ed un altro per aprire solamente il file specificato (OPEN_EXISTING), c'è poi OPEN_ALWAYS che serve ad aprire il file specificato e se non lo trova lo crea e poi TRUNCATE_EXISTING che una volta aperto il file lo tronca fino a farlo diventare di 0 bytes ecc....Dobbiamo poi specificare gli attributi che dovrebbe avere il file, di questi i principali sono:
FILE_ATTRIBUTE_HIDDEN FILE_ATTRIBUTE_NORMAL FILE_ATTRIBUTE_READONLY FILE_ATTRIBUTE_SYSTEM FILE_ATTRIBUTE_TEMPORARY
Se non volete assegnare nessun attributo particolare utilizzare FILE_ATTRIBUTE_NORMAL, per spiegare il penultimo parametro ci vorrebbe un libro, vi dico che solo che serve per specificare il tipo di scrittura da attuare sul file, noi non useremo nulla perchè non dobbiamo scrivere nulla ma se dovessimo scrivere qualcosa potremmo usare: FILE_FLAG_WRITE_THROUGH, che in pratica scrive i dati attraverso una cache e da questa poi sul disco duro. L'ultimo param invece specifica un tipo di lettura generica su un file template quindi non ci serve. Un esempio forse è meglio che ve lo faccio, ecco come si potrebbe aprire un file:
Extrn CreateFileA:Proc .data OF_READ = 0000H NULL equ 0 .code Start: push offset Nome push OF_READ push FILE_SHARE_READ push NULL push OPEN_EXISTING push NULL push NULL call CreateFileA
Allora, in questo caso vediamo che apriamo un file di nome: File.exe, in modalità di sola lettura e condividiamo la lettura anche da programmi esterni, lo apriamo con OPEN_EXISTING, in questo modo se il programma non trova il file invece di crearlo ci segnala l'errore. Okkei adesso siete diventati degli apritori di file :) quindi divertitevi con questo programma che è abbastanza semplice ma ricordate sempre che il key-file viene aperto, letto e poi i dati vengono confrontati con dei dati che il programma ha in se, oppure con dei dati creti sul momento, come? Bhè, ad esempio il programmatore potrebbe creare un key-file personalizzato in base al vostro nome, in pratica metto il nick da qualche parte nel key-file, in plain-text o in forma crittata, lo legge, lo decritta se necessario, lo manipola e dalla manipolazione va a creare dei dati che verrano poi confrontati con quelli presi dal file, ricordate che qualunque metodo di crittazione venisse usato (in genere si tratta si semplici maschere Xor) il programma dovrebbe comunque confrontare da qualche parte i suoi bytes con i vostri, quindi occhio a tutti i cmp che trovate e spulciate per benino tutti i buffer ed i valori pushati dalle varie API perchè saranno loro a condurvi sulla retta via. Raga scusatemi ancora per questo scarno tutorial ma l'ho scritto in sole tre ore quindi abbiate pazienza che è tardissimo, devo andare a letto e devo uppare domani mattina il sitozzo scusatemi di nuovo anche se la lezione sembra più di programmazione che di reversing ma proprio a questo proposito ho messo tutti gli esempi in asm perchè è così che li troverete nei programmi, quindi raga buon divertimento ciauzzzzzzzzzz |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| OBIETTIVI: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Quequero |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||