⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 030.txt

📁 会变语言实现的一些程序
💻 TXT
字号:
第30课: Win32调试API 第三部分


--------------------------------------------------------------------------------
在本章中,我们将继续探讨win32调试api。特别地,我们将学习如何去跟踪被调试程序.
下载 例子. 
理论:
如果你以前使用过调试器,那么你应对跟踪比较熟悉。当"跟踪"一个程序时,程序在每执行一条指令后将会停止,这使你有机会去检查寄存器/内存中的值。这种单步运行的官方定义为跟踪(tracing)。
单步运行的特色是由CPU本身提供的。标志寄存器的第8位称为陷阱标志trap flag。如果该位设置,则CPU运行于单步模式。CPU将在每条指令后产生一个debug异常。当debug 异常产生后,陷阱标志自动清除。利用win32调试api,我们也可以单步运行被调试程序。方法如下:

调用GetThreadContext, 指定 ContextFlags为CONTEXT_CONTROL, 来获得标志寄存器的值 
设置CONTEXT结构成员标志寄存器regFlag中的陷阱标志位 
调用 SetThreadContext 
等待调式事件。被调试程序将按单步模式执行,在每执行一条指令后,我们将得到调试 事件,u.Exception.pExceptionRecord.ExceptionCode值为EXCEPTION_SINGLE_STEP 
如果要跟踪下一条指令,需要再次设置陷阱标志位。 
例:
.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 
             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 

分析:
该程序先显示一个打开文件对话框,当用户选择了一个可执行文件,它将单步执行该程序,并记录执行的指令数,直到被调试程序退出运行。 

       .elseif DBEvent.dwDebugEventCode==EXCEPTION_DEBUG_EVENT           .if DBEvent.u.Exception.pExceptionRecord.ExceptionCode==EXCEPTION_BREAKPOINT 

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

             mov context.ContextFlags, CONTEXT_CONTROL 
             invoke GetThreadContext, pi.hThread, addr context 

调用GetThreadContext,以被调试程序的当前寄存器内容来填充CONTEXT 结构 特别地,我们需要标志寄存器的当前值。

             or context.regFlag,100h 

设置标志寄存器映象的陷阱位(第8位)

             invoke SetThreadContext,pi.hThread, addr context 
             invoke ContinueDebugEvent, DBEvent.dwProcessId, DBEvent.dwThreadId, DBG_CONTINUE 
             .continue 

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

          .elseif DBEvent.u.Exception.pExceptionRecord.ExceptionCode==EXCEPTION_SINGLE_STEP 
             inc TotalInstruction 

当调试程序中一条指令执行后,我们将接收到EXCEPTION_DEBUG_EVENT的调试事件, 必须要检查u.Exception.pExceptionRecord.ExceptionCode的值。如果该值为 EXCEPTION_SINGLE_STEP,那么,该调试事件是单步运行模式造成的。在这种情况 下,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 


由于陷阱标志在debug异常后自动清除了,如果我们需要继续保持单步运行模式,则必须设置陷阱标志位。
警告: 不要用本教程中的此例子来调试大程序: 跟踪是很慢的。你或许需要等待10 多分钟才能关闭被调试程序。 


--------------------------------------------------------------------------------




--------------------------------------------------------------------------------

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -