Kernel Debugging: Pronti? Via..

La prima volta.

Ci eravamo lasciati in questa situazione: Windbg avviato as admin, la VM configurata per il kernel debugging su seriale, ma spenta.

Se non lo siamo, chiudiamo tutto ed eseguiamo questi passi nell’ordine esatto.

Avviamo Windbg “As Administrator”. Verifichiamo che Symbol Path contenga il percorso corretto al nostro symbol store e il riferimento al server pubblico Microsoft, e andiamo a selezionare Kernel Debugger. Controlliamo che il tab COM sia configurato per collegarsi alla pipe creata, che "Reconnect” sia spuntato e premiamo “OK”.

Apparirà questo messaggio all’interno della finestra Command del debugger:

 Microsoft (R) Windows Debugger Version 6.11.0002.408 X86
Copyright (c) Microsoft Corporation. All rights reserved. 

Opened \\.\pipe\VistaDebug
Waiting to reconnect...

Il debugger è pronto in attesa che sulla pipe arrivino le informazioni dalla macchina target.

Avviamo ora la VM.

Dopo qualche istante, il sistema inizierà a mandare delle informazioni in output:

 Connected to Windows Server 2008/Windows Vista 6002 x86 compatible target at (Wed Dec 16 14:23:06.947 2009 (UTC + 1:00)), ptr64 FALSE
Kernel Debugger connection established.
Symbol search path is: D:\Symbols;srv*D:\Symbols*\\symbols\symbols
Executable search path is: 
Windows Server 2008/Windows Vista Kernel Version 6002 MP (1 procs) Free x86 compatible
Built by: 6002.18082.x86fre.vistasp2_gdr.090803-2339
Machine Name:
Kernel base = 0x81a49000 PsLoadedModuleList = 0x81b60c70
System Uptime: not available

XP si metteva subito in BreakPoint, ho notato che Vista non lo fa. Almeno, nel mio caso, ha raggiunto la schermata iniziale e ho dovuto premere io il pulsante “Break” Break sulla toolbar per poter vedere questo mesaggio:

 Break instruction exception - code 80000003 (first chance)
*******************************************************************************
*                                                                             *
*   You are seeing this message because you pressed either                    *
*       CTRL+C (if you run kd.exe) or,                                        *
*       CTRL+BREAK (if you run WinDBG),                                       *
*   on your debugger machine's keyboard.                                      *
*                                                                             *
*                   THIS IS NOT A BUG OR A SYSTEM CRASH                       *
*                                                                             *
* If you did not intend to break into the debugger, press the "g" key, then   *
* press the "Enter" key now.  This message might immediately reappear.  If it *
* does, press "g" and "Enter" again.                                          *
*                                                                             *
*******************************************************************************
nt!RtlpBreakWithStatusInstruction:
81af4a98 cc              int     3

Questo messaggio è la conferma che il sistema è sotto Kernel Debugger.

Giusto per sicurezza, verifichiamo che il debugger risponda ai comandi, usando il comando “vertarget”:

 kd> vertarget
Windows Server 2008/Windows Vista Kernel Version 6002 (Service Pack 2) MP (1 procs) Free x86 compatible
Built by: 6002.18082.x86fre.vistasp2_gdr.090803-2339
Machine Name: "MRRVISTALAB"
Kernel base = 0x81a49000 PsLoadedModuleList = 0x81b60c70
Debug session time: Wed Dec 16 14:24:28.961 2009 (UTC + 1:00)
System Uptime: 0 days 0:01:06.051

E’ tutto a posto. la macchina virtuale è sotto Kernel Debugger e il debugger riesce ad esaminare il target. Abbiamo accesso completo al kernel del sistema operativo.

A questo punto, la macchina virtuale è bloccata dal debugger. Dobbiamo farla ripartire e collegarci alla stessa premendo CTRL+ALT+CANC ed eseguire il logon.

Per farla ripartire, si preme il pulsanto “GOgo  sulla toolbar o F5 o si digita “g” sul prompt dei comandi in windbg.

A questo punto siamo pronti per avviare l’applicazione che vogliamo debuggare, o per riprodurre il problema che stiamo cercando di risolvere, come era nel mio caso.

Avviamo l’applicazione che vogliamo debuggare, ad esempio Notepad, e poi interrompiamo la macchina virtuale, premendo il pulsante Break Break

Dobbiamo eseguire una serie di comandi che ci permetteranno di entrare nel contesto del processo che vogliamo debuggare, perchè ricordiamoci che in Kernel, la memoria è condivisa, e tutti i thread di tutti i processi in esecuzione sono a disposizione, ma noi abbiamo bisogno solo dei thread relativi alla nostra applicazione.

1) troviamo il processo che ci interessa. Eseguiamo il comando

!process 0 0

ed esaminiamo l’output, finchè troviamo il nostro processo (di solito è l’ultimo).

 kd> !process 0 0
**** NT ACTIVE PROCESS DUMP ****
PROCESS 87e90a70  SessionId: none  Cid: 0004    Peb: 00000000  ParentCid: 0000
    DirBase: 00122000  ObjectTable: 8b400058  HandleCount: 601.
    Image: System

PROCESS 89c5a2d0  SessionId: none  Cid: 01c0    Peb: 7ffdc000  ParentCid: 0004
    DirBase: 3de18020  ObjectTable: 8b463a28  HandleCount:  28.
    Image: smss.exe
...

PROCESS 8855fb78  SessionId: 1  Cid: 03dc    Peb: 7ffdf000  ParentCid: 0420
    DirBase: 3de181a0  ObjectTable: a030c6f0  HandleCount:  48.
    Image: notepad.exe

2) ci mettiamo nel contesto del processo con questo comando

.process /i <process>

dove <process> nel nostro caso vale 8855fb78, quindi il comando reale sarà:

 kd> .process /i 8855fb78
You need to continue execution (press 'g' <enter>) for the context
to be switched. When the debugger breaks in again, you will be in
the new process context.

3) come dice il messaggio ritornato dal comando, dovete premere “g”, “Go” per poter effettivamente switchare nel contesto del processo:

 kd> g
Break instruction exception - code 80000003 (first chance)
nt!RtlpBreakWithStatusInstruction:
81af4a98 cc              int     3

Adesso siamo effettivamente nel processo che ci interessa.

4) un ulteriore variante del comando .process ci assicurerà di essere nel posto giusto, e caricherà i simboli del processo user mode.

.process /p /r <process>

 kd> .process /p /r 8855fb78
Implicit process is now 8855fb78
.cache forcedecodeuser done
Loading User Symbols
....................

 

5) i simboli di debug, come non smetterò mai di ripetere, sono fondamentali. E’ sempre buona norma essere certi che i simboli siano disponibili e caricati. Per cui, anche se non è strettamente necessario, visto che il comando precedente li ha caricati, è buona norma forzare il reload degli stessi.

.reload /user

 kd> .reload /user
Loading User Symbols
....................

Nel caso siate collegati ad internet, a questo punto potrebbe impiegare qualche minuto a scaricare i simboli dal server pubblico Microsoft. Attendete pazientemente.. un caffè può aiutare in questi casi Smile

 

6) impostate i breakpoint che dovete impostare nella vostra applicazione e che volete seguire poi in kernel mode. Giusto per test, la CreateFile è una di quelle funzioni base, che viene chiamata spesso e volentieri anche solo per verificare la security applicata ad un file o se il file esiste prima di crearlo, e sicuramente switcha in Kernel mode, dove le sue controparti potranno a questo punto essere esaminate. Attenzione che siccome questa funzione viene richiamata spessimssimo potrebbe accadere che vi troviate fuori dal vostro processo, e a quel punto dovrete ripartire da capo risincronizzandovi col processo che volete debuggare. Windows è multitasking, quindi purtroppo questa cosa non si può evitare..

BP è il comando per impostare un BreakPoint. Quindi ad esempio:

bp Kernel32!CreateFileW

 kd> bp kernel32!createfilew

Se windbg non ritorna errori, vuol dire che abbiamo impostato con successo il breakpoint e adesso possiamo fare ripartire la macchina virtuale.

 

 

Oppure, si può impostare un breakpoint globale, su una funzione che verrà chiamata da qualunque processo:

Ad esempio, se imposto un BreakPoint su NtCreateUserProcess, e poi da Start, Run, scrivo Notepad, per avviare una istanza di Notepad, e premo OK, prima o poi arriverò alla NtCreateUserProcess

image

 

Se controlliamo lo stack a questo punto possiamo vedere come siamo arrivati qui:

 kd> kL200
ChildEBP RetAddr  
05a3d680 774d5c20 ntdll!NtCreateUserProcess
05a3dc94 774b1c1f kernel32!CreateProcessInternalW+0xf85
05a3dccc 763becc6 kernel32!CreateProcessW+0x2c
05a3ddc4 763bdd50 SHELL32!_SHCreateProcess+0x254
05a3de18 763beaa3 SHELL32!CExecuteApplication::_CreateProcess+0xfe
05a3de28 763bea5a SHELL32!CExecuteApplication::_TryCreateProcess+0x2e
05a3de38 763bddd8 SHELL32!CExecuteApplication::_DoApplication+0x48
05a3de48 763be0ab SHELL32!CExecuteApplication::Execute+0x33
05a3de68 763bd981 SHELL32!CExecuteAssociation::_DoCommand+0x70
05a3de88 763bdb0b SHELL32!CExecuteAssociation::_TryApplication+0x3e
05a3dea0 76372fbd SHELL32!CExecuteAssociation::Execute+0x30
05a3dec0 76372f34 SHELL32!CAssocMenu::_Execute+0x4c
05a3e130 76372e0e SHELL32!CAssocMenu::InvokeCommand+0x9d
05a3e1a0 76372d45 SHELL32!HDXA_LetHandlerProcessCommandEx+0xbf
05a3e43c 76451e3d SHELL32!CDefFolderMenu::InvokeCommand+0x1e3
05a3e4a8 76451d71 SHELL32!CShellExecute::_InvokeInProcExec+0xb9
05a3e4c4 76451d1c SHELL32!CShellExecute::_InvokeCtxMenu+0x39
05a3e4dc 763c1ac6 SHELL32!CShellExecute::_DoExecute+0x5a
05a3e4f0 763bc1ea SHELL32!CShellExecute::ExecuteNormal+0x87
05a3e504 763bc177 SHELL32!ShellExecuteNormal+0x33
05a3e51c 7640db74 SHELL32!ShellExecuteExW+0x62
05a3e790 7640d9a9 SHELL32!ShellExecCmdLine+0x1aa
05a3ec58 7640dcd4 SHELL32!CRunDlg::OKPushed+0x169
05a3ecb0 77a2fd72 SHELL32!RunDlgProc+0x162
05a3ecdc 77a249a9 USER32!InternalCallWinProc+0x23
05a3ed58 77a2481f USER32!UserCallDlgProcCheckWow+0x132
05a3eda0 77a24a33 USER32!DefDlgProcWorker+0xa8
05a3edbc 77a2fd72 USER32!DefDlgProcW+0x22
05a3ede8 77a2fe4a USER32!InternalCallWinProc+0x23
05a3ee60 77a30943 USER32!UserCallWinProcCheckWow+0x14b
05a3eea0 77a30b36 USER32!SendMessageWorker+0x4b7
05a3eec0 74c4b4b2 USER32!SendMessageW+0x7c
05a3eee0 74c4b514 COMCTL32!Button_NotifyParent+0x3d
05a3eefc 74c4b61f COMCTL32!Button_ReleaseCapture+0x112
05a3ef5c 77a2fd72 COMCTL32!Button_WndProc+0xa98
05a3ef88 77a2fe4a USER32!InternalCallWinProc+0x23
05a3f000 77a3018d USER32!UserCallWinProcCheckWow+0x14b
05a3f064 77a3022b USER32!DispatchMessageWorker+0x322
05a3f074 77a251f5 USER32!DispatchMessageW+0xf
05a3f098 77a43bc5 USER32!IsDialogMessageW+0x586
05a3f0d4 77a42dc0 USER32!DialogBox2+0x143
05a3f0fc 77a42eec USER32!InternalDialogBox+0xd0
05a3f11c 77a410ef USER32!DialogBoxIndirectParamAorW+0x37
05a3f140 7640d747 USER32!DialogBoxParamW+0x3f
05a3f164 7640d680 SHELL32!SHFusionDialogBoxParam+0x32
05a3f1a0 00c44602 SHELL32!RunFileDlg+0xe8
WARNING: Frame IP not in any known module. Following frames may be wrong.
05a3f7e0 00c4442f 0xc44602
05a3fc84 7726c2c9 0xc4442f
05a3fd08 774fd0e9 SHLWAPI!WrapperThreadProc+0x11c
05a3fd14 778919bb kernel32!BaseThreadInitThunk+0xe
05a3fd54 7789198e ntdll!__RtlUserThreadStart+0x23
05a3fd6c 00000000 ntdll!_RtlUserThreadStart+0x1b

Si può notare che CRunDlg::OKPushed, cioè ho premuto OK sulla dialog di Run, chiamiamo ShellExecuteExW, la quale dopo un pò di preparazione, arriva a chiamare kernel32!CreateProcessW, che a sua volta chiama kernel32!CreateProcessInternalW e finalmente ntdll!NtCreateUserProcess. 

 

Senza simboli privati o se non state creando un device driver, è difficile che il Kernel Debugging vi possa interessare, ma potrebbe capitare che vi venga qualche curiosità da soddisfare, e seguendo i passi che vi ho mostrato riuscirete a iniziare un Kerne Debugging con successo.

Vi lascio con un link molto utile:

148660    How to Verify Windows Debug Symbols

http://support.microsoft.com/default.aspx?scid=kb;EN-US;148660

 

Alla prossima!

Mario Raccagni

Senior Support Engineer

Platform Development Support Team