En
este tutorial, continuaremos la exploración de la api de depuración
de win32. Específicamente, aprenderemos como trazar el depurando [debugee].
Baja el ejemplo.
Historia de revisiones:
12/2/2000: Se olvidó el alineamiento dword de la estructura CONTEXT
Si haz usado antes un depurador, estarás ya familiarizado con el trazado. Cuando "trazas" un programa, se detiene después de ejecutar cada función, dándote la oportunidad de examinar los valores de registros/memoria. Paso simple [single-stepping] es el nombre oficial del trazado.
El rasgo de paso simple [single-step] es proveído por el propio CPU. El 8vo bit de la bandera de registro es llamado la bandera bit de trampa [trap flag]. Si esta bandera (bit) está establecida, el CPU se ejecuta en modo paso simple [single-step]. El CPU generará una excepción de depuración después de cada instrucción. Después de que se genera la excepción de depuración, la bandera de trampa es limpiada automáticamente.
También podemos aplicar paso simple [single-step] a los depurandos [debuggee], usando la api de win32. Los pasos son los siguientes:
.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 Example no.4",0
ofn OPENFILENAME
<>
FilterString db "Executable Files",0,"*.exe",0
db "All Files",0,"*.*",0,0
ExitProc db "The debuggee exits",0Dh,0Ah
db "Total Instructions executed
: %lu",0
TotalInstruction dd 0
.data?
buffer db 512 dup(?)
startinfo STARTUPINFO <>
pi PROCESS_INFORMATION <>
DBEvent DEBUG_EVENT
<>
align dword
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
El programa muestra la caja de diálogo openfile. Cuando el usuario elige un archivo ejecutable, ejecuta el programa en modo de paso simple, contando el número de instrucciones ejecutadas hasta que el depurando [debugee] sale a Windows.
.elseif DBEvent.dwDebugEventCode==EXCEPTION_DEBUG_EVENT .if DBEvent.u.Exception.pExceptionRecord.ExceptionCode==EXCEPTION_BREAKPOINT
Aprovechamos esta oportunidad para poner el depurando [debuggee] en modo de paso simple [single-step mode]. Recuerda que Windows envía un EXCEPTION_BREAKPOINT justo antes de que se ejecute la primera instrucción del depurando.
mov context.ContextFlags,
CONTEXT_CONTROL
invoke GetThreadContext, pi.hThread, addr context
Llamamos a GetThreadContext para llenar la estructura CONTEXT con los valores actuales en los registros del depurando. Más específicamente, necesitamos el valor actual del registro de bandera.
or context.regFlag,100h
Ponemos el bit de trampa (8vo bit) en la imagen del registro de bandera.
invoke
SetThreadContext,pi.hThread, addr context
invoke ContinueDebugEvent, DBEvent.dwProcessId, DBEvent.dwThreadId, DBG_CONTINUE
.continue
Luego llamamos a SetThreadContext para sobreescribir los valores en la estructura CONTEXT con el (los) nuevo(s) y llamar a ContinueDebugEvent con la bandera DBG_CONTINUE para resumir el archivo en depuración [debuggee].
.elseif DBEvent.u.Exception.pExceptionRecord.ExceptionCode==EXCEPTION_SINGLE_STEP
inc TotalInstruction
Cuando se ejecuta una función en el archivo de depuración, recibimos un EXCEPTION_DEBUG_EVENT. Debemos examinar el valor de u.Exception.pExceptionRecord.ExceptionCode. Si el valor es EXCEPTION_SINGLE_STEP, entonces es generado este evento de depuración debido al modo de paso simple [single-step mode]. En este caso podemos incrementar en uno la variable TotalInstruction porque sabemos que se ha ejecutado exactamente una instrucción en el archivo bajo depuración.
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
Como la bandera de trampa es limpiada después que se genera la excepción de depuración, debemos poner la bandera de trampa de nuevo si queremos continuar en modo de paso simple [single-step mode].
Adevertencia: No uses el ejemplo en este tutorial con un programa muy grande: el trazado sería muy LENTO. Podrías tener que esperar hasta diez minutos antes de que puedas cerrar el archivo en depuración.
[Iczelion's Win32 Assembly Homepage]
n u M I T_o r's Programming Page
Este tutorial, original de Iczelion, ha sido traducido por: n u M I T_o r