Corso UIC Newbies 11
From UIC
Soluzione al 11° corso newbies - Addio WinDasm, è tempo di IDA!
Contents |
| Infos | |
|---|---|
| Author: | AndreaGeddon |
| Email: | andreageddon@hotmail.com |
| Website: | http://andreageddon.8m.com |
| Date: | 04/02/2002 (dd/mm/yyyy) |
| Level: |
|
| Language: | Italian |
| Comments: | Da quanto tempo non venivo a romperti nel tuo spazio riservato!!
hihihihihi :-P Eh...Gioca con il fuoco tu che un giorno ti bruci hihihihi intanto gia siamo partiti col cervello che non devi avere in buono stato visti i colori very-psichedelici che usi per IDA dhe hi hi :P |
Introduzione
Spero che dopo questo crackme Ida diventerà il vostro braccio destro!
Tools
- IDA
- e Softice? no :-) Possiamo fare tutto direttamente da ida!
Link e Riferimenti
Questo è il Corso UIC Newbies n°11 disponibile alla pagina Corsi UIC Newbies
Notizie sul programma
Il crackme ha un solo pulsante, se lo premete vi dice che non è registrato. Non dovete patchare il crackme per ottenere la giusta MessageBox, ma dovete solo capire cosa vuole per partire.
Essay
Niente patching, quindi mano al disassemblatore e vediamo cosa dobbiamo fare. Come già detto WinDasm è inutile, quindi mano a Ida perché se non lo conoscete è ora di imparare a usarlo! Innanzitutto come riferimento potete leggere il megatutorial su Ida scritto da Que, davvero completissimo e semplice. Io sto usando la versione 4.17, ma va bene anche qualsiasi versione precedente. Let's start.
Piccola Introduzione
Quando scegliete di aprire l'undicesimo corso vi verrà chiesto se aprirlo come PE, come normale exe (MZ) o come file binario. Voi ovviamente scegliete PE. Le altre opzioni non sono fondamentali, ma se volete conoscerle leggete il tute di Que. Bene fatelo disassemblare. Come vedete anche se il crackme è piccolo ci mette più tempo del Wdasm, e questo già è indice di tutto il lavoro di analisi che viene fatto! Quando Ida avrà finito il semaforino in alto a destra sarà verde e nella log window vi scriverà:
The initial autoanalysis is finished.
A questo punto dovrebbe avervi messo già sul corpo della WinMain (che in questo prog sta all'indirizzo 00401000). Ma vediamo un po' di esaminare a fondo la cosa. Andiamo all'entry point. Menu Jump->Jump to entry point. Vi verrà mostrata una finestra che contiene un solo entry point, se disassemblate una dll gli entry point saranno molti, uno per ogni export :-). Bene ora che siete all'entry point (riga 00401630) potete vedere il codice della funzione WinMain (non il corpo, ma la funzione stessa). Facciamo un po' di teoria su come funziona una applicazione scritta in C. Ogni applicazione ha la sua procedura WinMain, definita come:
HINSTANCE hInstance, // handle to current instance
HINSTANCE hPrevInstance, // handle to previous instance
LPSTR lpCmdLine, // pointer to command line
int nCmdShow // show state of window
);
il programma che viene scritto dal programmatore inizia DENTRO la win main, cioè nel punto in cui Ida ci aveva portato all'inizio (00401000), ma l'entry point vero e proprio parte dal codice della WinMain, cioè quel codice che si occupa di chiamare la linea 00401000 e di fornire i quattro parametri (oltre a fare vari lavori di inizializzazione dell'heap etc etc). Potete vedere come quasi tutte le call hanno un nome, tipo
.text:004016DC call __setenvp
.text:004016E1 call __cinit
in quanto ida le riconosce e le identifica come costrutti standard. Già questo aiuta molto. Scorrete un po' il codice in basso fino alla riga
ed ecco la call che vi porterà al codice vero e proprio che il programmatore ha scritto. Tutto questo finora non c'entrava nulla col crackme, perché ci perdiamo tempo? Beh perché la maggior parte dei programmi viene scritta in C, quindi quando li steppate dall'inizio è inutile perdervi nelle inizializzazioni della WinMain, andate direttamente alla chiamata alla WinMain che sta sempre dopo GetModuleHandle. Infatti:
.text:0040175E push esi ; lpszCmdLine
.text:0040175F push 0 ; hPreInst
.text:00401761 push 0 ; lpModuleName
.text:00401763 call ds:GetModuleHandleA
.text:00401769 push eax ; hInst
.text:0040176A call _WinMain@16
come vedete Ida vi risolve anche i parametri della WinMain! E questo per tutte le api supportate, quindi la vita diventa moolto più semplice. Okei finita questa parentesi introduttiva passiamo al crackme vero e proprio.
Iniziamo il lavoro
Alla riga 0040176A cliccate due volte sulla _WinMain@16 e verrete mandati direttamente alla riga della funzione stessa. Un po' come premere "destra" in windasm. Okei ora la prima cosa da fare è rintracciare la MessageBox che ci dà l'errore e capire che cosa viene controllato per poterci registrare. Come al solito cerchiamo la stringa di errore. Ora quasi tutti lamentano problemi con Ida perché non sanno trovare le string reference. A parte alcuni plugin che io non trovo granchè utili, le string ref in Ida ci sono :-). Andate nel menu View e da lì a seconda della versione o avete direttamente nel menu la voce "Names" o la trovate nel sottomenu "Open SubViews". Vi si aprirà una finestra contenente tutte le stringhe del programma, siano esse stringhe, funzioni importate, esportate o interne! Nella lista avete alla sinistra di ogni stringa l'icona di una lettera che ne identifica il tipo, ad esempio "A" sta per le stringhe, "I" sta per le funzioni importate, "L" per le funzioni locali etc etc. Quindi dobbiamo controllare le stringhe di tipo A. Avrete già notato che abbiamo a disposizione un "search", quindi cerchiamo "Registration" e nella finestra names avrete selezionata la stringa. Notate che ida alle stringhe aggiunge una "a" davanti, se volete queste opzioni posso essere modificate. Due righe sopra sRegistration abbiamo la stringa aNonRegistrato, Ida toglie anche gli spazi quindi quando cercate le stringhe ricordatevi di questi particolari! Cmq doppio click sulla stringa aNonRegistrato e verrete portati alla riga 0040502C. Questa riga è nella sezione dati, infatti vedete lì intorno tutte le altre stringhe etc etc. Ora come facciamo a trovare il codice che si riferiva alla stringa di non registrazione? Con le xref!
vedete quel DATA XREF? ci dice da quali righe viene chiamata la stringa in questione. In questo caso il programma è piccolo e le xref sono poche, di solito le xref di un dato sono molte per cui abiutuatevi a cercare direttamente nell'apposita xref window. La trovate nello stesso menu in cui avete trovato la voce "Names", si chiama "Cross references". Si aprirà la finestra e avrete la lista di tutte le xref per la linea che avete selezionato. In questo caso c'è una sola reference, doppio cliccateci sopra e ida vi porterò alla riga 004012E0. Vediamo un po' il codice lì intorno:
.text:004012AD jnz short loc_0_4012D5 ; salta a non registrato
.text:004012AF mov eax, [esp+24h]
.text:004012B3 push 40h
.text:004012B5 push offset aRegistration ; "Registration"
.text:004012BA push offset aComplimentiCeL ; "Complimenti ce l'hai fatta!"
.text:004012BF push eax
.text:004012C0 call ds:MessageBoxA
il JNZ decide se mandarci alla registrazione oppure no, quindi il compare precedente (riga 004012A5) deve essere quello che controlla la registrazione. Notate che quando siete su una riga potete premere ";" o ":" per inserire i vostri commenti nel codice! Così se volete appuntarvi delle note potete farlo! Ma torniamo a noi: per registrarsi il programma ha bisogno che la word all'indirizzo 00407580 sia uguale a 1Fh. Quando avviene ciò? Di nuovo ricorreremo alle xref per vedere in quali punti il codice agisce su quella variabile, ma prima facilitiamoci la vita: Ida permette di rinominare praticamente tutto, quindi rinominiamo la variabile in "Chiave", così ci sarà più facile la lettura del codice. Per rinominare la variabile potete o cliccare col tasto destro sulla word_0_407580 e scegliere rename, oppure doppioclick sulla word e ida vi porterà alla riga 00407580, a quel punto rinominate l'address. Il renaming dipende dalle versioni. Cmq dopo che avrete rinominato la variabile la precedente riga vi apparirà così:
suona meglio vero? Ora come prima andiamo alla riga 00407580 (quella della var Chiave) e da lì scegliamo xref.
| Direction | Type | Address | Instruction |
|---|---|---|---|
| Up | w | text.00401069 | or byte ptr Chiave, 10h |
| Up | w | text.00401108 | or byte ptr Chiave, 02 |
| Up | w | text.00401126 | or byte ptr Chiave, 01 |
| Up | w | text.00401225 | or byte ptr Chiave, 04 |
| Up | r | text.004012A5 | cmp Chiave, 1Fh |
| Up | w | text.00401462 | or byte ptr Chiave, 08 |
et voilà! Praticamente già da qui si capisce molto. Il tipo di xref indica r = read oppure w = write, in questo caso in r abbiamo solo la xref corrispondente al cmp, tutti gli altri sono w. Ora notate che 1 + 2 + 4 + 8 + 10h = 1Fh, quindi il cmp controlla se tutti i cinque flag sono attivi nella word:
bin 00000001 = hex 01 bin 00000010 = hex 02 bin 00000100 = hex 04 bin 00001000 = hex 08 bin 00010000 = hex 10
tutte le istruzioni OR di ogni xref corrispondono appunto all'attivazione del flag stesso, e ovviamente ognuno di questi flag dovrà corrispondere a un requisito occorrente alla registrazione. Quindi analizziamo tutte le xref. Partiamo dalla prima.
Il primo trick
.text:0040104C push 4000000h ; dwFlagsAndAttributes
.text:00401051 push 0 ; dwCreationDisposition
.text:00401053 push 0 ; lpSecurityAttributes
.text:00401055 push 0 ; dwShareMode
.text:00401057 push 0 ; dwDesiredAccess
.text:00401059 push offset a_Siwvid ; lpFileName
.text:0040105E call ds:CreateFileA
.text:00401064 cmp eax, 0FFFFFFFFh
.text:00401067 jnz short loc_0_401070 ; questo decide l'esito
.text:00401069 or byte ptr Chiave, 10h ; dobbiamo arrivare qui
.text:00401070 loc_0_401070:
il CreateFileA sembra decidere l'attivazione del flag. Analizziamo quindi i parametri della chiamata: come vedete ida in automatico mette il commento ad ogni parametro così da non dover sempre correre sulla reference :-). I parametri = 0 non ci interessano, prendiamo quelli usati:
lpFileName = .data:00405020 a_Siwvid db '\\.\siwvid',0 (mmm driver del softice!)
avete capito ora? Questo è un banalissimo MeltIce! FFFFFFFFh è il valore numerico di INVALID_HANDLE_VALUE, quindi viene tentata l'apertura del driver del softice, se l'apertura avviene allora il driver è presente (e quindi anche il softice) e quindi CreateFile restituirà un handle valido in EAX che sarà diverso da FFFFFFFFh, per cui il flag non viene attivato. Se invece il CreateFile fallisce allora non è riuscita l'apertura del driver, quindi il softice non c'è e il flag viene attivato. Quindi per risolvere questo primo trick basta o non avere softice caricato, o se avete softice basta usare FrogSice o IceDump per nasconderlo.
Il secondo trick
Vediamo ora il secondo trick:
.text:004010EC push ecx ; phkResult
.text:004010ED push 0F003Fh ; samDesired
.text:004010F2 push 0 ; ulOptions
.text:004010F4 push offset aUic ; lpSubKey
.text:004010F9 push 80000001h ; hKey
.text:004010FE call ds:RegOpenKeyExA
.text:00401104 test eax, eax
.text:00401106 jnz short loc_0_40110F
.text:00401108 or byte ptr Chiave, 2
.text:0040110F loc_0_40110F:
.text:0040110F xor eax, eax
stavolta ida non mette i commenti in automatico, li ho aggiunti io per semplicità. Innanzitutto stavolta vediamo che l'attivazione del flag è decisa dal buon esito dell'apertura di una chiave del registro. Analizziamo i parametri di RegOpenKeyEx:
lpSubKey = offset aUic = stringa "UIC" (nome della sottochiave)
samDesired = F003Fh = KEY_ALL_ACCESS
phkResult = buffer che conterrà l'handle della chiave aperta.
quindi viene aperta la chiave
HKEY_CURRENT_USER -> UIC
se la chiave è trovata il flag è attivato, altrimenti nisba. Per cui per risolvere questo secondo trick vi basta creare la suddetta sottochiave (non valore, CHIAVE!) e avrete risolto.
Il terzo trick
.text:00401124 jnz short loc_0_40112D
.text:00401126 or byte ptr Chiave, 1
.text:0040112D loc_0_40112D:
.text:0040112D call sub_0_401440
stavolta le cose sono un po' più complicate: come vedete c'è solo un CMP, niente funzioni o altro. Come fare allora? Semplice, rimettiamo mano alle xref! Infatti il test decisivo è il CMP tra word_0_407568 e 7CDh. Quindi prendiamo le xref della word in questione: troverete una xref che vi porta al compare appena visto e una che vi porta a 00401006:
.text:00401003 push ebx
.text:00401004 push esi
.text:00401005 push edi
.text:00401006 push offset word_0_407568 ; lpSystemTime
.text:0040100B call ds:GetSystemTime
okey quindi l'offset 00407568 è l'offset di una struttura di tipo SYSTEMTIME che viene riempita dall'apposita api GetSystemTime. Ma viene controllata tutta la struttura? No.
WORD wYear;
WORD wMonth;
WORD wDayOfWeek;
WORD wDay;
WORD wHour;
WORD wMinute;
WORD wSecond;
WORD wMilliseconds;
} SYSTEMTIME;
il CMP che a noi interessa è sulla WORD puntata da 00407568, cioè della prima word della struttura che appunto è rappresentata dal campo SYSTEMTIME.wYear. Quindi il CMP interessato adesso lo vediamo così:
CMP SYSTEMTIME.wYear, 1997dec (7CDhex)
a questo punto è chiaro che viene controllato se l'anno in corso è il 1997. Se siamo nel 1997 il flag viene attivato, quindi per superare questo trick mettete indietro la data del clock di win :-)
Il quarto trick:
.text:00401217 call ds:IsZoomed
.text:0040121D test eax, eax
.text:0040121F jz loc_0_40110F
.text:00401225 or byte ptr Chiave, 4
IsZoomed controlla se la finestra è massimizzata, in tal caso restituisce nonzero. Se invece restituisce zero il JZ ci fa evitare l'attivazione del flag, per cui è necessario massimizzare la finestra per evitare questo trick.
Il quinto ed ultimo trick
.text:00401442 push 80h ; dwFlagsAndAttributes
.text:00401447 push 3 ; dwCreationDisposition
.text:00401449 push 0 ; lpSecurityAttributes
.text:0040144B push 0 ; dwShareMode
.text:0040144D push 80000000h ; dwDesiredAccess
.text:00401452 push offset aCGeddon_uic ; lpFileName
.text:00401457 call ds:CreateFileA
.text:0040145D cmp eax, 0FFFFFFFFh
.text:00401460 jz short locret_0_401469
.text:00401462 or byte ptr Chiave, 8
.text:00401469 locret_0_401469:
.text:00401469 retn
ecco un altro CreateFile. Stavolta niente controlli su driver, analizziamo i parametri:
dwCreationDisposition = OPEN_EXISTING
dwDesiredAccess = GENERIC_READ
plFileName = "c:\geddon.uic"
come prima viene tentata l'apertura del file c:\geddon.uic. Se il file è trovato il flag sarà attivato, altrimenti nada. Create quindi tale file e siete a posto.
A questo punto se avete sistemato tutti e cinque i trick dovreste ottenere il messaggio di complimenti :-). Riepiloghiamo: per farvi registrare dovete:
- 1- non far rilevare il softice al programma
- 2- fargli trovare la chiave HKEY_CURRENT_USER\UIC\ nel registry
- 3- fargli credere di essere nel 1997
- 4- ingrandire il crackme a schermo intero
- 5- fargli trovare il file C:\geddon.uic
l'ordine dei cinque passi non è importante. Riavviate il crackme ed è fatta :-)
Ultime note
Spero che a questo punto vi siate convinti che WinDasm è solo un giocattolo rispetto a Ida. Ricordate che in IDA potete rinominare praticamente TUTTO: funzioni, variabili locali e globali, locazioni e molto altro. Per qualsiasi dubbio potete consultare il tutorial di Que. Nota che una volta disassemblato potete produrre vari output file: semplice testo, map e altri. In particolare il map è utile perché poi potete usare l'utility MSYM di softice per trasformare il map in symbols, e i symbols poi li potete usare nel softice per avere tutti i nomi a disposizione in fase di debugging :-) volete fare la prova?
disassemblate il programma
File->Produce->Create MAP file MSYM file.mape ottenete file.sym
Aprite il SiceLodaer32!
File->OpenModulee aprite il file.sym, non l'exe!
Module->Load File->OpenModule programma.exe
ora lanciate il programma. In Softice non dovreste vedere niente perché dovete rilocare i simboli. Se usate il comando SYM dovrebbe farvi vedere una lista di simboli, se non la vedete allora avete sbagliato qualcosa. Se la vedete usate:
SYMLOC n seg base_addr
per ogni sezione, dove n è il numero della sezione (numero 1-based), seg è il segmento della sezione e base_addr è l'indirizzo di base della sezione. Ad esempio
SYMLOC 1 0167 00401000 SYMLOC 2 016F 00404000
per la sezione di codice, e così via. Al posto di 167 dovete mettere il segmento del vostro CS
SYMLOC 2 016F 00404000
per la sezione data etc etc. Usate map32 per avere lista e baseaddress delle sezioni e usate il symloc su tutte (anche se in realtà bastano code + data).
Poi andate all'entry point
u 00401630
e vedrete un esempio di cosa vuol dire avere tutti i nomi risolti :-) In questo modo non solo vi sarà più facile debuggare e disassemblare, ma inizierete anche a pensare da veri reverser.
Byeeee
AndreaGeddon
Note finali
Un saluto a tutta la ML e i frequentatori di #crack-it, in particolare un saluto a Yado che mi sta mettendo sotto torchio :-P e ad Acid-Leo che è una vita che non lo vedevo! Anche un saluto a Giocrack (che mi ha fornito l'ultima ida!), a Killo che è sempre disponibile a spiegarci gli internals di win2k e un salutone a Lee :-* Ciao gente! (volevo salutare pure Phobolo ma è troppo grosso poi il tutorial viene di dieci mega :-P)
Disclaimer
Se vi è piaciuto questo corso o la UIC in generale mandate a me o a Quequero il vostro numero di cc e ci farete felici e contenti >:-) e ricordate che i crackme vanno comprati e non rubati!! :-P
-> Noi reversiamo al solo scopo informativo e di miglioramento del linguaggio Assembly
- |
- v
Questa è proprio una ca.. una vera cazza.. una enorme, gigantesca, stepitosa c@%%@ta!!
Capitoooooooo????? Bhè credo di si ;))))