理論:
Win32有一些供程序員使用的API,它們提供相當(dāng)于調(diào)試器的功能. 他們被稱作Win32調(diào)試API(或原語).利用這些API,我們可以:
加載一個程序或捆綁到一個正在運(yùn)行的程序上以供調(diào)試
獲得被調(diào)試的程序的低層信息,例如進(jìn)程ID,進(jìn)入地址,映像基址等.
當(dāng)發(fā)生與調(diào)試有關(guān)的事件時被通知,例如進(jìn)程/線程的開始/結(jié)束, DLL的加載/釋放等.
修改被調(diào)試的進(jìn)程或線程
簡而言之,我們可以用這些API寫一個簡單的調(diào)試器.由于這個題目有些過大,我把它分為幾部分,而本教程就是它的第一部分.在本教程中,我將講解一些基本概念及Win32調(diào)試API的大致框架.
使用Win32調(diào)試API的步驟如下:
創(chuàng)建一個進(jìn)程或捆綁到一個運(yùn)行中的進(jìn)程上. 這是使用Win32調(diào)試API的第一步.由于我們的程序要扮演調(diào)試器的角色,我們要找一個供調(diào)試的程序.一個被調(diào)試的程序被稱為debuggee.可以通過以下兩種方式獲得debuggee:
通過CreateProcess創(chuàng)建debuggee進(jìn)程.為了創(chuàng)建被調(diào)試的進(jìn)程,必須指定DEBUG_PROCESS標(biāo)志.這一標(biāo)志告訴Windows我們要調(diào)試該進(jìn)程. 當(dāng)debuggee中發(fā)生重要的與調(diào)試有關(guān)的事件(調(diào)試事件)時,Windows 會向我們的程序發(fā)送通知.debuggee會立即掛起以等待我們的程序準(zhǔn)備好.如果debuggee還創(chuàng)建了子進(jìn)程,Windows還會為每個子進(jìn)程中的調(diào)試事件向我們的程序發(fā)送通知.這一特性通常是不必要的.我們可以通過指定DEBUG_ONLY_THIS_PROCESS與 DEBUG_PROCESS的組合標(biāo)志來禁止它.
我們也可以用 DebugActiveProcess標(biāo)志捆綁到一個運(yùn)行中的進(jìn)程上.
等待調(diào)試事件. 在獲得了一個debuggee進(jìn)程后,debuggee的主線程被掛起,這種狀況將持續(xù)到我們的程序調(diào)用WaitForDebugEvent為止.這個函數(shù)和其他的WaitForXXX函數(shù)相似,比如說,它阻塞調(diào)用線程直到等待的事件發(fā)生.對這個函數(shù)來說, 它等待由Windows發(fā)送的調(diào)試事件.下面是它的定義:
WaitForDebugEvent proto lpDebugEvent:DWORD, dwMilliseconds:DWORD
lpDebugEvent is the address of a DEBUG_EVENT這個結(jié)構(gòu)將被填入關(guān)于debuggee中發(fā)生的調(diào)試事件的信息.
dwMilliseconds 該函數(shù)等待調(diào)試事件的時間,以毫秒為單位.如果這段時間沒有調(diào)試事件發(fā)生, WaitForDebugEvent返回調(diào)用者.另一方面,如果將該參數(shù)指定為 INFINITE 常數(shù),函數(shù)將一直等待直到調(diào)試事件發(fā)生.
現(xiàn)在我們看一下DEBUG_EVENT 結(jié)構(gòu).
DEBUG_EVENT STRUCT
dwDebugEventCode dd ?
dwProcessId dd ?
dwThreadId dd ?
u DEBUGSTRUCT <>
DEBUG_EVENT ENDS
dwDebugEventCode 該值指定了等待發(fā)生的調(diào)試事件的類型.因?yàn)橛泻芏喾N類型的事件發(fā)生,我們的程序要檢查該值,知道要發(fā)生事件的類型并做出響應(yīng). 該值可能的取值如下:
取值 含義
CREATE_PROCESS_DEBUG_EVENT 進(jìn)程被創(chuàng)建.當(dāng)debuggee進(jìn)程剛被創(chuàng)建(還未運(yùn)行) 或我們的程序剛以DebugActiveProcess被捆綁到一個運(yùn)行中的進(jìn)程時事件發(fā)生. 這是我們的程序應(yīng)該獲得的第一個事件.
EXIT_PROCESS_DEBUG_EVENT 進(jìn)程退出.
CREATE_THEAD_DEBUG_EVENT 當(dāng)一個新線程在deuggee進(jìn)程中創(chuàng)建或我們的程序首次捆綁到運(yùn)行中的進(jìn)程時事件發(fā)生.要注意的是當(dāng)debugge的主線程被創(chuàng)建時不會收到該通知.
EXIT_THREAD_DEBUG_EVENT debuggee中的線程退出時事件發(fā)生.debugee的主線程退出時不會收到該通知.我們可以認(rèn)為debuggee的主線程與debugge進(jìn)程是同義詞. 因此, 當(dāng)我們的程序看到CREATE_PROCESS_DEBUG_EVENT標(biāo)志時,對主線程來說,就是CREATE_THREAD_DEBUG_EVENT標(biāo)志.
LOAD_DLL_DEBUG_EVENT debuggee裝入一個DLL.當(dāng)PE裝載器第一次分解指向DLL的鏈接時,我們將收到這一事件. (當(dāng)調(diào)用CreateProcess裝入 debuggee時)并且當(dāng)debuggee調(diào)用LoadLibrary時也會發(fā)生.
UNLOAD_DLL_DEBUG_EVENT 一個DLL從debuggee中卸載時事件發(fā)生.
EXCEPTION_DEBUG_EVENT 在debuggee中發(fā)生異常時事件發(fā)生. 注意: 該事件僅在debuggee開始它的第一條指令之前發(fā)生一次.異常實(shí)際上是一個調(diào)試中斷(int 3h).如果想恢復(fù)debuggee事,以 DBG_CONTINUE 標(biāo)志調(diào)用ContinueDebugEvent 函數(shù). 不要使用DBG_EXCEPTION_NOT_HANDLED 標(biāo)志否則debuggee會在NT下拒絕運(yùn)行(Win98下運(yùn)行得很好).
OUTPUT_DEBUG_STRING_EVENT 當(dāng)debuggee調(diào)用DebugOutputString函數(shù)向我們的程序發(fā)送消息字符串時該事件發(fā)生.
RIP_EVENT 系統(tǒng)調(diào)試發(fā)生錯誤
dwProcessId 和dwThreadId發(fā)生調(diào)試事件的進(jìn)程和線程Id.我們可以用這些值作為我們感興趣的進(jìn)程或線程的標(biāo)志符.記住如果我們使用CreateProcess來裝載debuggee,我們?nèi)钥稍赑ROCESS_INFO結(jié)構(gòu)中獲得debuggee的進(jìn)程和線程.我們可以用這些值來區(qū)別調(diào)試事件是發(fā)生在debuggee中還是它的子進(jìn)程中(當(dāng)沒有指定 DEBUG_ONLY_THIS_PROCESS 標(biāo)志時).
u 是一個聯(lián)合,包含了調(diào)試事件的更多信息.根據(jù)上面dwDebugEventCode的不同,它可以是以下結(jié)構(gòu):
dwDebugEventCode u的解釋
CREATE_PROCESS_DEBUG_EVENT 名為CreateProcessInfo的CREATE_PROCESS_DEBUG_INFO結(jié)構(gòu)
EXIT_PROCESS_DEBUG_EVENT 名為ExitProcess的EXIT_PROCESS_DEBUG_INFO結(jié)構(gòu)
CREATE_THREAD_DEBUG_EVENT 名為CreateThread的CREATE_THREAD_DEBUG_INFO結(jié)構(gòu)
EXIT_THREAD_DEBUG_EVENT 名為ExitThread的EXIT_THREAD_DEBUG_EVENT 結(jié)構(gòu)
LOAD_DLL_DEBUG_EVENT 名為LoadDll的LOAD_DLL_DEBUG_INFO 結(jié)構(gòu)
UNLOAD_DLL_DEBUG_EVENT 名為UnloadDll的UNLOAD_DLL_DEBUG_INFO結(jié)構(gòu)
EXCEPTION_DEBUG_EVENT 名為Exception的EXCEPTION_DEBUG_INFO結(jié)構(gòu)
OUTPUT_DEBUG_STRING_EVENT 名為DebugString的OUTPUT_DEBUG_STRING_INFO 結(jié)構(gòu)
RIP_EVENT 名為RipInfo的RIP_INFO 結(jié)構(gòu)
我不會在這一個教程里講所有這些結(jié)構(gòu)的細(xì)節(jié),這里只詳細(xì)講一下CREATE_PROCESS_DEBUG_INFO 結(jié)構(gòu).
假設(shè)我們的程序調(diào)用了WaitForDebugEvent函數(shù)并返回,我們要做的第一件事就是檢查dwDebugEventCode中的值來看debuggee進(jìn)程中發(fā)生了那種類型的調(diào)試事件.比如說,如果dwDebugEventCode的值為 CREATE_PROCESS_DEBUG_EVENT,就可認(rèn)為u的成員為CreateProcessInfo 并用u.CreateProcessInfo來訪問.
在我們的程序中做對調(diào)試事件的響應(yīng). 當(dāng)WaitForDebugEvent 返回時,這意味著在debuggee進(jìn)程中發(fā)生了調(diào)試事件或者發(fā)生了超時.所以我們的程序要檢查dwDebugEventCode 來作出適當(dāng)?shù)姆磻?yīng).這里有些象處理Windows消息:由用戶來選擇和忽略消息.
繼續(xù)運(yùn)行debuggee. 當(dāng)調(diào)試事件發(fā)生時, Windows掛起了debuggee,所以當(dāng)我們處理完調(diào)試事件,還要讓debuggee繼續(xù)運(yùn)行.調(diào)用ContinueDebugEvent 函數(shù)來完成這一過程.
ContinueDebugEvent proto dwProcessId:DWORD, dwThreadId:DWORD, dwContinueStatus:DWORD
該函數(shù)恢復(fù)由于調(diào)試事件而掛起的線程.
dwProcessId和dwThreadId是要恢復(fù)的線程的進(jìn)程ID和線程ID,通常這兩個值從 DEBUG_EVENT結(jié)構(gòu)的dwProcessId 和dwThreadId成員獲得.
dwContinueStatus顯示了如何繼續(xù)報(bào)告調(diào)試事件的線程.可能的取值有兩個: DBG_CONTINUE 和DBG_EXCEPTION_NOT_HANDLED. 對大多數(shù)調(diào)試事件,這兩個值都一樣:恢復(fù)線程.的例外是EXCEPTION_DEBUG_EVENT,如果線程報(bào)告發(fā)生了一個異常調(diào)試事件,這意味著在debuggee的線程中發(fā)生了一個異常.如果指定了DBG_CONTINUE,線程將忽略它自己的異常處理部分并繼續(xù)執(zhí)行.在這種情況下,我們的程序必須在以DBG_CONTINUE恢復(fù)線程之前檢查并處理異常,否則異常將生生不息地不斷發(fā)生....如果我們指定了 DBG_EXCEPTION_NOT_HANDLED值,就是告訴Windows我們的程序并不處理異常:Windows將使用debuggee的默認(rèn)異常處理函數(shù)來處理異常.
總而言之,如果我們的程序沒有考慮異常,而調(diào)試事件又指向debuggee進(jìn)程中的一個異常的話,就應(yīng)調(diào)用含DBG_CONTINUE標(biāo)志的ContinueDebugEvent函數(shù).否則,我們的程序就必須以DBG_EXCEPTION_NOT_HANDLED調(diào)用 ContinueDebugEvent.但在下面這種情況下必須使用DBG_CONTINUE標(biāo)志:第一個在ExceptionCode成員中有值EXCEPTION_BREAKPOINT的 EXCEPTION_DEBUG_EVENT事件.當(dāng)debuggee開始執(zhí)行它的第一條指令時,我們的函數(shù)將接受到異常調(diào)試事件.它事實(shí)上是一個調(diào)試中斷(int 3h).如果我們以DBG_EXCEPTION_NOT_HANDLED調(diào)用ContinueDebugEvent 來響應(yīng)調(diào)試事件, Windows NT會拒絕執(zhí)行debuggee(因?yàn)樗鼪]有異常處理).所以在這種情況下,要用DBG_CONTINUE標(biāo)志告訴Windows我們希望該線程繼續(xù)執(zhí)行.
繼續(xù)上面的步驟循環(huán)直到debuggee進(jìn)程退出. 我們的程序必須在一個很象消息循環(huán)的無限循環(huán)中直到debuggee結(jié)束.該循環(huán)大體如下:
.while TRUE
invoke WaitForDebugEvent, addr DebugEvent, INFINITE
.break .if DebugEvent.dwDebugEventCode==EXIT_PROCESS_DEBUG_EVENT
<調(diào)試事件處理>
invoke ContinueDebugEvent, DebugEvent.dwProcessId, DebugEvent.dwThreadId, DBG_EXCEPTION_NOT_HANDLED
.endw
就是說,當(dāng)開始調(diào)試程序時,我們的程序不能和debuggee分開直到它結(jié)束.
我們再來總結(jié)一下這些步驟:
創(chuàng)建一個進(jìn)程或捆綁我們的程序到運(yùn)行中的進(jìn)程上.
等待調(diào)試事件
響應(yīng)調(diào)試事件.
繼續(xù)執(zhí)行debuggee.
繼續(xù)這一無盡循環(huán)直到debuggee進(jìn)程結(jié)束
例子:
這個例子調(diào)試一個win32程序并顯示諸如進(jìn)程句柄,進(jìn)程Id,映象基址等.
.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.1",0
ofn OPENFILENAME <>
FilterString db "Executable Files",0,"*.exe",0
db "All Files",0,"*.*",0,0
ExitProc db "The debuggee exits",0
NewThread db "A new thread is created",0
EndThread db "A thread is destroyed",0
ProcessInfo db "File Handle: %lx ",0dh,0Ah
db "Process Handle: %lx",0Dh,0Ah
db "Thread Handle: %lx",0Dh,0Ah
db "Image Base: %lx",0Dh,0Ah
db "Start Address: %lx",0
.data?
buffer db 512 dup(?)
startinfo STARTUPINFO <>
pi PROCESS_INFORMATION <>
DBEvent DEBUG_EVENT <>
.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 MessageBox, 0, addr ExitProc, addr AppName, MB_OK+MB_ICONINFORMATION
.break
.elseif DBEvent.dwDebugEventCode==CREATE_PROCESS_DEBUG_EVENT
invoke wsprintf, addr buffer, addr ProcessInfo, DBEvent.u.CreateProcessInfo.hFile, DBEvent.u.CreateProcessInfo.hProcess, DBEvent.u.CreateProcessInfo.hThread, DBEvent.u.CreateProcessInfo.lpBaseOfImage, DBEvent.u.CreateProcessInfo.lpStartAddress
invoke MessageBox,0, addr buffer, addr AppName, MB_OK+MB_ICONINFORMATION
.elseif DBEvent.dwDebugEventCode==EXCEPTION_DEBUG_EVENT
.if DBEvent.u.Exception.pExceptionRecord.ExceptionCode==EXCEPTION_BREAKPOINT
invoke ContinueDebugEvent, DBEvent.dwProcessId, DBEvent.dwThreadId, DBG_CONTINUE
.continue
.endif
.elseif DBEvent.dwDebugEventCode==CREATE_THREAD_DEBUG_EVENT
invoke MessageBox,0, addr NewThread, addr AppName, MB_OK+MB_ICONINFORMATION
.elseif DBEvent.dwDebugEventCode==EXIT_THREAD_DEBUG_EVENT
invoke MessageBox,0, addr EndThread, addr AppName, MB_OK+MB_ICONINFORMATION
.endif
invoke ContinueDebugEvent, DBEvent.dwProcessId, DBEvent.dwThreadId, DBG_EXCEPTION_NOT_HANDLED
.endw
invoke CloseHandle,pi.hProcess
invoke CloseHandle,pi.hThread
.endif
invoke ExitProcess, 0
end start
Win32有一些供程序員使用的API,它們提供相當(dāng)于調(diào)試器的功能. 他們被稱作Win32調(diào)試API(或原語).利用這些API,我們可以:
加載一個程序或捆綁到一個正在運(yùn)行的程序上以供調(diào)試
獲得被調(diào)試的程序的低層信息,例如進(jìn)程ID,進(jìn)入地址,映像基址等.
當(dāng)發(fā)生與調(diào)試有關(guān)的事件時被通知,例如進(jìn)程/線程的開始/結(jié)束, DLL的加載/釋放等.
修改被調(diào)試的進(jìn)程或線程
簡而言之,我們可以用這些API寫一個簡單的調(diào)試器.由于這個題目有些過大,我把它分為幾部分,而本教程就是它的第一部分.在本教程中,我將講解一些基本概念及Win32調(diào)試API的大致框架.
使用Win32調(diào)試API的步驟如下:
創(chuàng)建一個進(jìn)程或捆綁到一個運(yùn)行中的進(jìn)程上. 這是使用Win32調(diào)試API的第一步.由于我們的程序要扮演調(diào)試器的角色,我們要找一個供調(diào)試的程序.一個被調(diào)試的程序被稱為debuggee.可以通過以下兩種方式獲得debuggee:
通過CreateProcess創(chuàng)建debuggee進(jìn)程.為了創(chuàng)建被調(diào)試的進(jìn)程,必須指定DEBUG_PROCESS標(biāo)志.這一標(biāo)志告訴Windows我們要調(diào)試該進(jìn)程. 當(dāng)debuggee中發(fā)生重要的與調(diào)試有關(guān)的事件(調(diào)試事件)時,Windows 會向我們的程序發(fā)送通知.debuggee會立即掛起以等待我們的程序準(zhǔn)備好.如果debuggee還創(chuàng)建了子進(jìn)程,Windows還會為每個子進(jìn)程中的調(diào)試事件向我們的程序發(fā)送通知.這一特性通常是不必要的.我們可以通過指定DEBUG_ONLY_THIS_PROCESS與 DEBUG_PROCESS的組合標(biāo)志來禁止它.
我們也可以用 DebugActiveProcess標(biāo)志捆綁到一個運(yùn)行中的進(jìn)程上.
等待調(diào)試事件. 在獲得了一個debuggee進(jìn)程后,debuggee的主線程被掛起,這種狀況將持續(xù)到我們的程序調(diào)用WaitForDebugEvent為止.這個函數(shù)和其他的WaitForXXX函數(shù)相似,比如說,它阻塞調(diào)用線程直到等待的事件發(fā)生.對這個函數(shù)來說, 它等待由Windows發(fā)送的調(diào)試事件.下面是它的定義:
WaitForDebugEvent proto lpDebugEvent:DWORD, dwMilliseconds:DWORD
lpDebugEvent is the address of a DEBUG_EVENT這個結(jié)構(gòu)將被填入關(guān)于debuggee中發(fā)生的調(diào)試事件的信息.
dwMilliseconds 該函數(shù)等待調(diào)試事件的時間,以毫秒為單位.如果這段時間沒有調(diào)試事件發(fā)生, WaitForDebugEvent返回調(diào)用者.另一方面,如果將該參數(shù)指定為 INFINITE 常數(shù),函數(shù)將一直等待直到調(diào)試事件發(fā)生.
現(xiàn)在我們看一下DEBUG_EVENT 結(jié)構(gòu).
DEBUG_EVENT STRUCT
dwDebugEventCode dd ?
dwProcessId dd ?
dwThreadId dd ?
u DEBUGSTRUCT <>
DEBUG_EVENT ENDS
dwDebugEventCode 該值指定了等待發(fā)生的調(diào)試事件的類型.因?yàn)橛泻芏喾N類型的事件發(fā)生,我們的程序要檢查該值,知道要發(fā)生事件的類型并做出響應(yīng). 該值可能的取值如下:
取值 含義
CREATE_PROCESS_DEBUG_EVENT 進(jìn)程被創(chuàng)建.當(dāng)debuggee進(jìn)程剛被創(chuàng)建(還未運(yùn)行) 或我們的程序剛以DebugActiveProcess被捆綁到一個運(yùn)行中的進(jìn)程時事件發(fā)生. 這是我們的程序應(yīng)該獲得的第一個事件.
EXIT_PROCESS_DEBUG_EVENT 進(jìn)程退出.
CREATE_THEAD_DEBUG_EVENT 當(dāng)一個新線程在deuggee進(jìn)程中創(chuàng)建或我們的程序首次捆綁到運(yùn)行中的進(jìn)程時事件發(fā)生.要注意的是當(dāng)debugge的主線程被創(chuàng)建時不會收到該通知.
EXIT_THREAD_DEBUG_EVENT debuggee中的線程退出時事件發(fā)生.debugee的主線程退出時不會收到該通知.我們可以認(rèn)為debuggee的主線程與debugge進(jìn)程是同義詞. 因此, 當(dāng)我們的程序看到CREATE_PROCESS_DEBUG_EVENT標(biāo)志時,對主線程來說,就是CREATE_THREAD_DEBUG_EVENT標(biāo)志.
LOAD_DLL_DEBUG_EVENT debuggee裝入一個DLL.當(dāng)PE裝載器第一次分解指向DLL的鏈接時,我們將收到這一事件. (當(dāng)調(diào)用CreateProcess裝入 debuggee時)并且當(dāng)debuggee調(diào)用LoadLibrary時也會發(fā)生.
UNLOAD_DLL_DEBUG_EVENT 一個DLL從debuggee中卸載時事件發(fā)生.
EXCEPTION_DEBUG_EVENT 在debuggee中發(fā)生異常時事件發(fā)生. 注意: 該事件僅在debuggee開始它的第一條指令之前發(fā)生一次.異常實(shí)際上是一個調(diào)試中斷(int 3h).如果想恢復(fù)debuggee事,以 DBG_CONTINUE 標(biāo)志調(diào)用ContinueDebugEvent 函數(shù). 不要使用DBG_EXCEPTION_NOT_HANDLED 標(biāo)志否則debuggee會在NT下拒絕運(yùn)行(Win98下運(yùn)行得很好).
OUTPUT_DEBUG_STRING_EVENT 當(dāng)debuggee調(diào)用DebugOutputString函數(shù)向我們的程序發(fā)送消息字符串時該事件發(fā)生.
RIP_EVENT 系統(tǒng)調(diào)試發(fā)生錯誤
dwProcessId 和dwThreadId發(fā)生調(diào)試事件的進(jìn)程和線程Id.我們可以用這些值作為我們感興趣的進(jìn)程或線程的標(biāo)志符.記住如果我們使用CreateProcess來裝載debuggee,我們?nèi)钥稍赑ROCESS_INFO結(jié)構(gòu)中獲得debuggee的進(jìn)程和線程.我們可以用這些值來區(qū)別調(diào)試事件是發(fā)生在debuggee中還是它的子進(jìn)程中(當(dāng)沒有指定 DEBUG_ONLY_THIS_PROCESS 標(biāo)志時).
u 是一個聯(lián)合,包含了調(diào)試事件的更多信息.根據(jù)上面dwDebugEventCode的不同,它可以是以下結(jié)構(gòu):
dwDebugEventCode u的解釋
CREATE_PROCESS_DEBUG_EVENT 名為CreateProcessInfo的CREATE_PROCESS_DEBUG_INFO結(jié)構(gòu)
EXIT_PROCESS_DEBUG_EVENT 名為ExitProcess的EXIT_PROCESS_DEBUG_INFO結(jié)構(gòu)
CREATE_THREAD_DEBUG_EVENT 名為CreateThread的CREATE_THREAD_DEBUG_INFO結(jié)構(gòu)
EXIT_THREAD_DEBUG_EVENT 名為ExitThread的EXIT_THREAD_DEBUG_EVENT 結(jié)構(gòu)
LOAD_DLL_DEBUG_EVENT 名為LoadDll的LOAD_DLL_DEBUG_INFO 結(jié)構(gòu)
UNLOAD_DLL_DEBUG_EVENT 名為UnloadDll的UNLOAD_DLL_DEBUG_INFO結(jié)構(gòu)
EXCEPTION_DEBUG_EVENT 名為Exception的EXCEPTION_DEBUG_INFO結(jié)構(gòu)
OUTPUT_DEBUG_STRING_EVENT 名為DebugString的OUTPUT_DEBUG_STRING_INFO 結(jié)構(gòu)
RIP_EVENT 名為RipInfo的RIP_INFO 結(jié)構(gòu)
我不會在這一個教程里講所有這些結(jié)構(gòu)的細(xì)節(jié),這里只詳細(xì)講一下CREATE_PROCESS_DEBUG_INFO 結(jié)構(gòu).
假設(shè)我們的程序調(diào)用了WaitForDebugEvent函數(shù)并返回,我們要做的第一件事就是檢查dwDebugEventCode中的值來看debuggee進(jìn)程中發(fā)生了那種類型的調(diào)試事件.比如說,如果dwDebugEventCode的值為 CREATE_PROCESS_DEBUG_EVENT,就可認(rèn)為u的成員為CreateProcessInfo 并用u.CreateProcessInfo來訪問.
在我們的程序中做對調(diào)試事件的響應(yīng). 當(dāng)WaitForDebugEvent 返回時,這意味著在debuggee進(jìn)程中發(fā)生了調(diào)試事件或者發(fā)生了超時.所以我們的程序要檢查dwDebugEventCode 來作出適當(dāng)?shù)姆磻?yīng).這里有些象處理Windows消息:由用戶來選擇和忽略消息.
繼續(xù)運(yùn)行debuggee. 當(dāng)調(diào)試事件發(fā)生時, Windows掛起了debuggee,所以當(dāng)我們處理完調(diào)試事件,還要讓debuggee繼續(xù)運(yùn)行.調(diào)用ContinueDebugEvent 函數(shù)來完成這一過程.
ContinueDebugEvent proto dwProcessId:DWORD, dwThreadId:DWORD, dwContinueStatus:DWORD
該函數(shù)恢復(fù)由于調(diào)試事件而掛起的線程.
dwProcessId和dwThreadId是要恢復(fù)的線程的進(jìn)程ID和線程ID,通常這兩個值從 DEBUG_EVENT結(jié)構(gòu)的dwProcessId 和dwThreadId成員獲得.
dwContinueStatus顯示了如何繼續(xù)報(bào)告調(diào)試事件的線程.可能的取值有兩個: DBG_CONTINUE 和DBG_EXCEPTION_NOT_HANDLED. 對大多數(shù)調(diào)試事件,這兩個值都一樣:恢復(fù)線程.的例外是EXCEPTION_DEBUG_EVENT,如果線程報(bào)告發(fā)生了一個異常調(diào)試事件,這意味著在debuggee的線程中發(fā)生了一個異常.如果指定了DBG_CONTINUE,線程將忽略它自己的異常處理部分并繼續(xù)執(zhí)行.在這種情況下,我們的程序必須在以DBG_CONTINUE恢復(fù)線程之前檢查并處理異常,否則異常將生生不息地不斷發(fā)生....如果我們指定了 DBG_EXCEPTION_NOT_HANDLED值,就是告訴Windows我們的程序并不處理異常:Windows將使用debuggee的默認(rèn)異常處理函數(shù)來處理異常.
總而言之,如果我們的程序沒有考慮異常,而調(diào)試事件又指向debuggee進(jìn)程中的一個異常的話,就應(yīng)調(diào)用含DBG_CONTINUE標(biāo)志的ContinueDebugEvent函數(shù).否則,我們的程序就必須以DBG_EXCEPTION_NOT_HANDLED調(diào)用 ContinueDebugEvent.但在下面這種情況下必須使用DBG_CONTINUE標(biāo)志:第一個在ExceptionCode成員中有值EXCEPTION_BREAKPOINT的 EXCEPTION_DEBUG_EVENT事件.當(dāng)debuggee開始執(zhí)行它的第一條指令時,我們的函數(shù)將接受到異常調(diào)試事件.它事實(shí)上是一個調(diào)試中斷(int 3h).如果我們以DBG_EXCEPTION_NOT_HANDLED調(diào)用ContinueDebugEvent 來響應(yīng)調(diào)試事件, Windows NT會拒絕執(zhí)行debuggee(因?yàn)樗鼪]有異常處理).所以在這種情況下,要用DBG_CONTINUE標(biāo)志告訴Windows我們希望該線程繼續(xù)執(zhí)行.
繼續(xù)上面的步驟循環(huán)直到debuggee進(jìn)程退出. 我們的程序必須在一個很象消息循環(huán)的無限循環(huán)中直到debuggee結(jié)束.該循環(huán)大體如下:
.while TRUE
invoke WaitForDebugEvent, addr DebugEvent, INFINITE
.break .if DebugEvent.dwDebugEventCode==EXIT_PROCESS_DEBUG_EVENT
<調(diào)試事件處理>
invoke ContinueDebugEvent, DebugEvent.dwProcessId, DebugEvent.dwThreadId, DBG_EXCEPTION_NOT_HANDLED
.endw
就是說,當(dāng)開始調(diào)試程序時,我們的程序不能和debuggee分開直到它結(jié)束.
我們再來總結(jié)一下這些步驟:
創(chuàng)建一個進(jìn)程或捆綁我們的程序到運(yùn)行中的進(jìn)程上.
等待調(diào)試事件
響應(yīng)調(diào)試事件.
繼續(xù)執(zhí)行debuggee.
繼續(xù)這一無盡循環(huán)直到debuggee進(jìn)程結(jié)束
例子:
這個例子調(diào)試一個win32程序并顯示諸如進(jìn)程句柄,進(jìn)程Id,映象基址等.
.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.1",0
ofn OPENFILENAME <>
FilterString db "Executable Files",0,"*.exe",0
db "All Files",0,"*.*",0,0
ExitProc db "The debuggee exits",0
NewThread db "A new thread is created",0
EndThread db "A thread is destroyed",0
ProcessInfo db "File Handle: %lx ",0dh,0Ah
db "Process Handle: %lx",0Dh,0Ah
db "Thread Handle: %lx",0Dh,0Ah
db "Image Base: %lx",0Dh,0Ah
db "Start Address: %lx",0
.data?
buffer db 512 dup(?)
startinfo STARTUPINFO <>
pi PROCESS_INFORMATION <>
DBEvent DEBUG_EVENT <>
.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 MessageBox, 0, addr ExitProc, addr AppName, MB_OK+MB_ICONINFORMATION
.break
.elseif DBEvent.dwDebugEventCode==CREATE_PROCESS_DEBUG_EVENT
invoke wsprintf, addr buffer, addr ProcessInfo, DBEvent.u.CreateProcessInfo.hFile, DBEvent.u.CreateProcessInfo.hProcess, DBEvent.u.CreateProcessInfo.hThread, DBEvent.u.CreateProcessInfo.lpBaseOfImage, DBEvent.u.CreateProcessInfo.lpStartAddress
invoke MessageBox,0, addr buffer, addr AppName, MB_OK+MB_ICONINFORMATION
.elseif DBEvent.dwDebugEventCode==EXCEPTION_DEBUG_EVENT
.if DBEvent.u.Exception.pExceptionRecord.ExceptionCode==EXCEPTION_BREAKPOINT
invoke ContinueDebugEvent, DBEvent.dwProcessId, DBEvent.dwThreadId, DBG_CONTINUE
.continue
.endif
.elseif DBEvent.dwDebugEventCode==CREATE_THREAD_DEBUG_EVENT
invoke MessageBox,0, addr NewThread, addr AppName, MB_OK+MB_ICONINFORMATION
.elseif DBEvent.dwDebugEventCode==EXIT_THREAD_DEBUG_EVENT
invoke MessageBox,0, addr EndThread, addr AppName, MB_OK+MB_ICONINFORMATION
.endif
invoke ContinueDebugEvent, DBEvent.dwProcessId, DBEvent.dwThreadId, DBG_EXCEPTION_NOT_HANDLED
.endw
invoke CloseHandle,pi.hProcess
invoke CloseHandle,pi.hThread
.endif
invoke ExitProcess, 0
end start