Debugger működése Megszakítás és nyomkövetés. Izsó Tamás 2013. szeptember 19. Izsó Tamás Debugger működése/ 1
Section 1 Interrupt kezelés Izsó Tamás Debugger működése/ 2
Interrupt és kivételkezelés Interrupt és kivételdobás események megszakítják a processzor utasítás végrehajtásnak normális folyamatát. Az interrupt lehet szinkron, és aszinkron. Aszinkron interrupt esetén a processzor képes az utasítás végrehajtás ismétlésére. A megszakítást mindig az operációs rendszer kapja el és továbbítja. Izsó Tamás Debugger működése/ 3
Interrupt és kivétel továbbítása Trap Handler Megszakítás feljegyzés Debugger port Debugger első esély megszakítás kezelő Kivétel diszpécser Debugger port Debugger második esély Kivétel port Alrendszer (pl. POSIX) Alapértelmezett megszakítás kezelő Izsó Tamás Debugger működése/ 4
Interrupt és kivétel továbbítása Ha egy processz debugger alatt lett elindítva, akkor a rendszer a megszakításról üzenetet küld a debuggernek. Ha a processz nem debugger alatt fut, akkor a megszakítás feljegyzést a kernel a user stack-re teszi vmilyen (CONTEXT) formában, és meghívja a user által írt megszakításkezelőt. Ha nincs kivételkezelő rutin, akkor a processz visszatér kernel módba, és ha van debugger akkor meghívja azt (második esély). Különben az alapértelmezett kivételkezelő fut le. Ha a processz az operációs rendszer alrendszere, akkor az alrendszer számára egy megfelelő formába adja át az interrupt adatokat. Izsó Tamás Debugger működése/ 5
Hibalekezelés C-ben #include < s t d i o. h> #include <Windows. h> void TestExceptions ( ) { i n t a, b=10; p r i n t f ( " T r i g g e r i n g SEH exception \ n " ) ; a = b / 0; i n t main ( ) { t r y { TestExceptions ( ) ; except ( GetExceptionCode ( ) == EXCEPTION_INT_DIVIDE_BY_ZERO? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { p r i n t f ( " Hiba lekezelve \ n " ) ; p r i n t f ( " Nincs hibakezelés \ n " ) ; TestExceptions ( ) ; return 0; Izsó Tamás Debugger működése/ 6
try és except nem szabványos C nyelvi elem EXCEPTION_EXECUTE_HANDLER itt kezeljük le a megszakítást. EXCEPTION_CONTINUE_SEARCH következő regisztrált megszakításkezelő keresése. Izsó Tamás Debugger működése/ 7
Demo Interrupt kezelés Debugger cd E:\Reverse.Uj\Debug\example1\Debug\ devidebyzero.exe stackoverflow.exe breakpoint.exe Mit tapasztalunk, ha a breakpoint programot debugger alatt futtatjuk? Hol használható fel ez a tapasztalat? Izsó Tamás Debugger működése/ 8
Mit kell megjegyezni? Elveket, de a konkrét függvényneveket, struktúrákat nem! Izsó Tamás Debugger működése/ 9
Kivételkezelő függvény prototípusa A következő függvényt az excp.h header file tartalmazza. EXCEPTION_DISPOSITION cdecl _ except_ handler ( / I n / struct _EXCEPTION_RECORD _ExceptionRecord, / I n / void _EstablisherFrame, / I n o u t / struct _CONTEXT _ContextRecord, / I n o u t / void _DispatcherContext ) ; Izsó Tamás Debugger működése/ 10
_except_handler visszatérési értéke / Exception disposition return values. / typedef enum _EXCEPTION_DISPOSITION { ExceptionContinueExecution, ExceptionContinueSearch, ExceptionNestedException, ExceptionCollidedUnwind EXCEPTION_DISPOSITION ; ExceptionContinueSearch következő kivételkezelő keresése ExceptionContinueExecution futás folytatása Az utolsó két értéket figyelmen kívül hagyjuk. Izsó Tamás Debugger működése/ 11
_except_handler első paramétere A paraméter típusát a winnt.h header file tartalmazza. typedef s t r u c t _EXCEPTION_RECORD { DWORD ExceptionCode ; DWORD ExceptionFlags ; s t r u c t _EXCEPTION_RECORD ExceptionRecord ; PVOID ExceptionAddress ; DWORD NumberParameters ; ULONG_PTR ExceptionInformation [EXCEPTION_MAXIMUM_PARAMETERS ] ; EXCEPTION_RECORD; ExceptionCode a kivételdobás (interrupt) oka. ExceptionAddress a hiba helye. többitől most eltekintünk. Izsó Tamás Debugger működése/ 12
_EXCEPTION_RECORD-ban az ExceptionCode EXCEPTION_ACCESS_VIOLATION EXCEPTION_DATATYPE_MISALIGNMENT EXCEPTION_BREAKPOINT EXCEPTION_SINGLE_STEP EXCEPTION_ARRAY_BOUNDS_EXCEEDED EXCEPTION_FLT_DENORMAL_OPERAND EXCEPTION_FLT_DIVIDE_BY_ZERO EXCEPTION_FLT_INEXACT_RESULT EXCEPTION_FLT_INVALID_OPERATION EXCEPTION_FLT_OVERFLOW EXCEPTION_FLT_STACK_CHECK EXCEPTION_FLT_UNDERFLOW EXCEPTION_INT_DIVIDE_BY_ZERO EXCEPTION_INT_OVERFLOW EXCEPTION_PRIV_INSTRUCTION EXCEPTION_IN_PAGE_ERROR EXCEPTION_ILLEGAL_INSTRUCTION EXCEPTION_NONCONTINUABLE_EXCEPTION EXCEPTION_STACK_OVERFLOW EXCEPTION_INVALID_DISPOSITION EXCEPTION_GUARD_PAGE EXCEPTION_INVALID_HANDLE EXCEPTION_POSSIBLE_DEADLOCK Izsó Tamás Debugger működése/ 13
Strukturált exception handler SEH regisztráció kivételkezelő függvény címe 0xFFFFFFFF Egyéb adatok kivételkezelő függvény címe előző kivételkezelő regisztráció FS:[0] Kivételkezelő regisztráció Thread Information Block kivételkezelő függvény címe előző kivételkezelő regisztráció EXCEPTION_DISPOSITION cdecl _except_handler ( struct _EXCEPTION_RECORD *_ExceptionRecord, void * _EstablisherFrame, struct _CONTEXT *_ContextRecord, void * _DispatcherContext ) {............... stack Izsó Tamás Debugger működése/ 14
Saját kivételkezelő első kísérlet # include <windows. h> #include < s t d i o. h> typedef EXCEPTION_DISPOSITION ( pexcept_handler ) ( struct _EXCEPTION_RECORD ExceptionRecord, void EstablisherFrame, struct _CONTEXT ContextRecord, void DispatcherContext ) ; struct EXCEPTION_REGISTRATION_FIELD { struct EXCEPTION_REGISTRATION_FIELD prev ; pexcept_handler handler ; ; i n t adat ; struct EXCEPTION_REGISTRATION_FIELD pregister =0; struct EXCEPTION_REGISTRATION_FIELD prev =0; Izsó Tamás Debugger működése/ 15
Saját kivételkezelő első kísérlet EXCEPTION_DISPOSITION cdecl my_except_handler ( st ru ct _EXCEPTION_RECORD ExceptionRecord, void EstablisherFrame, st ru ct _CONTEXT ContextRecord, void DispatcherContext ) { p r i n t f ( " Megszakitaskezelo f u t... \ n " ) ; / / Hiba kijavítása ContextRecord >Eax = ( i n t )& adat ; / / Megszakítást okozó utasítás megismétlése return ExceptionContinueExecution ; Izsó Tamás Debugger működése/ 16
Saját kivételkezelő első kísérlet i n t main ( ) { / / Stack-be felépítjük a láncolást asm { sub ESP, 8 mov dword p t r pregister, ESP mov EAX, FS : [ 0 ] mov dword p t r prev,eax pregister >h a n d l e r = my_except_handler ; pregister >prev = prev ; / / TIB-be regisztráljuk a hibakezelőnket asm { mov EAX, dword p t r pregister mov FS : [ 0 ], EAX Izsó Tamás Debugger működése/ 17
Saját kivételkezelő első kísérlet / / Megpróbáljuk a 0-ás címen lévő adatot / / felülírni asm { mov EAX, 0 mov [EAX], 2 p r i n t f ( " Hiba utan %d \ n ", adat ) ; / / saját kivételkezelő megszüntetése asm { mov eax, [ ESP] mov FS : [ 0 ], EAX add esp, 8 return 0; Izsó Tamás Debugger működése/ 18
a program fordítása Interrupt kezelés Debugger cl -c kiserlet1.c link /SAFESEH:NO kiserlet1.obj Izsó Tamás Debugger működése/ 19
Demo Interrupt kezelés Debugger E:\Reverse.Uj\Debug\Kiserlet\kiserlet1.exe Megszakitaskezelo fut... Hiba utan 2 Mit látunk a futás eredménye alapján. Izsó Tamás Debugger működése/ 20
Mit tudtunk meg? Hogyan kell saját kivételkezelőt regisztrálni. A megszakított program megismétli a megszakítást okozó utasítást, ha ExceptionContinueExecution értékkel térünk vissza. Biztonsági okok miatt a /SAFESEH:NO értékkel kell linkelni a programot. Izsó Tamás Debugger működése/ 21
Hogy működik a beépített megszakításkezelő? Kísérlet megtervezése: Írjunk saját megszakításkezelőt de ne kezeljük le a hibát, hanem térjünk vissza ExceptionContinueSearch értékkel. A main használja a _try.. _except kivételkezelést. A main hívjon meg egy függvényt, amelyben saját kivételkezelőt regisztrálunk. Kiírásokkal kövessük nyomon a vezérlést. Izsó Tamás Debugger működése/ 22
Beépített kivételkezelő második kísérlet EXCEPTION_DISPOSITION cdecl my_except_handler ( s t r u c t _EXCEPTION_RECORD ExceptionRecord, void EstablisherFrame, struct _CONTEXT ContextRecord, void DispatcherContext ) { p r i n t f ( " Megszakitaskezelo f u t, exception f l a g %08X \ n ", ExceptionRecord >ExceptionFlags ) ; / / Nem itt foglalkozunk a megszakítással. return ExceptionContinueSearch ; Izsó Tamás Debugger működése/ 23
Beépített kivételkezelő második kísérlet void Test ( ) { / / Stack-be felépítjük a láncolást asm { sub ESP, 8 mov dword p t r pregister, ESP mov EAX, FS : [ 0 ] mov dword p t r prev,eax pregister >handler = my_except_handler ; pregister >prev = prev ; / / TIB-be regisztráljuk a hibakezelőnket asm { mov EAX, dword p t r pregister mov FS : [ 0 ], EAX ( char )0 = X ; / / EXCEPTION_ACCESS_VIOLATION p r i n t f ( " Ide nem j u t u n k soha! \ n " ) ; Izsó Tamás Debugger működése/ 24
Beépített kivételkezelő második kísérlet i n t main ( ) { _ t r y { Test ( ) ; _except ( EXCEPTION_EXECUTE_HANDLER ) { p r i n t f ( " K i v e t e l elkapasa main() ben \ n " ) ; p r i n t f ( " K i v e t e l utan a main() ben \ n " ) ; return 0; Output: Megszakitaskezelo fut, exception flag 00000000 Megszakitaskezelo fut, exception flag 00000002 Kivetel elkapasa main()-ben Kivetel utan a main()-ben Izsó Tamás Debugger működése/ 25
Működés 1 A főprogram _try... _except része a beépített kivételkezelőt regisztrálja a stack-en. 2 A _try részben meghívjuk a Test() függvényt. 3 A Test() függvény saját kivételkezelőt regisztrál a stack-en. 4 A Test() függvény tárvédelmi hibát okoz. 5 Az operációs rendszer megkapja a megszakítást és a megfelelő négy paraméterrel meghívja a legutoljára létesített kivételkezelő eljárást. 6 A saját exception handler továbbpasszolja a hibakezelést. 7 A main lekezeli a hibát. 8 A rendszer másodszor is meghívja a megszakításkezelőket, abból a célból, hogy a stack-et kisöpörjék. Ezt a szándékot az ExceptionRecord >ExceptionFlags paraméter tartalmazza. C++ esetén ilyenkor hívódnak meg a létrejött objektumok destruktorai. Izsó Tamás Debugger működése/ 26
Section 2 Debugger Izsó Tamás Debugger működése/ 27
Debugger felépítése Proccess indítás nyomkövetéssel. Nyomkövető ciklus létrehozása. STARTUPINFO s i ; PROCESS_INFORMATION p i ; ZeroMemory ( &si, sizeof ( s i ) ) ; s i. cb = sizeof ( s i ) ; ZeroMemory ( &pi, sizeof ( p i ) ) ; CreateProcess ( ProcessNameToDebug, NULL, NULL, NULL, FALSE, DEBUG_ONLY_THIS_PROCESS, NULL, NULL, &si, & p i ) ; Izsó Tamás Debugger működése/ 28
Definíciók és követelmények 1 Debugger az a processz amely egy másik processzt monitoroz. 2 Debuggolt program a nyomkövetett program. 3 Debuggolt program csak egy debugger-hez lehet hozzárendelve. 4 Egy debugger több processzt is nyomkövethet. 5 A processz indítása és a processz debuggolására alkalmas ciklus ugyanabban a processzben kell hogy legyen. Izsó Tamás Debugger működése/ 29
Debug ciklus DEBUG_EVENT debug_event = { 0 ; for ( ; ; ) { i f (! WaitForDebugEvent (& debug_event, INFINITE ) ) return ; / / User-defined function, not API ProcessDebugEvent (& debug_event ) ; ContinueDebugEvent ( debug_event. dwprocessid, debug_event. dwthreadid, DBG_CONTINUE ) ; DBG_CONTINUE folytatjuk a nyomkövetést. DBG_EXCEPTION_NOT_HANDLED valami hiba volt és feladjuk. Izsó Tamás Debugger működése/ 30
Nyomkövetési esemény rekordja struct DEBUG_EVENT { DWORD dwdebugeventcode ; DWORD dwprocessid ; DWORD dwthreadid ; union { EXCEPTION_DEBUG_INFO Exception ; CREATE_THREAD_DEBUG_INFO CreateThread ; CREATE_PROCESS_DEBUG_INFO CreateProcessInfo ; EXIT_THREAD_DEBUG_INFO ExitThread ; EXIT_PROCESS_DEBUG_INFO ExitProcess ; LOAD_DLL_DEBUG_INFO Loa ddll ; UNLOAD_DLL_DEBUG_INFO UnloadDll ; OUTPUT_DEBUG_STRING_INFO DebugString ; RIP_INFO R i p I n f o ; u ; ; Izsó Tamás Debugger működése/ 31
Debug ciklus kicsit részletesebben bool bcontinuedebugging = t r u e ; while ( bcontinuedebugging ) { i f (! WaitForDebugEvent (& debug_event, INFINITE ) ) return ; switch ( debug_event. dwdebugeventcode ) { case CREATE_PROCESS_DEBUG_EVENT:... break ; case CREATE_THREAD_DEBUG_EVENT:... break ; case EXIT_THREAD_DEBUG_EVENT:... break ; case EXIT_PROCESS_DEBUG_EVENT:... break ; case LOAD_DLL_DEBUG_EVENT:... break ; case UNLOAD_DLL_DEBUG_EVENT:... break ; case OUTPUT_DEBUG_STRING_EVENT:... break ; case EXCEPTION_DEBUG_EVENT:... EXCEPTION_DEBUG_INFO& exception = debug_event. u. Exception ;... break ; Izsó Tamás Debugger működése/ 32
CREATE_PROCESS_DEBUG_EVENT esemény Ez az esemény a nyomkövetett program betöltése után a futás előtt következik be. Itt lehet megadni a breakpoint-ot. s t r u c t CREATE_PROCESS_DEBUG_INFO { HANDLE h F i l e ; / / (.EXE) file handle HANDLE hprocess ; / / process handle HANDLE hthread ; / / első thread handle LPVOID lpbaseofimage ; 1 DWORD dwdebuginfofileoffset ; DWORD ndebuginfosize ; LPVOID lpthreadlocalbase ; LPTHREAD_START_ROUTINE lpstartaddress ; 2 LPVOID lpimagename ; 3 WORD funicode ; / / fájlnév kódolása ; 1 A memóriába betöltött exe fájl kezdőcíme. 2 Első végrehajtandó utasítás címe. 3 Program nevének a címe a debuggolt program címterében. Izsó Tamás Debugger működése/ 33
CREATE_PROCESS_DEBUG_EVENT esemény használata Ez az esemény a nyomkövetett program betöltése után a futás előtt következik be. Itt lehet megadni a breakpoint-ot. case CREATE_PROCESS_DEBUG_EVENT: { char filename = GetFileNameFromHandle ( debug_event. u. CreateProcessInfo. h F i l e ) ; ; A betöltött végrehajtható fájl nevét nem a lpimagename alapján, hanem a hozzá tartozó fájl handler alapján a GetFileNameFromHandle függvény segítségével állítjuk elő. A függvény leírását az MSDN-en a Obtaining a File Name From a File Handle címmel találhatjuk meg. Kb 10 rendszerhívást tartalmaz, de számunkra ez most nem izgalmas. Izsó Tamás Debugger működése/ 34
OUTPUT_DEBUG_STRING_EVENT esemény Ez az esemény akkor következik be, ha a nyomkövetett programban diagnosztikai üzeneteket írunk ki a OutputDebugString rendszerhívás segítségével. Ezeket az üzeneteket a debugger kapja meg. struct OUTPUT_DEBUG_STRING_INFO { LPSTR lpdebugstringdata ; / / char* WORD funicode ; WORD ndebugstringlength ; ; Izsó Tamás Debugger működése/ 35
OUTPUT_DEBUG_STRING_EVENT esemény case OUTPUT_DEBUG_STRING_EVENT: { char msg ; 1 OUTPUT_DEBUG_STRING_INFO & DebugString = debug_event. u. DebugString ; msg=new char [ DebugString. ndebugstringlength + 1 ] ; ReadProcessMemory ( p i. hprocess, 2 DebugString. lpdebugstringdata, 3 msg, 4 DebugString. ndebugstringlength, NULL ) ; p r i n t f ( " Debug i n f o : %s \ n ", msg ) ; d e l e t e [ ] msg ; 1 Nem Unicode-ot használunk. 2 Debuggolt processz handle-t a processz indításánál kaptuk meg. 3 Üzenet címe a debuggolt program címterében. 4 Üzenet másolata a debugger címterében. Izsó Tamás Debugger működése/ 36
EXIT_PROCESS_DEBUG_INFO esemény Nyomkövetett program befejeződésénél jelentkező esemény. st ru ct EXIT_PROCESS_DEBUG_INFO { DWORD dwexitcode ; ; Használata: bool bcontinuedebugging= t r u e ;... case EXIT_PROCESS_DEBUG_EVENT: { p r i n t f ( " Process e x i t e d with code : 0x%x ", debug_event. u. ExitProcess. dwexitcode ) ; bcontinuedebugging= f a l s e ; break ; Izsó Tamás Debugger működése/ 37