tutorial 30: win32 debug api part 3
in this tutorial, we continue the exploration of win32 debug api. specifically, we will learn how to trace the debuggee.
theory:
if you have used a debugger before, you would be familiar with tracing. when you "trace" a program, the program stops after executing each instruction, giving you the chance to examine the values of registers/memory. single-stepping is the official name of tracing.
the single-step feature is provided by the cpu itself. the 8th bit of the flag register is called trap flag. if this flag(bit) is set, the cpu executes in single-step mode. the cpu will generate a debug exception after each instruction. after the debug exception is generated, the trap flag is cleared automatically.
we can also single-step the debuggee, using win32 debug api. the steps are as follows:
- call getthreadcontext, specifying context_control in contextflags, to obtain the value of the flag register.
- set the trap bit in regflag member of the context structure
- call setthreadcontext
- wait for the debug events as usual. the debuggee will execute in single-step mode. after it executes each instruction, we will get exception_debug_event with exception_single_step value in u.exception.pexceptionrecord.exceptioncode
- if you need to trace the next instruction, you need to set the trap bit again.
example:
.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 <>
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