Soluzione al 7° corso newbies
KeyFile sniffing


09/10/2000

by "AndreaGeddon"

 

 

UIC's Home Page

Published by Quequero

I must not fear. Fear is the mind killer

Qualche mio eventuale commento sul tutorial :)))

Fear is the little  death that brings to total obliteration
UIC's form
Home page:      www.andreageddon.8m.com
E-mail:    andreageddon@hotmail.com
IRC chan:   #crack-it  /  #hackmaniaci / #warezitalia  su irc.azzurra.it
UIC's form

Difficoltà

(X)NewBies ( )Intermedio ( )Avanzato ( )Master

 

Eccovi la soluzione ai 4 livelli di questo semplice crackme.


Soluzione al 7° corso newbies
KeyFile sniffing
Written by AndreaGeddon

Introduzione

Vediamo che bisogna fare in caso di protezione a KeyFile.

Tools usati

-  SoftIce

-  FileMon  (monitor degli accessi ai files  -  Opzionale)

-  WDasm32

URL o FTP del programma

quequero.cjb.net 

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à presenti in memoria

:00401814    mov ecx, 0000000F  F = numero di char da confrontare

:00401819    repz  |

:0040181A    cmpsb | esegui il confronta

: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  e arrivate qui

: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]  questa è Read File

:00401A59    test eax, eax       se la lettura è andata male

:00401A5B    je 00401AA8        salta a errore

:00401A5D    mov eax, dword ptr [ebp+FFFFFDD4]  metti in eax il numero di char letti

: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 stinga 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

                                                                          UIC's page of reverse engineering, scegli dove andare:

Home   Anonimato   Assembly    CrackMe   ContactMe   Forum   Iscrizione      
       Lezioni    Links   Linux   NewBies   News   Playstation        
  Tools   Tutorial   Search  
UIC Faq

                                UIC