Iczelions Tutorial 28 |
||
Data |
by "-Death_Reaver- |
|
5/10/2005 |
Published by Quequero |
|
Xor quequero, quequero |
mov
eax, capodelmondo |
mov capodelmondo, Death_Reaver |
.... |
.... |
|
Difficolt |
( )NewBies ()Intermedio (X)Avanzato ( )Master |
Introduzione |
Dopo qualche mesetto di pausa si riparte con la traduzione dei tutorial: Ora siamo arrivati al n 28: le utilissime debug api. Il tutorial si compone di 3 parti: Questa la prima. Per errori/segnalazioni/commenti mandatemi una mail a [email protected].
Tools usati |
URL o FTP del programma |
Essay |
In questo tutorial impareremo cosa Win32 offre per i programmatori riguardo gli strumenti di debugging. Dopo aver letto questo tutorial avrai imparato come debuggare un processo.
TEORIA:
Win32 possiede molte API che permettono ai programmatori alcune potenziabilit di un debugger. Sono chiamate Win32 Debug-API o "primitives". Con esse puoi:
Caricare un programma o attaccarsi a un programma in esecuzione
Ottenere informazioni di basso livello sul programma che stai debuggando, come per esempio le process ID, indirizzo dell'entry-point, imagebase etc.
Essere avvisato dagli eventi relativi al debugger come succede quando un processo/thread inizia/esce, le DLL sono caricate/scaricate etc.
Modificare il processo/thread che stato debuggato
In poche parole, puoi scrivere un semplice debugger con queste API. Siccome l'argomento vasto, stato diviso in parti pi maneggevoli: Il tutorial inizia con la prima parte. Qui si spiegheranno i concetti di base e il framework generale per usare le Debug-API.
Le step da seguire per usare le debug-API sono:
1. Creare un processo o "attaccare" il tuo programma a un processo in esecuzione. Questo il primo passo per usare le Debug-API. Finch il tuo programma lavora da debugger ti serve un programma da debuggare. Il programma che sta per essere debuggato chiamato "Debuggee". Puoi acquisire il Debuggee in due modi:
Puoi creare il processo del debuggee da te con CreateProcess. per poter creare un processo da debuggare devi specificare il flag DEBUG_PROCESS. Questo flag fa in modo che Windows sappia che vogliamo debuggare il
processo. Windows manderà delle notificazioni al tuo programma riguardo importanti eventi di debugging(debug-events) che si presenteranno nel debuggee. Il processo debuggee verrà immediatamente sospeso finchè il tuo programma non è pronto. Se il debuggee crea a sua volta processi-figli, Windows notificherà dei debug-events provenienti anche da tutti i processi figli. Di solito non si desidera tale comportamento. Lo puoi disabilitare specificando il flag DEBUG_ONLY_THIS_PROCESS combinato con DEBUG_PROCESS (in masm, si scrive 'DEBUG_ONLY_THIS_PROCESS or DEBUG_PROCESS' NTD).
· Puoi "attaccare" il tuo programma a un processo già presente con DebugActiveProcess.
2. Aspettare i debug-events. Dopo che il tuo programma ha acquisito il debuggee, il thread primario del debuggee è sospeso e lo rimarrà finchè il tuo programma chiama WaitForDebugEvent. Questa funzione lavora come gli altri WaitFor..... cioè blocca il thread chiamato finchè i eventi attesi si verificheranno. In questo caso, aspetta i debug-events mandati da Windows. Vediamo il suo prototipo:
WaitForDebugEvent proto lpDebugEvent:DWORD, dwMilliseconds:DWORD
DEBUG_EVENT STRUCT
dwDebugEventCode dd ?
dwProcessId dd ?
dwThreadId dd ?
u DEBUGSTRUCT <>
DEBUG_EVENT ENDS
dwDebugEventCode contiene il valore che specifica il tipo di debug-event arrivato. Brevemente, qui ci possono essere tanti tipi di eventi, e perciò il tuo programma deve controllare il valore di questo campo in modo che sappia quale tipo di evento è arrivato e agire di conseguenza. I valori possibili sono:
Valore |
Significato |
CREATE_PROCESS_DEBUG_EVENT | Quando un processo è creato. Questo evento sarà mandato quando il debuggee è stato appena creato (ma non in eseguzione)o quando il tuo programma si attacca a un processo in eseguzione con DebugActiveProcess. Questo è il primo evento che il tuo programma riceverà |
EXIT_PROCESS_DEBUG_EVENT | Quando un processo viene chiuso |
CREATE_THEAD_DEBUG_EVENT | Quando un nuovo thread è creato nel processo del debuggee o quando il tuo programma si attacca a un processo in eseguzione. Notare che non si ricevono questo tipo di eventi quando il thread principale è creato |
EXIT_THREAD_DEBUG_EVENT | Quando un thread del debuggee viene chiuso. Il tuo programma però non riceverà questo debug-event per il thread principale. In poche parole, puoi vedere il thread principale del debuggee come l'equivalente del processo del debuggee stesso. Quindi quando il tuo programma vede CREATE_PROCESS_DEBUG_EVENT è in realtà CREATE_THREAD_DEBUG_EVENT per il thread principale |
LOAD_DLL_DEBUG_EVENT | Quando il debuggee carica una DLL., Il programma riceverà questo evento solo quando il PE Loader ha risolto i link alle DLL (scrive la IAT NTD) oppure quando il debuggee chiama LoadLibrary |
UNLOAD_DLL_DEBUG_EVENT | Quando una DLL è scaricata dal processo del debuggee |
EXCEPTION_DEBUG_EVENT | Quando viene generata un'eccezione nel processo del debuggee. IMPORTANTE:Questo evento arriverà appena prima che il debuggee inizia a esegure la sua prima istruzione. L'eccezione è attualmente un debug break (int3). Quando vuoi "riesumare" il debuggee chiama ContinueDebugEvent con il flag DBG_CONTINUE. Non usare il flag DBG_EXCEPTION_NOT_HANDLED altrimenti il debuggee si rifiuterà di eseguire su WinNT (e quindi anche su WinXP e Win2k NTD). In Win98 invece va bene. |
OUTPUT_DEBUG_STRING_EVENT | Questo evento è generato quando il debuggee chiama la funzione DebugOutPutString per mandare una stringa di messaggio al tuo programma |
RIP_EVENT | Quando arrivano degli errori di debug di sistema (System Debugging errors NTD) |
dwProcessId e dwThreadId sono le ID del processo e del thread nel quale arrivano gli eventi di debug. Puoi usare questi valori come identificatori del processo/thread a cui sei interessato. Ricorda che se usi CreateProcess per caricare il debuggee, puoi anche prendere la process ID e la thread ID del debuggee nella struttura PROCESS_INFO. Puoi usare questi valori per distinguere i debug-event del debuggee e quelli dei child-process (nel caso tu non abbia specificato il flag DEBUG_ONLY_THIS_PROCESS)
u è un unione che contiene informazioni sul debug-event. Può essere una delle strutture sottostanti, dipende dal valore di dwDebugEventCode
valore in dwDebugEventCode |
Interpretazione di u |
CREATE_PROCESS_DEBUG_EVENT | Una struttura CREATE_PROCESS_DEBUG_INFO chiamata CreateProcessInfo |
EXIT_PROCESS_DEBUG_EVENT | Una struttura EXIT_PROCESS_DEBUG_INFO chiamata ExitProcess |
CREATE_THEAD_DEBUG_EVENT | Una struttura CREATE_THREAD_DEBUG_INFO chiamata CreateThread |
EXIT_THREAD_DEBUG_EVENT | Una struttura EXIT_THREAD_DEBUG_EVENT chiamata ExitThread |
LOAD_DLL_DEBUG_EVENT | Una struttura LOAD_DLL_DEBUG_INFO chiamata LoadDll |
UNLOAD_DLL_DEBUG_EVENT | Una struttura UNLOAD_DLL_DEBUG_INFO chiamata UnloadDll |
EXCEPTION_DEBUG_EVENT | Una struttura EXCEPTION_DEBUG_INFO chiamata Exception |
OUTPUT_DEBUG_STRING_EVENT | Una struttura OUTPUT_DEBUG_STRING_INFO chiamata DebugString |
RIP_EVENT | Una struttura RIP_INFO chiamata RipInfo |
Non spiegherò nei dettagli tutte queste strutture in questo tutorial,solo la struttura CREATE_PROCESS_DEBUG_INFO sarà spiegata.
Immaginiamo che il nostro programma chiamasse WaitForDebugEvent e che ritorni. La prima cosa che dovremo fare sarà esaminare il valore in dwDebugEventCode per vedere che tipo di debug-event è accaduto nel processo del debuggee. Per esempio se il valore in dwDebugEventCode è CREATE_PROCESS_DEBUG_EVENT puoi interpretare il membro "u" come CreateProcessInfo e accedervi tramite u.CreateProcessInfo.
3. Fai qualunque cosa il tuo programma voglia fare in risposta ai debug-event. Quando WaitForDebugEvent ritorna, significa che un debug-event è appena accaduto nel processo del debugggee oppure il timeout è scaduto. Il tuo programma deve esaminare il valore in dwDebugEventCode in modo da reagire al debug-event in modo appropiato. In questo senso è come quando si processano i messaggi di Windows: tu scegli di gestirne alcuni e di tralasciarne altri.
4. Fai continuare l'esecuzione del debuggee. Quando arriva un debug-event, Windows sopende il processo del debuggee. Quando hai finito di gestire gli eventi, devi "spingere" il debuggee in modo da farlo ripartire. Puoi farlo chiamando la funzione ContinueDebugEvent.
ContinueDebugEvent proto dwProcessId:DWORD, dwThreadId:DWORD, dwContinueStatus:DWORD
Questa funzione "riesuma"(fa ripartire, NTD) il thread che è stato precedentemente sospeso a causa della comparsa di un debug-event.
dwProcessId e dwThreadId sono rispettivamente l'ID del processo e del thread che sarà riesumato. Di solito prendi questi valori dai membri dwProcessId e dwThreadId della struttura DEBUG_EVENT. dwContinueStatus specifica come continuare il thread che ha generato il debug-event. Ci sono due possibili valori: DBG_CONTINUE e DBG_EXCEPTION_NOT_HANDLED.Questi due valori fanno la stessa per tutti i debug-event: continuano il thread. L'eccezione è EXCEPTION_DEBUG_EVENT. Se il thread riporta un'eccezione, significa che si è avuta un'eccezione del thread del debuggee. Se specifichi DBG_CONTINUE, il thread ignorerà il proprio gestore di eccezioni (vedi il corso NewBie N°13 NTD) e continuerà l'eseguzione. Per questo, il tuo programma deve esaminare e risolvere le eccezioni prima di riesumare il thread con DBG_CONTINUE oppure l'eccezione arriverà ancora e ancora...Se specifichi DBG_EXCEPTION_NOT_HANDLED, il tuo programma sta dicendo a Windows che è incapace di gestire l'eccezione. Windows dovrà usare il gestore di eccezioni di default del debuggee per poter gestire l'eccezione. In conclusione, se il debug-event si riferisce a un'eccezione avvenuta nel processo del debuggee, dovresti chiamare ContinueDebugEvent con il flag DBG_CONTINUE se il tuo programma ha già risolto l'eccezione. Altrimenti, il tuo programma deve chiamare ContinueDebugEvent con il flag DBG_EXCEPTION_NOT_HANDLED. Dovresti sempre usare DBG_CONTINUE tranne in un caso:il primo EXCEPTION_DEBUG_EVENT che ha il valore EXCEPTION_BREAKPOINT nel membro ExceptionCode. Quando il debuggee sta per eseguire la sua primissima istruzione, il tuo programma riceverà un eccezione (che è un tipo di debug-event NTD) che precisamente è un debug-break (INT 3). Se rispondi chiamando ContinueDebugEvent con il flag DBG_EXCEPTION_NOT_HANDLED, Windows NT (e quindi anche XP e 2000) si rifiuterà di eseguire il debuggee (perchè non ne tiene conto). Devi sempre usare il flag DBG_CONTINUE per dire a Windows che vuoi che il thread vada avanti.
5.Continuare questo ciclo all'infinito finchè il processo del debuggee esce. Il tuo programma deve rimanere in un loop infinito come un message-loop finchè il debuggee non chiude. Il loop dovrebbe essere tipo questo:
.while TRUE
invoke WaitForDebugEvent, addr DebugEvent, INFINITE
.break .if DebugEvent.dwDebugEventCode==EXIT_PROCESS_DEBUG_EVENT
<Gestione dei debug-event>
invoke ContinueDebugEvent, DebugEvent.dwProcessId, DebugEvent.dwThreadId, DBG_EXCEPTION_NOT_HANDLED
.endw
Ecco la sorpresa: Una volta che sei partito a debuggare un programma, non puoi staccarti finchè il programma non esce.
Facciamo un breve riepilogo:
1. Creare un processo o "attaccare" il tuo programma a un processo in esecuzione
2. Aspettare i debug-events
3. Fai qualunque cosa il tuo programma voglia fare in risposta ai debug-event.
4. Fai continuare l'esecuzione del debuggee
5.Continuare questo ciclo all'infinito finchè il processo del debuggee esce.
Esempio:
Questo esempio debugga un programma in Win32 e mostra importanti informazioni come l'handle del processo, ID, imagebase etc.
.386 |
ANALISI:
Il programma riempie la struttura OPENFILENAME e chiama GetOpenFileName per far scegliere all'utente il programma da debuggare.
invoke GetStartupInfo,addr startinfo invoke CreateProcess, addr buffer,NULL,NULL,NULL,FALSE,DEBUG_PROCESS + DEBUG_ONLY_THIS_PROCESS,NULL,NULL,addr startinfo,addr pi |
Quando l'utente ne sceglie uno, il programma chiama CreateProcess per caricare il programma. Poi chiama GetStartupInfo per riempire la struttura STARTUPINFO con i valori di default. Notare il fatto che stiamo usando DEBUG_PROCESS combinato con DEBUG_ONLY_THIS_PROCESS in modo da debuggare solo il nostro programma e non i processi figli di quest'ultimo.
.while TRUE invoke WaitForDebugEvent, addr DBEvent, INFINITE |
Quando il debuggee è caricato, entriamo nel debug-loop infinito, dove viene chiamato WaitForDebugEvent. WaitForDebugEvent non ritornerà finchè non si verifica un debug-event nel debuggee, questo perchè abbiamo specificato il flag INFINITE come suo secondo parametro. Quando si verifica un debug-event, WaitForDebugEvent ritorna e DBEvent (cioè la nostra struttura NTD) è riempita con le informazioni riguardanti il debug-event.
.if DBEvent.dwDebugEventCode==EXIT_PROCESS_DEBUG_EVENT invoke MessageBox, 0, addr ExitProc, addr AppName, MB_OK+MB_ICONINFORMATION .break |
Prima controlliamo il valore di dwDebugEventCode: Se è EXIT_PROCESS_DEBUG_EVENT mostriamo il messaggio che dice "il debuggee è uscito" e quindi usciamo dal
debug-loop.
.elseif DBEvent.dwDebugEventCode==CREATE_PROCESS_DEBUG_EVENT invoke wsprintf, addr buffer, addr ProcessInfo, DBEvent.u.CreateProcessInfo.hFile, DBEvent.u.CreateProcessInfo.hProcess, DBEvent.u.CreateProcessInfo.hThread, DBEvent.u.CreateProcessInfo.lpBaseOfImage, DBEvent.u.CreateProcessInfo.lpStartAddress invoke MessageBox,0, addr buffer, addr AppName, MB_OK+MB_ICONINFORMATION |
Se il valore di dwDebugEventCode è CREATE_PROCESS_DEBUG_EVENT, mostriamo un pò di informazioni sul debuggee, tutto su una message-box. Otteniamo queste informazioni da u.CreateProcessInfo. CreatProcessInfo è una struttura del tipo CREATE_PROCESS_DEBUG_INFO. Puoi prelevare altre informazioni su questa struttura dalla API Reference.
.elseif DBEvent.dwDebugEventCode==EXCEPTION_DEBUG_EVENT .if DBEvent.u.Exception.pExceptionRecord.ExceptionCode==EXCEPTION_BREAKPOINT invoke ContinueDebugEvent, DBEvent.dwProcessId, DBEvent.dwThreadId, DBG_CONTINUE .continue .endif |
Se il valore di dwDebugEventCode è EXCEPTION_DEBUG_EVENT dobbiamo capire che tipo di eccezione è. E' una lunga serie di strutture nidificate (vedi tutorial N°13 per newbie! NTD) ma puoi ottenere il tipo d'eccezione dal membro ExceptionCode. Sei il valore di ExceptionCode è EXCEPTION_BREAKPOINT e arriva per la prima volta (oppure siamo sicuri che il debuggee non abbia nel codice degli int 3h) possiamo dire senza sbagliare che il debuggee sta per eseguire la sua prima istruzione. Abbiamo finito con il processing (ovvero di controllare i debug-event NTD), dobbiamo chiamare ContinueDebugEvent con il flag DBG_CONTINUE per permettere al debuggee di ripartire. Quindi aspettiamo il prossimo debug-event.
.elseif DBEvent.dwDebugEventCode==CREATE_THREAD_DEBUG_EVENT invoke MessageBox,0, addr NewThread, addr AppName, MB_OK+MB_ICONINFORMATION .elseif DBEvent.dwDebugEventCode==EXIT_THREAD_DEBUG_EVENT invoke MessageBox,0, addr EndThread, addr AppName, MB_OK+MB_ICONINFORMATION .endif |
Se il valore di dwDebugEventCode è CREATE_THREAD_DEBUG_EVENT o EXIT_THREAD_DEBUG_EVENT, mostriamo il testo che lo dice
invoke ContinueDebugEvent, DBEvent.dwProcessId, DBEvent.dwThreadId, DBG_EXCEPTION_NOT_HANDLED .endw |
Eccetto per il caso di EXCEPTION_DEBUG_EVENT sopra, chiamiamo ContinueDebugEvent con il flag DBG_EXCEPTION_NOT_HANDLED
invoke CloseHandle,pi.hProcess invoke CloseHandle,pi.hThread |
Quando il debuggee esce, siamo fuori dal debug-loop e dobbiamo chiudere entrambi i processi e gli handle dei thread del debuggee. Chiuderli non significa chiudere il processo/thread. Significa solo che non vogliamo usare più questi handle per riferirci al processo/thread.
§-Death_Reaver-
Note finali |
Ringrazio tutti, ma soprattutto quequero che visto che è un bravo ragaz...papero correggierà tutti i miei errrori ortogggraficci
Disclaimer |
Vorrei ricordare che il software va comprato e non rubato, dovete registrare il vostro prodotto dopo il periodo di valutazione. Non mi ritengo responsabile per eventuali danni causati al vostro computer determinati dall'uso improprio di questo tutorial. Questo documento stato scritto per invogliare il consumatore a registrare legalmente i propri programmi, e non a fargli fare uso dei tantissimi file crack presenti in rete, infatti tale documento aiuta a comprendere lo sforzo che ogni sviluppatore ha dovuto portare avanti per fornire ai rispettivi consumatori i migliori prodotti possibili.BLA BLA BLA
Reversiamo al solo scopo informativo e per migliorare la nostra conoscenza del linguaggio Assembly. SI SI Jalappi Cia' Cia' Cia 'Ciao