Lezione2 zmaster solution
From UIC
Soluzione lezione 2
Contents |
| Infos | |
|---|---|
| Author: | Zmaster |
| Email: | zmaster.adsl@gmail.com |
| Website: | |
| Date: | 17/09/2008 (dd/mm/yyyy) |
| Level: |
|
| Language: | Italian |
| Comments: | |
Introduzione
Eccomi a fare il mio primo tute… da qualche parte bisogna pure iniziare, perciò ho pensato di farlo dall’inizio :)
Non ho considerazioni particolari da fare, questo primo reversing è molto semplice, immagino che sia stato compilato apposta per facilitare al massimo le cose.
Rileggendo il tute mi è venuto il dubbio di essere stato un po’ prolisso, il motivo è che ho cercato di non dare certe cose per scontate, secondo me chi si avvicina al mondo del reversing spesso ha bisogno di fare “il primo passo” e magari alcune cose è meglio ripeterle piuttosto che ometterle.
Tools
OllyDbg 1.10, beh questo mi sembra ovvio
Stealth64(plug-in di Olly), a me è necessario per poter usare OllyDbg 1.10 su Vista64.
Essay
Dunque, la consegna era di analizzare un po’ più nel dettaglio il funzionamento del programma allegato alla Lezione2, a tale scopo ci viene chiesto di brekkare nel punto di creazione della finestra principale del programma e dare poi un occhio alla DialogProc.
Iniziamo: avviamo Olly e carichiamo lezione2.exe. Olly in pochi istanti carica e analizza il tutto e ci posiziona all’Entry Point del programma, ovvero il punto da cui parte l’esecuzione.
Come richiesto settiamo un break alla chiamata di DialogBoxParam, per fare questo digitiamo nella command line:
bp DialogBoxParamA
e premiamo F9 per avviare l’esecuzione.
Olly brekka subito all’indirizzo 4010C5:
004010B4 |. 6A 00 PUSH 0 ; /lParam = NULL
004010B6 |. 68 00104000 PUSH lezione2.00401000 ; |DlgProc = lezione2.00401000
004010BB |. 6A 00 PUSH 0 ; |hOwner = NULL
004010BD |. 6A 6A PUSH 6A ; |pTemplate = 6A
004010BF |. 50 PUSH EAX ; |hInst
004010C0 |. A3 B0B64000 MOV DWORD PTR DS:[40B6B0],EAX ; |
004010C5 |. FF15 E8804000 CALL DWORD PTR DS:[<&USER32.DialogBoxPar>; \DialogBoxParamA
004010CB |. 33C0 XOR EAX,EAX
004010CD \. C2 1000 RETN 10
Notiamo che all’indirizzo 4010B0 è presente un $ che ci indica che si tratta di un indirizzo utilizzato da un CALL , e che dopo più avanti c’è un RET: si tratta rispettivamente di inizio e fine di una funzione. Chi ha preparato il programma per evitarci confusione ha avuto l’accortezza di fare la chiamata all’interno di una funzione e focalizzare la nostra attenzione su quelle poche righe.
Apriamo il menù contestuale con un click destro sulla riga con la CALL e scegliamo “Help on symbolic name” per avere informazioni su cosa fa questa funzione (bisogna aver settato il percorso di win32.hlp) . L’help spiega che la funzione fa apparire una Dialog prendendola dalle risorse del modulo.
Diamo una rapida occhiata agli argomenti che vengono passati attraverso delle PUSH nello stack, Olly ci aiuta scrivendoci nomi e valori nella 4° colonna:
hInst, è l’handle del modulo che serve alla funzione per recuperare la Dialog dalle risorse.
pTemplate = 0x6A, si tratta dell’ID della finestra nelle risorse. Se apriamo l’EXE con ResHack o un altro editor di risorse troveremo solo una finestra con ID 106 (106 == 0x6A).
hOwner = NULL, è l’handle della finestra genitore. Essendo NULL non ha genitore. .
DlgProc, questa è una cosa interessante: è la funzione che si occupa di gestire i messaggi della finestra. E’ qui che tra molto poco andremo a vedere cosa viene fatto..
lParam, è un parametro che viene passato all’init della Dialog, in questo caso non è importante..
Questo lo abbiamo capito nel giro di pochissimo tempo, come sarebbe stato senza gli aiuti di Olly? Ci sarebbe voluto un po’ più tempo… :S Hehe, spesso non ci si rende conto di quanto utile è una cosa finche non ci si trova senza. Mi è venuto in mente perché un paio d’anni fa devo aver provato ad usare Sice e non mi ricordo che fosse tutto così rose e fiori! :)
Ok ok, andiamo avanti. Andiamo all’indirizzo della DlgProc cioè 401000, per fare questo premiamo CTRL+G e inseriamo tale valore. Ecco la nostra funzione:
00401003 . A1 10A04000 MOV EAX,DWORD PTR DS:[40A010]
00401008 . 33C4 XOR EAX,ESP
0040100A . 894424 28 MOV DWORD PTR SS:[ESP+28],EAX
0040100E . 8B4424 34 MOV EAX,DWORD PTR SS:[ESP+34]
00401012 . 83E8 10 SUB EAX,10 ; Switch (cases 10..111)
00401015 . 8B4C24 30 MOV ECX,DWORD PTR SS:[ESP+30]
00401019 . 74 79 JE SHORT lezione2.00401094
0040101B . 2D 00010000 SUB EAX,100
00401020 . 74 4B JE SHORT lezione2.0040106D
00401022 . 83E8 01 SUB EAX,1
00401025 . 75 76 JNZ SHORT lezione2.0040109D
00401027 . B8 E8030000 MOV EAX,3E8 ; Case 111 (WM_COMMAND) of switch 00401012
0040102C . 66:394424 38 CMP WORD PTR SS:[ESP+38],AX
00401031 . 75 6A JNZ SHORT lezione2.0040109D
00401033 . 6A 14 PUSH 14 ; /Count = 14 (20.)
00401035 . 8D5424 04 LEA EDX,DWORD PTR SS:[ESP+4] ; |
00401039 . 52 PUSH EDX ; |Buffer
0040103A . 68 E9030000 PUSH 3E9 ; |ControlID = 3E9 (1001.)
0040103F . 51 PUSH ECX ; |hWnd
00401040 . FF15 EC804000 CALL DWORD PTR DS:[<&USER32.GetDlgItemTe>; \GetDlgItemTextW
00401046 . 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL
00401048 . 68 3C814000 PUSH lezione2.0040813C ; |Title = "Caption"
0040104D . 8D4424 08 LEA EAX,DWORD PTR SS:[ESP+8] ; |
00401051 . 50 PUSH EAX ; |Text
00401052 . 6A 00 PUSH 0 ; |hOwner = NULL
00401054 . FF15 F0804000 CALL DWORD PTR DS:[<&USER32.MessageBoxW>>; \MessageBoxW
0040105A . 33C0 XOR EAX,EAX
0040105C . 8B4C24 28 MOV ECX,DWORD PTR SS:[ESP+28]
00401060 . 33CC XOR ECX,ESP
00401062 . E8 69000000 CALL lezione2.004010D0
00401067 . 83C4 2C ADD ESP,2C
0040106A . C2 1000 RETN 10
0040106D > 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL; Case 110 (WM_INITDIALOG) of switch 00401012
0040106F . 68 30814000 PUSH lezione2.00408130 ; |Title = "InitDialog"
00401074 . 68 00A04000 PUSH lezione2.0040A000 ; |Text = "breakpointMe"
00401079 . 6A 00 PUSH 0 ; |hOwner = NULL
0040107B . FF15 F4804000 CALL DWORD PTR DS:[<&USER32.MessageBoxA>>; \MessageBoxA
00401081 . 33C0 XOR EAX,EAX
00401083 . 8B4C24 28 MOV ECX,DWORD PTR SS:[ESP+28]
00401087 . 33CC XOR ECX,ESP
00401089 . E8 42000000 CALL lezione2.004010D0
0040108E . 83C4 2C ADD ESP,2C
00401091 . C2 1000 RETN 10
00401094 > 6A 00 PUSH 0 ; /Result = 0; Case 10 (WM_CLOSE) of switch 00401012
00401096 . 51 PUSH ECX ; |hWnd
00401097 . FF15 F8804000 CALL DWORD PTR DS:[<&USER32.EndDialog>] ; \EndDialog
0040109D > 8B4C24 28 MOV ECX,DWORD PTR SS:[ESP+28] ; Default case of switch 00401012
004010A1 . 33CC XOR ECX,ESP
004010A3 . 33C0 XOR EAX,EAX
004010A5 . E8 26000000 CALL lezione2.004010D0
004010AA . 83C4 2C ADD ESP,2C
004010AD . C2 1000 RETN 10
La funzione è decisamente più corposa della precedente ma tutto sommato non è poi tanto complessa. Se consultiamo win32.hlp alla voce DialogProc ci viene mostrato il prototipo di questa funzione. Non penso che sia il caso di mettersi a descrivere per filo e per segno il funzionamento di windows e dei messaggi alle finestre, questo tutorial riguarda l’uso di Olly. Diciamo solo la cosa più importante: la funzione viene chiamata per processare dei messaggi che vengono inviati in varie circostanze, nel codice c’è uno switch…case che a seconda del tipo esegue operazioni diverse, le istruzioni che saltano ai vari case sono situate tra 401012 e 401025.
In questa DialogProc vengono gestiti 3 messaggi, ognuno dei quali in un case riconosciuto ed evidenziato da Olly che associa al suo codice il nome.
A 401027 viene processato WM_COMMAND, questo accade quando si clicca sul pulsante o si eseguono altre azioni sui controlli.
A 40106F viene processato WM_INITDIALOG, questo accade subito dopo la creazione della Dialog e dei suoi controlli, come dice il nome viene utilizzata per inizializzare il valore di questi ultimi.
A 401094 viene processato WM_CLOSE, questo accade premendo la X in cima alla finestra per terminare il programma o premendo ALT+F4.
Mettiamo un break ad ognuno di questi indirizzi con F2 ed eseguiamo il programma con F9 per vedere cosa succede quando vengono gestiti.
Il primo a brekkare è quello relativo a WM_INITDIALOG, come ci si poteva aspettare. Steppando pochi passi vediamo che vengono passati i parametri e chiamata la funzione MessageBoxA, e voilà, la prima messagebox appare! Mmm tutto ciò appare un po’ strano: stiamo creando la finestra principale del programma e invece ci ritroviamo il messaggio. Non doveva apparire PRIMA della finestra principale? E in effetti la finestra principale non è ancora visualizzata: cosa sta succedendo, non è già stata creata?
In effetti si poteva immaginare che la MessageBox fosse visualizzata chiamando l’opportuna API PRIMA della DialogBoxParam, qui invece viene fatto “durante” la chiamata alla DialogBoxParam. Se infatti scorriamo verso il basso lo Stack ci accorgeremo che sono state eseguite una miriade di CALL, ma siamo ancora all’interno di questa funzione, quindi l’esecuzione è stata bloccata dalla MessageBox e non proseguirà finchè l’utente non la chiuderà.
Consultando l’help della DialogBoxParam scopriamo che questa funzione:
- crea la finestra
- invia ad essa il messaggio WM_INITDIALOG
- visualizza la finestra creata ed inizializzata
Quindi ecco quello che succede: la finestra principale viene creata ma non è ancora visibile, viene mandato il messaggio WM_INITDIALOG e quindi viene visualizzata la MessageBox, quando l’utente la chiude l’esecuzione della DialogBoxParam prosegue e la finestra viene finalmente visualizzata.
Il programma è ora in esecuzione, se proviamo a fare qualcosa OllyDbg poppa subito sulla WM_COMMAND. Questo perché questo messaggio viene inviato per varie notifiche, se notiamo le prime tre righe fanno una comparazione ed eventualmente escono subito dal case. Questa comparazione non fa altro che controllare l’ID del controllo oggetto della notifica: significa “se il componente non ci interessa (cioè l’ID non è quello del pulsante) allora ignoralo”. Dato che vogliamo vedere cosa viene fatto premendo il pulsante posizioniamoci subito dopo queste tre istruzioni e riproviamo a scrivere qualcosa nella casella di testo. Questa volta tutto liscio, premiamo Ok ed ecco che Olly poppa! :)
Anche in questo caso è facile vedere quello che viene fatto: viene letto ciò che è stato scritto nella casella di testo con la GetDlgItemTextW e viene generata una MessageBox con lo stesso testo con la MessageBoxW... non occorrerebbe neanche steppare.
Ok, rimandiamo in esecuzione il programma premendo F9. Ora proviamo a chiuderlo premendo il tasto X in alto a destra della finestra: Olly poppa nel case della WM_CLOSE.
Anche qui nulla di particolare, viene chiamata la EndDialog che serve a far chiudere la finestra e a ritornare dalla DialogBoxParam.
In tutti i 3 case (e anche in quello di default, nel quale prosegue l’esecuzione dal case del messaggio WM_CLOSE) c’è una CALL a 4010D0 che è piuttosto misteriosa, a occhio mi verrebbe da dire che riguarda le eccezioni non gestite ma non ne ho ancora ben chiaro il funzionamento, debuggando ho potuto constatare che la funzione ritorna subito perché non viene mai effettuato il salto condizionale a 4010D6:
004010D6 . 75 02 JNZ SHORT lezione2.004010DA
004010D8 . F3: PREFIX REP: ; Superfluous prefix
004010D9 . C3 RETN
Ne consegue che la funzione non fa mai nulla ed è come non ci fosse.
Note Finali
Ecco conclusa la mia analisi della DialogProc, si tratta di un caso molto semplice ma che mi ha permesso di provare varie funzionalità di Olly. Non ho ulteriormente divagato per non appesantire il testo dato che le prove che ho fatto non avevano una finalità ben precisa. Tanto per citarne un paio trovo che siano molto utili le funzioni “Follow in Dump/Stack” e “Find references to”.
Colgo l’occasione per ringraziare tutti i docenti e studenti della UIC per dare a tutti quelli come me un “trampolino” da cui partire.
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 malevoli 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.