Iczelion - 30 - Win32 Debug API Teil 3

Tutorial 30: Win32 Debug API Teil 3



In diesem Tutorial, fahren wir mit der Erkundung des Win32 Debug-APIs fort. Insbesondere werden wir lernen wie man ein zu debuggendes Programm 'traced'.
Laden Sie das Beispiel hier herunter.

Theorie:

Wenn Sie schon mal einen Debuger benutzt haben, werden Sie mit dem 'tracen' vertraut sein. Wenn Sie ein Programm 'tracen', stoppt das Programm nach jeder Ausführung eines Befehls, um ihnen die Chance zu geben die Werte der Register / des Speichers genauer zu untersuchen. Einzelschritt-Ausführung ist der offizielle Name des 'tracens'.
Die Einzelschritt-Ausführung wird von der CPU selbst unterstützt. Das 8te Bit des Flag-Registers heißt Trap Flag. Wenn das Flag (Bit) gesetzt ist, führt die CPU nur Schrittweise aus. Die CPU generiert nach jedem Befehl eine Debug-Exception. Nachdem die Debug-Exception erzeugt wurde, wird das Trap-Flag automatisch wieder gelöscht.
Wir können das zu debuggende Programm auch schrittweise mit Hilfe der Win32 Debug API ausführen. Die Schritte dazu sind foglende:

  1. Rufen Sie GetThreadContext auf, wobei CONTEXT_CONTROL in ContextFlags spezifiziert ist, um den Wert des Flag-Registers zu erhalten.
  2. Setzen Sie das Trap-Bit im regFlag Element der CONTEXT Struktur
  3. rufen Sie SetThreadContext auf
  4. Warten Sie wie immer auf Debug-Ereignisse. Das zu debuggende Programm wird im Einzelschritt-Modus ausgeführt. Nach jedem einzelnen Befehl erhalten wir ein EXCEPTION_DEBUG_EVENT mit EXCEPTION_SINGLE_STEP als Wert in u.Exception.pExceptionRecord.ExceptionCode
  5. Wenn Sie den nächsten Befehl auch tracen wollen, müssen Sie das Trap-Bit wieder setzen.

Beispiel:

.386 .model flat,stdcall option casemap:none include \masm32\include\windows.inc include \masm32\include\kernel32.inc include \masm32\include\comdlg32.inc include \masm32\include\user32.inc includelib \masm32\lib\kernel32.lib includelib \masm32\lib\comdlg32.lib includelib \masm32\lib\user32.lib .data AppName db "Win32 Debug Beispiel Nr.4",0 ofn OPENFILENAME FilterString db "Ausführbare Dateien",0,"*.exe",0 db "Alle Dateien",0,"*.*",0,0 ExitProc db "Das debugte Programm wird beendet",0Dh,0Ah db "Anzahl ausgeführter Befehle : %lu",0 TotalInstruction dd 0 .data? buffer db 512 dup(?) startinfo STARTUPINFO pi PROCESS_INFORMATION DBEvent DEBUG_EVENT context CONTEXT .code start: mov ofn.lStructSize,SIZEOF ofn mov ofn.lpstrFilter, OFFSET FilterString mov ofn.lpstrFile, OFFSET buffer mov ofn.nMaxFile,512 mov ofn.Flags, OFN_FILEMUSTEXIST or OFN_PATHMUSTEXIST or OFN_LONGNAMES or OFN_EXPLORER or OFN_HIDEREADONLY invoke GetOpenFileName, ADDR ofn .if eax==TRUE invoke GetStartupInfo,addr startinfo invoke CreateProcess, addr buffer, NULL, NULL, NULL, FALSE, DEBUG_PROCESS+ DEBUG_ONLY_THIS_PROCESS, NULL, NULL, addr startinfo, addr pi .while TRUE invoke WaitForDebugEvent, addr DBEvent, INFINITE .if DBEvent.dwDebugEventCode==EXIT_PROCESS_DEBUG_EVENT invoke wsprintf, addr buffer, addr ExitProc, TotalInstruction invoke MessageBox, 0, addr buffer, addr AppName, MB_OK+MB_ICONINFORMATION .break .elseif DBEvent.dwDebugEventCode==EXCEPTION_DEBUG_EVENT .if DBEvent.u.Exception.pExceptionRecord.ExceptionCode==EXCEPTION_BREAKPOINT mov context.ContextFlags, CONTEXT_CONTROL invoke GetThreadContext, pi.hThread, addr context or context.regFlag,100h invoke SetThreadContext,pi.hThread, addr context invoke ContinueDebugEvent, DBEvent.dwProcessId, DBEvent.dwThreadId, DBG_CONTINUE .continue .elseif DBEvent.u.Exception.pExceptionRecord.ExceptionCode==EXCEPTION_SINGLE_STEP inc TotalInstruction invoke GetThreadContext,pi.hThread,addr context or context.regFlag,100h invoke SetThreadContext,pi.hThread, addr context invoke ContinueDebugEvent, DBEvent.dwProcessId, DBEvent.dwThreadId,DBG_CONTINUE .continue .endif .endif invoke ContinueDebugEvent, DBEvent.dwProcessId, DBEvent.dwThreadId, DBG_EXCEPTION_NOT_HANDLED .endw .endif invoke CloseHandle,pi.hProcess invoke CloseHandle,pi.hThread invoke ExitProcess, 0 end start


Analyse:

Das Programm zeigt einen Öffnen-Dialog. Wenn der Benutzer eine ausführbare Datei auswählt, wird diese im Einzelschritt-Modus ausgeführt und die Anzahl der Befehle gezählt, die ausgeführt wurden, bis das debuggende Programm beendet wurde.

.elseif DBEvent.dwDebugEventCode==EXCEPTION_DEBUG_EVENT .if DBEvent.u.Exception.pExceptionRecord.ExceptionCode==EXCEPTION_BREAKPOINT


Wir benutzen diese Gelegenheit um das zu debugende Programm in den Einzelschritt-Modus zu versetzen. Denken Sie daran, dass Windows ein EXCEPTION_BREAKPOINT sendet, bevor es den ersten Befehl des zu debuggenden Programm ausführt.

mov context.ContextFlags, CONTEXT_CONTROL invoke GetThreadContext, pi.hThread, addr context


Wir rufen GetThreadContext auf, um die CONTEXT Struktur mit den jetzigen Werten der Register des zu debuggenden Programms zu füllen. Genauer gesagt brauchen wir den aktuellen Wert des Flag-Registers.

or context.regFlag,100h


Wir setzen das Trap Bit (8tes Bit) in der Kopie des Flag-Registers.

invoke SetThreadContext,pi.hThread, addr context invoke ContinueDebugEvent, DBEvent.dwProcessId, DBEvent.dwThreadId, DBG_CONTINUE .continue


Dann rufen wir SetThreadContext auf um die Werte in der CONTEXT Struktur mit den neuen zu überschreiben und rufen ContinueDebugEvent mit dem DBG_CONTINUE Flag auf, um das zu debuggende Programm fortzusetzen.

.elseif DBEvent.u.Exception.pExceptionRecord.ExceptionCode==EXCEPTION_SINGLE_STEP inc TotalInstruction


Wenn ein Befehl in unserem zu debuggenden Programm ausgeführt wurde, erhalten wir ein EXCEPTION_DEBUG_EVENT. Wir müssen den Wert von u.Exception.pExceptionRecord.ExceptionCode untersuchen. Ist der Wert gleich EXCEPTION_SINGLE_STEP, dann wurde dieses Debug-Ereignis wegen des Einzelschritt-Modus ausgeführt. In diesem Fall können wir die Variable TotalInstruction um eins erhöhen, da wir wissen, dass genau ein Befehl ausgeführt wurde.

invoke GetThreadContext,pi.hThread,addr context or context.regFlag,100h invoke SetThreadContext,pi.hThread, addr context invoke ContinueDebugEvent, DBEvent.dwProcessId, DBEvent.dwThreadId,DBG_CONTINUE .continue


Da das Trap-Flag nach jeder erzeugten Debug-Exception gelöscht wird, müssen wir das Trap-Flag wieder setzen, wenn wir im Einzelschritt-Modus fortfahren wollen.
Warnung: Benutzen Sie das Beispiel in diesem Tutorial nicht mit einem großen Programm: Tracen is LANGSAM. Sie müssen vielleicht zehn Minuten warten, bevor Sie das zu debuggende Programm schließen können.


Deutsche Übersetzung: Joachim Rohde
Die original Win32Asm-Tutorials stammen von Iczelion's Win32 Assembly HomePage