Win32调试API第三部分,第1张

Win32调试API第三部分,第2张

理论:
如果你以前使用过调试器,那么你应该熟悉跟踪。当“跟踪”一个程序时,程序会在执行完每条指令后停止,这给了你一个检查寄存器/内存中的值的机会。这种单步操作的官方定义是跟踪。
单步操作的特性是由CPU本身提供的。寄存器的第8位称为陷阱标志trap flag。如果该位置位,CPU以单步模式运行。CPU将在每条指令后生成一个调试异常。出现调试异常时,陷阱标志会自动清除。使用win32调试api,也可以一步运行调试好的程序。方法如下:
调用GetThreadCONTEXT并将ContextFlags指定为CONTEXT_CONTROL获取标志寄存器的值
在上下文结构成员的标志寄存器中设置trap标志位
并调用SetThreadContext
等待模态事件。被调试的程序将以单步方式执行。每条指令执行后,我们都会得到调试事件。u . EXCEPTION . pexception record . EXCEPTION code的值为EXCEPTION_SINGLE_STEP
。如果要跟踪下一条指令,需要再次设置陷阱标志。
例如:
.386
。modelflat,stdcall
option casemap:none
include \ masm 32 \ include \ windows . Inc
include \ masm 32 kernel 32 . Inc
include \ com DLG 32 . Inc
include \ masm 32 \ include \ user 32 . Inc
include lib \ masm 32 \ lib \ kernel 32 . lib
include lib \ masm 32 \ lib \ com DLG 32 . lib

。data
AppName db "Win32调试示例no.4 ",0
of n open filename
filter string db "可执行文件",0," *。exe ",0
db "所有文件",0," *。* ",0,0
ExitProc db "被调试程序退出",0Dh,0Ah
db "执行的总指令数:%lu ",0
总指令数dd 0

。数据?
缓冲db 512 dup(?)
startinfo startup info
pi PROCESS _ INFORMATION
db EVENT DEBUG _ EVENT
CONTEXT上下文

。code
start:
mov of n . l structsize,SIZEOF of of n
mov of n . lpstrfilter,OFFSET FilterString
mov of n . lpstrfile,OFFSET buffer
mov of n . nmax file,512
mov ofn。标志,OFN _文件必须存在或OFN _路径必须存在或OFN _长名字或OFN _资源管理器或OFN _隐藏只读
调用GetOpenFileName,ADDR ofn
。if eax = = TRUE
invoke getstartup info,addr startinfo
invoke CreateProcess,addr buffer,NULL,NULL,NULL,FALSE,DEBUG _ PROCESS+DEBUG _ ONLY _ THIS _ PROCESS,NULL,NULL,addr startinfo,addr pi
。当TRUE
调用WaitForDebugEvent,addr DBEvent,INFINITE
。if db EVENT . dwdebugeventcode = = EXIT _ PROCESS _ DEBUG _ EVENT
调用wsprintf,addr buffer,addr ExitProc,total instruction
调用MessageBox,0,addr buffer,addr AppName,MB_OK+MB_ICONINFORMATION
。break
。else if db EVENT . dwdebugeventcode = = EXCEPTION _ DEBUG _ EVENT。if db event . u . EXCEPTION . pexception record . EXCEPTION code = = EXCEPTION _ BREAKPOINT
mov context。ContextFlags,CONTEXT _ CONTROL
调用GetThreadContext,pi.hThread,addr CONTEXT
或context.regFlag,100h
调用SetThreadContext,pi.hThread,addr CONTEXT
调用ContinueDebugEvent,DBEvent.dwProcessId,DBEvent.dwThreadId,DBG_CONTINUE
。继续
。else if db event . u . EXCEPTION . pexception record . EXCEPTION code = = EXCEPTION _ SINGLE _ STEP
Inc total instruction
invoke getthread context,pi.hThread,addr context或context.regFlag,100h
invoke setthread context,pi.hThread,addr context
invoke ContinueDebugEvent,DBEvent.dwProcessId,DBEvent.dwThreadId,DBG_CONTINUE
。继续
。endif
。endif
invoke ContinueDebugEvent,DBEvent.dwProcessId,DBEvent.dwThreadId,DBG _异常_未处理
。endw
。endif
invoke CloseHandle,pi . h process
invoke close handle,pi . hthread
invoke exit process,0
end start
分析:
程序首先显示一个文件打开对话框,当用户选择一个可执行文件时,它会进入程序并记录执行的指令数,直到被调试的程序退出运行。

。else if db EVENT . dwdebugeventcode = = EXCEPTION _ DEBUG _ EVENT。if db event . u . EXCEPTION . pexception record . EXCEPTION code = = EXCEPTION _ BREAKPOINT

利用这个机会,将被调试的程序设置为单步运行模式。记住,在被调试程序的第一条指令执行之前,windows会发送一条EXCEPTION_BREAKPOINT消息。

mov上下文。ContextFlags,CONTEXT _ CONTROL
调用GetThreadContext,pi.hThread,addr上下文

调用GetThreadContext,用被调试程序的当前寄存器内容填充上下文结构。特别是,我们需要标记寄存器的当前值。

或context.regFlag,100h

设置标志寄存器映像的陷阱位(位8)。

调用SetThreadContext,pi.hThread,addr context
调用ContinueDebugEvent,DBEvent.dwProcessId,DBEvent.dwThreadId,DBG_CONTINUE
。继续

然后调用SetThreadContext来覆盖Context的值。然后用DBG_CONTINUE调用ContinueDebugEvent恢复被调试程序的运行。

。else if db event . u . EXCEPTION . pexception record . EXCEPTION code = = EXCEPTION _ SINGLE _ STEP
Inc total instruction

调试器中的一条指令执行时,我们会收到EXCEPTION_DEBUG_EVENT的调试事件,我们必须检查u . EXCEPTION . pexception record . EXCEPTION code的值,如果值是EXCEPTION_SINGLE_STEP,那么调试事件是单步运行模式引起的。在这种情况下,TotalInstruction加1,因为我们确切地知道被调试程序此时已经执行了一条指令。

调用GetThreadContext,pi.hThread,addr context或context.regFlag,100h
调用SetThreadContext,pi.hThread,addr context
调用ContinueDebugEvent,DBEvent.dwProcessId,DBEvent.dwThreadId,DBG_CONTINUE
。继续


由于trap标志在调试异常后自动清零,如果我们需要保持单步运行模式,就必须设置trap标志位。
警告:不要在本教程中使用此示例来调试大型程序:跟踪速度很慢。您可能需要等待10分钟以上才能关闭被调试的程序。

位律师回复
DABAN RP主题是一个优秀的主题,极致后台体验,无插件,集成会员系统
白度搜_经验知识百科全书 » Win32调试API第三部分

0条评论

发表评论

提供最优质的资源集合

立即查看 了解详情