Zoom Icon

Corso UIC Newbies 07

From UIC

Soluzione al 7° corso newbies - KeyFile sniffing

Contents


Infos
Author: AndreaGeddon
Email: andreageddon@hotmail.com
Website: http://andreageddon.8m.com
Date: 09/10/2000 (dd/mm/yyyy)
Level: Slightly hard
Language: Italian Image:Flag_Italian.gif
Comments: I must not fear. Fear is the mind killer. Fear is the little death that brings to total obliteration



Introduzione

Vediamo che bisogna fare in caso di protezione a KeyFile.


Tools


Link e Riferimenti

Questo è il Corso UIC Newbies n°07 disponibile alla pagina Corsi UIC Newbies


Notizie sul programma

Cinque livelli diversi di keyfeeling! L'intento è quello di concentrarsi sulle operazioni su file, cioè ricerca, apertura e lettura. La parte crittografica (analisi dei dati letti e algo vari di calcolo) l'ho tralasciata perchè in fin dei conti si può far rientrare nel filone delle protezioni Nome/Serial e in questo corso non è quello che ci interessa.


Essay

Suvvia, diamoci dentro.

LIVELLO 1

Iniziamo dal primo livello. La prima cosa da fare è di stabilire il nome del keyfile e il suo percorso. Se avete FileMon è semplice: lanciatelo, lanciate il crackme, e premete il tasto relativo al livello 1. Otterete la seguente riga:

Settimo      OPEN         c:\Keyfile.and          NOT FOUND        OPEN EXISTING....

che ci dice già tutto. Il file deve chiamarsi keyfile.and e si deve trovare nella root. Se invece non avete FileMon, dobbiamo sniffare il keyfile tramite softice. Innanzitutto premiamo il tasto INFO a destra relativo al primo livello: ci comunicherà di usare CreateFileA. Bene! ctrl+d per poppare il sice, digitiamo bpx CreateFileA, usciamo da softice e premiamo il tasto del livello 1. Finirete nel seguente codice:

00401574 mov [ebp-04], 00000000
0040157B push 00000000 parametri vari per CreateFileA
0040157D push 00000080
00401582 push 00000003
00401584 push 00000000
00401586 push 00000000
00401588 push 80000000
* Possible StringData Ref from Data Obj ->"C:\KeyFile.And"
0040158D push 00404154
* Reference To: KERNEL32.CreateFileA, Ord:0031h
00401592 Call dword ptr [004052AC] qui è dove abbiamo breakato
00401598 mov dword ptr [ebp-3C], eax --> vi ritroverete qui!
0040159B cmp dword ptr [ebp-3C], FFFFFFFF controlla se il file è stato aperto

okei, già seguendo il disassemblato del WDasm siamo a cavallo. La stringa passata come nome del file a CreateFileA è "C:\KeyFile.and", quindi sappiamo nome e locazione. Infatti se vediamo la sintassi di CreateFile:

HANDLE CreateFile(
LPCTSTR lpFileName, // Puntatore al nome del file
DWORD dwDesiredAccess, // access (read-write) mode
DWORD dwShareMode, // share mode
LPSECURITY_ATTRIBUTES lpSecurityAttributes, // pointer to security attributes
DWORD dwCreationDistribution, // how to create
DWORD dwFlagsAndAttributes, // file attributes
HANDLE hTemplateFile // handle to file with attributes to copy
);

il primo parametro è proprio il puntatore al nome del file, quindi se andiamo ad esaminare l'ULTIMO push prima di createfilea (ricordate la struttura dello stack???) ci troveremo il puntatore al nome del file. Vi ricordo che CreateFile non serve solo a creare un file, ma anche ad aprirlo in caso esista già. Bene. Ora abbiamo i dati anagrafici del file, creiamolo (vuoto). Premiamo il pulsante e... Keyfile Vuoto! Per comodità scriviamoci una stringa che ci servirà per la ricerca in memoria (io scrivo 111333555). Premiamo il pulsante e ci dice "codice sbagliato". Allora adesso dobbiamo sniffare la lettura e vedere cosa succede. Riandiamo in sice, digitiamo bpx ReadFile, usciamo e premiamo il pulsante del Livello 1. Al solito, breakarete nel kernel, premete F12 e vi ritroverete nel processo del settimo in queste righe:

004015DF Call dword ptr [004052A4] arrivate qui (call a readfile)
004015E5 test eax, eax controlla se la lettura è andata a buon fine
004015E7 je 0040163D se si, salta
004015E9 cmp dword ptr [ebp+FFFFFF58], 00000000 controlla se i caratteri letti sono 0
004015F0 je 0040163D se si salta e scrivi file vuoto
004015F2 mov edx, dword ptr [ebp+FFFFFF58] metti il numero dei char letti in edx
004015F8 mov byte ptr [ebp+edx-000000A0], 00 aggiunge 00 (terminatore) alla fine della stringa
letta dal file

00401600 xor ecx, ecx azzera ecx (usato come contatore)
00401602 lea esi, dword ptr [ebp+ecx-000000A0] carica in esi il puntatore alla stringa
letta da file (111333555)

00401609 lea edi, dword ptr [ebp+ecx-00000038] carica il puntatore alla stringa "LaFollia..."
00401610 mov ecx, 0000002B mette in ecx il numero di bytes da comparare (43)
00401615 repz |
00401616 cmpsb | confronta byte per byte
00401617 je 0040162B se le stringhe sono uguali, salta a Codice Giusto!
* Possible StringData Ref from Data Obj ->" Codice sbagliato"
00401619 push 00404140 se arriviamo qui le stringhe non erano uguali

okei, vediamo che in esi viene caricato il puntatore alla stringa "LaFolliaGovernaIlVentoCheScendeDalleStelle", poi c'è un REPZ CMPSB che confronta tutti i 43 byte di questa stringa con i byte della stringa letta dal keyfile. Se le stringhe risultano diverse, allora viene pushata la stringa "Codice Sbagliato". Quindi apriamo il nostro keyfile e ci scriviamo la stringa trovata. Ora se premiamo il pulsante, otterremo la scritta REGISTERED.

LIVELLO 2

Anche qui premiamo il pulsante, e ci dice: Errore nell'apertura del keyfile. L'info ci dice che c'è di mezzo un algoritmo. Procedendo analogamente al livello 1, sia col FileMon, sia col SoftIce possiamo ricavare facilmente che il nome del file è Andrewz.UIC e il suo path è c:\Windows\. Andiamo a creare tale file vuoto. Ora ci dice KeyFile vuoto, quindi ci possiamo concentrare su readfile. Settate il bpx readfile nel softice (prima scriviamo la solita stringa 111333555 nel keyfile), premiamo il pulsante, e ci ritroveremo qui:

004017BE Call dword ptr [004052A4] arriviamo qui
004017C4 test eax, eax controlla se la lettura è andata bene
004017C6 je 00401841 se no, salta
004017C8 cmp dword ptr [ebp+FFFFFF74], 00000000 vedi se il numero di char letti è 0
004017CF je 00401841
004017D1 mov eax, dword ptr [ebp+FFFFFF74] metti in eax il num di char letti
004017D7 mov byte ptr [ebp+eax-00000084], 00 aggiungi 00 alla stringa letta da file
004017DF xor ecx, ecx azzera ecx ed eax
004017E1 xor eax, eax
004017E3 mov al, byte ptr [ebp+ecx-00000084] metti in al l'n-esimo char della stringa letta
dal file

004017EA cmp al, 00 controlla se tale char è zero
004017EC je 004017FC se lo è, abbiamo finito
004017EE xor al, AA xora il char con il valore AAh
004017F0 mov byte ptr [ebp+ecx-00000084], al metti il char xorato al posto del char
corrispondente nella stringa in memoria

004017F7 inc ecx incrementa il contatore
004017F8 xor eax, eax azzera eax
004017FA jmp 004017E1 ripeti
004017FC mov byte ptr [ebp+ecx-00000084], 00 aggiungi il terminatore alla nuova stringa
ottenuta

00401804 xor ecx, ecx azzera ecx
00401806 lea esi, dword ptr [ebp+ecx-00000084] carica in esi il puntatore alla nostra stringa
lavorata

0040180D lea edi, dword ptr [ebp+ecx-0000001C] carica in edi il puntatore ad una stringa già
presente in memoria

00401814 mov ecx, 0000000F F = numero di char da confrontare
00401819 repz |
0040181A cmpsb | esegui il confronto
0040181B je 0040182F se sono uguali salta a codice giusto
* Possible StringData Ref from Data Obj ->" Codice sbagliato"
0040181D push 00404140 altrimenti finiamo qui

qui non è facile come prima. Noi inseriamo la stringa nel file, questa stringa viene letta, viene xorata, e viene confrontata con un valore già presente in memoria, che deve essere quindi la giusta stringa già xorata. L'ipotetica giusta stringa è (in hex):

DF C3 C9 F8 FF E6 EF F0 CC C5 D8 CF DC CF D8

che potete trovare scrivendo "d edi" quando nel softice vi trovate sulla linea 0040180D. Quindi per risalire alla stringa originale, dobbiamo effettuare all'inverso lo xor. Dato che a xor b = c e quindi c xor b = a, facciamoci i nostri calcoletti:

DF C3 C9 F8 FF E6 EF F0 CC C5 D8 CF DC CF D8 //xor

AA AA AA ... =

--------------------------------------------------------------------

75 69 63 52 55 4C 45 5A 66 6F 72 65 76 65 62 //hex

u i c R U L E Z f o r e v e r //ascii

abbiamo quindi ottenuto il giusto valore da inserire nel keyfile, cioè la stringa uicRULEZforever.

LIVELLO 3

Qui l'info ci dice che non dobbiamo fidarci ciecamente del FileMon. Beh, noi usiamolo lo stesso. Ecco quello che succede:

Settimo    Attributes     C:\WINDOWS\DESKTOP\AVELLETRI.FRA    NOTFOUND 

Settimo    Attributes     C:\WINDOWS\SYSTEM\AVELLETRI.FRA    NOTFOUND

Settimo    Attributes     C:\WINDOWS\AVELLETRI.FRA    NOTFOUND

Settimo    Attributes     C:\WINDOWS\AVELLETRI.FRA    NOTFOUND

Settimo    Attributes     C:\WINDOWS\COMMAND\AVELLETRI.FRA     NOTFOUND

Settimo    Attributes     C:\WINDOWS\SYSTEM32\AVELLETRI.FRA    NOTFOUND

a quanto pare vengono cercati 6 keyfile. La prima riga corrisponde al percorso della directory corrente dove tenete il programma eseguibile (nel mio caso sul desktop). Non contenti, andiamo a dare un' occhiata più da vicino con il softice. Se proviamo a breakare con CreateFileE come per i due precedenti livelli non otteniamo niente. Allora proviamo qualche altro break, uno a caso: OpenFile. Bpx OpenFile, premiamo il pulsante e il sice poppa qui:

0040193D lea ecx, dword ptr [ebp+FFFFFDD8]
00401943 push ecx parametri per OpenFile
* Possible StringData Ref from Data Obj ->"Avelletri.fra"
00401944 push 004042D0 viene pushato il nome del file
00401949 Call dword ptr [004052B0] breakate qui
0040194F mov dword ptr [ebp+FFFFFEC4], eax </comment>e arrivate qui</comment>
00401955 cmp dword ptr [ebp+FFFFFEC4], FFFFFFFF vedi se l'apertura è andata bene
0040195C jne 00401973 se si, salta
* Possible StringData Ref from Data Obj ->" Errore nell'apertura"
0040195E push 004042AC altrimenti dai errore
00401963 lea ecx, dword ptr [ebp+FFFFFDCC]

come vedete per OpenFile viene pushato solo il nome del file, senza nessun path. A questo puntosarebbe lecito pensare di metter il keyfile Avelletri.fra nella stessa directory dove si trova il file eseguibile. Provate pure, e il prog vi dirà: Te l'avevo detto di non fidarti del FileMon! Eppure se controlliamo con FileMon il file viene aperto. Dov'è l'inganno? Basta guardare bene la documentazione della funzione OpenFile. Se passate un file senza path, infatti, OpenFile lo cercherà prima nella stessa directory dell'applicazione, poi nella directory corrente di lavoro, quindi nella directory System del winslow, poi nella dir 16 bit system di NT, nella dir di windows ed infine nella directory definita dalla variabile ambiente PATH. Allora riprendiamo il disassemblato di poco fa, e se scorriamo qualche linea più su troviamo:

00401935 Call dword ptr [004052B4] call GetSystemDirectoryA

come prevedibile, questa funzione restituisce il path della directory System del windogs. Quindi il programma cerca il keyfile nelle dir listate poc'anzi, se lo trova controlla che il keyfile si trovi nella directory system del win, quindi prosegue con la lettura. Allora creiamo il file nella dir specificata, ci scrivo la solita stringa, e ripremo il pulsante del livello 3: codice sbagliato. Oki, quindi adesso il file viene trovato, aperto e letto. Ora risettiamo il break su ReadFile per vedere che cosa legge. Arrivate qui:

00401A53 Call dword ptr [004052A4] </comment>questa è Read File</comment>
00401A59 test eax, eax </comment>se la lettura è andata male</comment>
00401A5B je 00401AA8 </comment>salta a errore</comment>
00401A5D mov eax, dword ptr [ebp+FFFFFDD4] </comment>metti in eax il numero di char letti</comment>
00401A63 mov byte ptr [ebp+eax-000001A0], 00 aggiungi il terminatore alla stringa
letta dal keyfile

00401A6B xor ecx, ecx azzera il contatore
00401A6D lea esi, dword ptr [ebp+ecx-000001A0] metti in esi il puntatore alla stringa
letta dal keyfile

00401A74 lea edi, dword ptr [ebp+ecx-00000034] metti in edi il puntatore a una stringa
in memoria ("La mia mente...")

00401A7B mov ecx, 00000024 metti 24h nel contatore
00401A80 repz |
00401A81 cmpsb | effettua la scansione delle due stringhe
00401A82 je 00401A96 salta se sono uguali
* Possible StringData Ref from Data Obj ->" Codice sbagliato"
00401A84 push 00404140 altrimenti beccati l'errore

basta che quando vi trovate in softice alla linea 00401A74 digitate "d edi", e visualizzerete in memoria la stringa "La mia mente controlla la mia realt" (doveva essere "realtà", ma pare che si sia smarrita l'ultima lettera :-). Voi direte: ma io vedo la stringa "...mia realt" e quindi non digito la à. Così però non funziona. Infatti in ecx alla linea 00401A7B viene messo il valore 24h = 36 che è il numero dei caratteri da confrontare. Se togliete la "à" il numero dei caratteri in tutto della stringa è 35, quindi non va. Inoltre, la stringa è a posto: infatti alla letera "à" corrisponde il valore E0, che ritroviamo nel sice; semplicemente la data window del sice non mostra la "à" come corrispondente al valore E0 (non tutti i valori di un byte vengono rappresentati dal sice).

Passiamo avanti.

LIVELLO 4

Bene, abbiamo registrato i primi tre livelli, manca quest'ultimo. Al solito carichiamo il FileMon e premiamo il tasto Livello 4:

Settimo    Open      C:\WINDOWS\DESKTOP\PAPERO.QUE    SUCCESS

Settimo    Read      C:\WINDOWS\DESKTOP\PAPERO.QUE    SUCCESS

Settimo    Close     C:\WINDOWS\DESKTOP\PAPERO.QUE    SUCCESS

oki, abbiamo tutti i dati. Il percorso è relativo alla directory che contiene l'applicazione ovviamente, e non al desktop in sè. Uhmm... Sulla prima riga abbiamo una richiesta in Open e come risultato SUCCESS? Come SUCCESS? Io non ho messo nessun keyfile! Beh, il programma l'ha creato lui! Infatti con Open da filemon potevamo vedere i flag CREATENEW OPENEXISTING READONLY DENYNONE, quindi se il file esiste, lo apre, se non esiste lo crea e lo apre. Tanto di guadagnato per noi! Ora rimane il solito struggente quesito: che ce devo scrive ner file? Come prima, bpx readfile. Appena il softice poppa, ci troveremo nel processo del kernel. Premte F12 e ... ops! Siamo in MSVCRT. Dobbiamo premere F12 altre sette volte per arrivare al processo del settimo! Che noia che barba. Vabbè, cmq arriviamo qui:

* Reference To: MSVCIRT.??5istream@@QAEAAV0@AAH@Z, Ord:0076h
00401BBF Call dword ptr [00405440] noi usciamo da qui
00401BC5 lea ecx, dword ptr [esp+0C] e arriviamo qui
* Reference To: MSVCIRT.?close@fstream@@QAEXXZ, Ord:00FEh
00401BC9 Call dword ptr [00405448] strana call
00401BCF cmp dword ptr [esp+08], 08311809 cmp sospetto
00401BD7 jne 00401BE0 salta se il compare fallisce
* Possible StringData Ref from Data Obj ->" - REGISTERED - "
00401BD9 push 0040412C se siamo qui, è fatta
00401BDE jmp 00401BE5
* Possible StringData Ref from Data Obj ->" Codice sbagliato"
00401BE0 push 00404140 se siamo qui, invece, non ci siamo

invece di open file, readfile etc... abbiamo delle funzioni importate da MSVCIRT. Tali funzioni vengono chiamate perchè quell'idiota dell'autore del crackme ha usato gli oggetti di tipo fstream per le operazione su file. Beh, poco importa questa implementazione, ciò che ci serve ora è capire cosa dobbiamo scrivere nel file. Scriviamoci intanto la nostra solita stringa (111333555), e analizziamo bene quello che succede.

Alla riga 00401BBF viene letto il file.

Alla riga 00401BC9 c'è una dubbia call a close stream

Alla riga 00401BCF c'è un compare di una locazione (esp+08) e un valore fisso (08311809).

se il compare va bene, allora saremo registrati, altrimenti falliamo. Andiamo in softice, breakiamo su read file, e arriviamo fino alla riga 00401BCF del compare. Questa riga dice:

confronta la dword che sta all'indirizzo Esp+08 con il valore 08311809

digitiamo "d esp+08", e troviamo i seguenti byte

B3 D0 A2 06

quindi, considerando la DWORD dobbiamo ricomporli in 06A2D0B3. Ovviamente se avevate la data window settata in visualizzazione di dwords, avreste visto la dword già bella che ricomposta. Bene, questo è il numero che deve essere uguale a 08311809. Tra la lettura dal keyfile e il compare ci sono solo 2 call, che chiamano funzioni in MSVCIRT (che noi non vogliamo assolutamente debuggare) quindi come facciamo a sapere da dove sbuca questo valore? Con un pò di intuito! Essendo le funzioni in MSVCIRT standard, scartiamo subito l'ipotesi che si tratti di una sorta di crypting. Possiamo invece pensare che si tratti di qualche conversione dell'input ascii. Ora se facciamo attenzione andiamo in sice e digitiamo "? 06A2D0B3", in risposta ci verrà dato l'output: 111333555. Esatto. Il valore in esp+08 altri non è che il valore ascii inserito nel file, convertito in hex (le operazioni di fstream prevedono la lettura da file di numeri inseriti in ascii e la loro conversione in interi o altri formati). Quindi se volete potete stepparvi tutta la giungla di MSVCIRT e vedere dove e come il programma converte l'input ascii in un numero intero, ma credo che sia più facile usare la testa! Allora il numero fisso 08311809 hex = 137435145 dec. Inseriamo 137435145 nel keyfile e avremo completato anche l'ultimo livello.

A dire il vero in questo crackme avevo intenzione di aggiungere qualche altro livello, solo che poi mi veniva troppo difficile. Anche questi 4 livelli all'inizio erano venuti molto difficili a causa delle MFC che si accorpavano tutti i cast e la robaccia che avevo scritto. Indi, ho rifatto tutte le routine di lettura e confronto dei codici in asm, ma anche così non mi piace molto :-(

Fatemi sapere cosa volete nel prossimo corso, che non ho molte idee.

Aourevoir

AndreaGeddon


Note finali

Saluto tuuuutta la ML, che adesso è arrivata a più di 350 membri. Saluto Nerds che sta sempre in chat a farmi compagnia :-). Non saluto il Que perchè non vuole farmi entrare in uic2 perchè dice che sono cattivo >:-| Io ti sparooooooo! Per dimostrarti quanto sono buono stavolta non ho scritto nell'apposito spazio riservato a te! Byeeeee Sentiamoci tutti a SMAU!

ps: l'altro giorno in chat ero io Jolter :-PPPP o meglio, un mio clone :-9



Disclaimer

Queste informazioni sono solo a scopo puramente didattico. Ricordate di comprare il crackme se volete continuare ad usarlo dopo 15 giorni :-P Con i soldi ricavati gli sviluppatori del crackme potranno venire a SMAU!! >:-PPPPPPP