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:
- Rufen Sie GetThreadContext auf, wobei
CONTEXT_CONTROL in
ContextFlags spezifiziert ist, um den Wert des Flag-Registers zu erhalten.
- Setzen Sie das Trap-Bit im regFlag
Element der CONTEXT Struktur
- rufen Sie SetThreadContext
auf
- 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
- 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