Tutorial 30: API de Depuración de Win32 Parte 3

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

Teoría:

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:

  1. Llamar a GetThreadContext, especificando CONTEXT_CONTROL en ContextFlags, para obtener el valor del registro de bandera.
  2. Poner el bit de trampa en el miembro regFlag de la estructura CONTEXT
  3. Llamar a SetThreadContext
  4. Esperar por los eventos de depuración, como es usual. El depurando [debuggee] se ejecutará en modo de paso simple [single-step]. Después de que se ejecuta cada instrucción, obtendremos EXCEPTION_DEBUG_EVENT con el valor EXCEPTION_SINGLE_STEP en u.Exception.pExceptionRecord.ExceptionCode
  5. Si necesitas trazar la próxima instrucción , necesitarás poner de nuevo el bit de trampa.

Ejemplo:

.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

Análisis:

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.


Index

Next

[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

www.000webhost.com