?? lion-tut-c28.htm
字號:
<html>
<head>
<link rel="stylesheet" href="../../asm.css">
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>Iczelion 的 Win32asm 教程</title>
</head>
<body bgcolor="#FFFFFF" background="../../images/back01.jpg">
<div align="center"><font size="+1" color="#0000FF">第二十八課: Win32調(diào)試API 第一部分</font>
<hr size=1>
</div>
<p>在本教程中,我們將學(xué)習(xí)Win32提供給開發(fā)者的用于調(diào)試的原語. 在教程的結(jié)尾,我們將學(xué)習(xí)如何調(diào)試一個進(jìn)程. <br>
下載 <b><a href="files/tut28.zip" style="text-decoration:none">例子程序</a></b>.</p>
<h3>理論:</h3>
<p>Win32有一些供程序員使用的API,它們提供相當(dāng)于調(diào)試器的功能. 他們被稱作Win32調(diào)試API(或原語).利用這些API,我們可以:</p>
<ul>
<li>加載一個程序或捆綁到一個正在運(yùn)行的程序上以供調(diào)試</li>
<li>獲得被調(diào)試的程序的低層信息,例如進(jìn)程ID,進(jìn)入地址,映像基址等.</li>
<li>當(dāng)發(fā)生與調(diào)試有關(guān)的事件時被通知,例如進(jìn)程/線程的開始/結(jié)束, DLL的加載/釋放等.</li>
<li>修改被調(diào)試的進(jìn)程或線程</li>
</ul>
<p>簡而言之,我們可以用這些API寫一個簡單的調(diào)試器.由于這個題目有些過大,我把它分為幾部分,而本教程就是它的第一部分.在本教程中,我將講解一些基本概念及Win32調(diào)試API的大致框架.<br>
使用Win32調(diào)試API的步驟如下:</p>
<ol>
<li><b>創(chuàng)建一個進(jìn)程或捆綁到一個運(yùn)行中的進(jìn)程上</b>. 這是使用Win32調(diào)試API的第一步.由于我們的程序要扮演調(diào)試器的角色,我們要找一個供調(diào)試的程序.一個被調(diào)試的程序被稱為debuggee.可以通過以下兩種方式獲得debuggee:
<ul>
<li>通過<b>CreateProcess</b>創(chuàng)建debuggee進(jìn)程.為了創(chuàng)建被調(diào)試的進(jìn)程,必須指定<b>DEBUG_PROCESS</b>標(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ā)送通知.這一特性通常是不必要的.我們可以通過指定<b>DEBUG_ONLY_THIS_PROCESS</b>與
<b>DEBUG_PROCESS</b>的組合標(biāo)志來禁止它. </li>
<li>我們也可以用 <b>DebugActiveProcess</b>標(biāo)志捆綁到一個運(yùn)行中的進(jìn)程上.</li>
</ul>
</li>
<li><b>等待調(diào)試事件</b>. 在獲得了一個debuggee進(jìn)程后,debuggee的主線程被掛起,這種狀況將持續(xù)到我們的程序調(diào)用<b>WaitForDebugEvent</b>為止.這個函數(shù)和其他的WaitForXXX函數(shù)相似,比如說,它阻塞調(diào)用線程直到等待的事件發(fā)生.對這個函數(shù)來說,
它等待由Windows發(fā)送的調(diào)試事件.下面是它的定義:
<p><b>WaitForDebugEvent proto lpDebugEvent:DWORD, dwMilliseconds:DWORD</b></p>
<p><b>lpDebugEvent</b> is the address of a <b>DEBUG_EVENT</b>這個結(jié)構(gòu)將被填入關(guān)于debuggee中發(fā)生的調(diào)試事件的信息.</p>
<p><b>dwMilliseconds</b> 該函數(shù)等待調(diào)試事件的時間,以毫秒為單位.如果這段時間沒有調(diào)試事件發(fā)生,<b> WaitForDebugEvent</b>返回調(diào)用者.另一方面,如果將該參數(shù)指定為<b>
INFINITE </b>常數(shù),函數(shù)將一直等待直到調(diào)試事件發(fā)生.</p>
<p>現(xiàn)在我們看一下DEBUG_EVENT 結(jié)構(gòu).</p>
<p><b>DEBUG_EVENT STRUCT <br>
dwDebugEventCode dd ? <br>
dwProcessId dd ? <br>
dwThreadId dd ? <br>
u DEBUGSTRUCT <> <br>
DEBUG_EVENT ENDS </b></p>
<p><b>dwDebugEventCode</b> 該值指定了等待發(fā)生的調(diào)試事件的類型.因為有很多種類型的事件發(fā)生,我們的程序要檢查該值,知道要發(fā)生事件的類型并做出響應(yīng).
該值可能的取值如下:</p>
</li>
<table border="1" cellspacing="2" cellpadding="2" align="center">
<tr bgcolor="#009999">
<th><b>取值</b></th>
<th>含義</th>
</tr>
<tr>
<td><b>CREATE_PROCESS_DEBUG_EVENT</b></td>
<td>進(jìn)程被創(chuàng)建.當(dāng)debuggee進(jìn)程剛被創(chuàng)建(還未運(yùn)行) 或我們的程序剛以<b>DebugActiveProcess</b>被捆綁到一個運(yùn)行中的進(jìn)程時事件發(fā)生.
這是我們的程序應(yīng)該獲得的第一個事件.</td>
</tr>
<tr>
<td><b>EXIT_PROCESS_DEBUG_EVENT</b></td>
<td>進(jìn)程退出.</td>
</tr>
<tr>
<td><b>CREATE_THEAD_DEBUG_EVENT</b></td>
<td>當(dāng)一個新線程在deuggee進(jìn)程中創(chuàng)建或我們的程序首次捆綁到運(yùn)行中的進(jìn)程時事件發(fā)生.要注意的是當(dāng)debugge的主線程被創(chuàng)建時不會收到該通知.
</td>
</tr>
<tr>
<td height="131"><b>EXIT_THREAD_DEBUG_EVENT</b></td>
<td height="131">debuggee中的線程退出時事件發(fā)生.debugee的主線程退出時不會收到該通知.我們可以認(rèn)為debuggee的主線程與debugge進(jìn)程是同義詞.
因此, 當(dāng)我們的程序看到<b>CREATE_PROCESS_DEBUG_EVENT</b>標(biāo)志時,對主線程來說,就是<b>CREATE_THREAD_DEBUG_EVENT</b>標(biāo)志.</td>
</tr>
<tr>
<td><b>LOAD_DLL_DEBUG_EVENT</b></td>
<td>debuggee裝入一個DLL.當(dāng)PE裝載器第一次分解指向DLL的鏈接時,我們將收到這一事件. (當(dāng)調(diào)用<b>CreateProcess</b>裝入
debuggee時)并且當(dāng)debuggee調(diào)用LoadLibrary時也會發(fā)生.</td>
</tr>
<tr>
<td><b>UNLOAD_DLL_DEBUG_EVENT</b></td>
<td>一個DLL從debuggee中卸載時事件發(fā)生. </td>
</tr>
<tr>
<td><b>EXCEPTION_DEBUG_EVENT</b></td>
<td>在debuggee中發(fā)生異常時事件發(fā)生. <b>注意:</b> 該事件僅在debuggee開始它的第一條指令之前發(fā)生一次.異常實(shí)際上是一個調(diào)試中斷(int
3h).如果想恢復(fù)debuggee事,以<b> DBG_CONTINUE </b>標(biāo)志調(diào)用<b>ContinueDebugEvent </b>函數(shù).
不要使用<b>DBG_EXCEPTION_NOT_HANDLED</b> 標(biāo)志否則debuggee會在NT下拒絕運(yùn)行(Win98下運(yùn)行得很好).</td>
</tr>
<tr>
<td><b>OUTPUT_DEBUG_STRING_EVENT</b></td>
<td>當(dāng)debuggee調(diào)用<b>DebugOutputString</b>函數(shù)向我們的程序發(fā)送消息字符串時該事件發(fā)生. </td>
</tr>
<tr>
<td><b>RIP_EVENT</b></td>
<td>系統(tǒng)調(diào)試發(fā)生錯誤</td>
</tr>
</table>
<p><b>dwProcessId</b> 和<b>dwThreadId</b>發(fā)生調(diào)試事件的進(jìn)程和線程Id.我們可以用這些值作為我們感興趣的進(jìn)程或線程的標(biāo)志符.記住如果我們使用<b>CreateProcess</b>來裝載debuggee,我們?nèi)钥稍?lt;b>PROCESS_INFO</b>結(jié)構(gòu)中獲得debuggee的進(jìn)程和線程.我們可以用這些值來區(qū)別調(diào)試事件是發(fā)生在debuggee中還是它的子進(jìn)程中(當(dāng)沒有指定
<b>DEBUG_ONLY_THIS_PROCESS </b>標(biāo)志時).</p>
<p> <b>u</b> 是一個聯(lián)合,包含了調(diào)試事件的更多信息.根據(jù)上面<b>dwDebugEventCode</b>的不同,它可以是以下結(jié)構(gòu):</p>
<table border="1" cellspacing="2" cellpadding="2" align="center">
<tr bgcolor="#009900">
<th><b>dwDebugEventCode</b></th>
<th>u的解釋</th>
</tr>
<tr>
<td><b>CREATE_PROCESS_DEBUG_EVENT</b></td>
<td>名為<b>CreateProcessInfo</b>的<b>CREATE_PROCESS_DEBUG_INFO</b>結(jié)構(gòu)</td>
</tr>
<tr>
<td><b>EXIT_PROCESS_DEBUG_EVENT</b></td>
<td>名為<b>ExitProcess</b>的<b>EXIT_PROCESS_DEBUG_INFO</b>結(jié)構(gòu)</td>
</tr>
<tr>
<td><b>CREATE_THREAD_DEBUG_EVENT</b></td>
<td>名為<b>CreateThread</b>的<b>CREATE_THREAD_DEBUG_INFO</b>結(jié)構(gòu)</td>
</tr>
<tr>
<td><b>EXIT_THREAD_DEBUG_EVENT</b></td>
<td>名為<b>ExitThread</b>的<b>EXIT_THREAD_DEBUG_EVENT </b>結(jié)構(gòu)</td>
</tr>
<tr>
<td><b>LOAD_DLL_DEBUG_EVENT</b></td>
<td>名為<b>LoadDll</b>的<b>LOAD_DLL_DEBUG_INFO</b> 結(jié)構(gòu)</td>
</tr>
<tr>
<td><b>UNLOAD_DLL_DEBUG_EVENT</b></td>
<td>名為<b>UnloadDll</b>的<b>UNLOAD_DLL_DEBUG_INFO</b>結(jié)構(gòu)</td>
</tr>
<tr>
<td><b>EXCEPTION_DEBUG_EVENT</b></td>
<td>名為<b>Exception</b>的<b>EXCEPTION_DEBUG_INFO</b>結(jié)構(gòu)</td>
</tr>
<tr>
<td><b>OUTPUT_DEBUG_STRING_EVENT</b></td>
<td>名為<b>DebugString</b>的<b>OUTPUT_DEBUG_STRING_INFO </b>結(jié)構(gòu)</td>
</tr>
<tr>
<td><b>RIP_EVENT</b></td>
<td>名為<b>RipInfo</b>的<b>RIP_INFO</b> 結(jié)構(gòu)</td>
</tr>
</table>
<p>我不會在這一個教程里講所有這些結(jié)構(gòu)的細(xì)節(jié),這里只詳細(xì)講一下<b>CREATE_PROCESS_DEBUG_INFO </b>結(jié)構(gòu). <br>
假設(shè)我們的程序調(diào)用了<b>WaitForDebugEvent</b>函數(shù)并返回,我們要做的第一件事就是檢查<b>dwDebugEventCode</b>中的值來看debuggee進(jìn)程中發(fā)生了那種類型的調(diào)試事件.比如說,如果<b>dwDebugEventCode</b>的值為
<b>CREATE_PROCESS_DEBUG_EVENT</b>,就可認(rèn)為<b>u</b>的成員為<b>CreateProcessInfo</b>
并用<b>u.CreateProcessInfo</b>來訪問. </p>
<li><b>在我們的程序中做對調(diào)試事件的響應(yīng)</b>. 當(dāng)<b>WaitForDebugEvent </b>返回時,這意味著在debuggee進(jìn)程中發(fā)生了調(diào)試事件或者發(fā)生了超時.所以我們的程序要檢查<b>dwDebugEventCode</b>
來作出適當(dāng)?shù)姆磻?yīng).這里有些象處理Windows消息:由用戶來選擇和忽略消息.</li>
<li><b>繼續(xù)運(yùn)行debuggee</b>. 當(dāng)調(diào)試事件發(fā)生時, Windows掛起了debuggee,所以當(dāng)我們處理完調(diào)試事件,還要讓debuggee繼續(xù)運(yùn)行.調(diào)用<b>ContinueDebugEvent</b>
函數(shù)來完成這一過程.
<p><b>ContinueDebugEvent proto dwProcessId:DWORD, dwThreadId:DWORD, dwContinueStatus:DWORD</b></p>
<p>該函數(shù)恢復(fù)由于調(diào)試事件而掛起的線程.<br>
<b>dwProcessId</b>和<b>dwThreadId</b>是要恢復(fù)的線程的進(jìn)程ID和線程ID,通常這兩個值從 <b>DEBUG_EVENT</b>結(jié)構(gòu)的<b>dwProcessId</b>
和<b>dwThreadId</b>成員獲得.<br>
<b>dwContinueStatus</b>顯示了如何繼續(xù)報告調(diào)試事件的線程.可能的取值有兩個:<b> DBG_CONTINUE</b> 和<b>DBG_EXCEPTION_NOT_HANDLED</b>.
對大多數(shù)調(diào)試事件,這兩個值都一樣:恢復(fù)線程.唯一的例外是<b>EXCEPTION_DEBUG_EVENT</b>,如果線程報告發(fā)生了一個異常調(diào)試事件,這意味著在debuggee的線程中發(fā)生了一個異常.如果指定了<b>DBG_CONTINUE</b>,線程將忽略它自己的異常處理部分并繼續(xù)執(zhí)行.在這種情況下,我們的程序必須在以<b>DBG_CONTINUE</b>恢復(fù)線程之前檢查并處理異常,否則異常將生生不息地不斷發(fā)生....如果我們指定了
<b>DBG_EXCEPTION_NOT_HANDLED</b>值,就是告訴Windows我們的程序并不處理異常:Windows將使用debuggee的默認(rèn)異常處理函數(shù)來處理異常.
<br>
總而言之,如果我們的程序沒有考慮異常,而調(diào)試事件又指向debuggee進(jìn)程中的一個異常的話,就應(yīng)調(diào)用含<b>DBG_CONTINUE</b>標(biāo)志的<b>ContinueDebugEvent</b>函數(shù).否則,我們的程序就必須以<b>DBG_EXCEPTION_NOT_HANDLED</b>調(diào)用
<b>ContinueDebugEvent</b>.但在下面這種情況下必須使用<b>DBG_CONTINUE</b>標(biāo)志:第一個在ExceptionCode成員中有值<b>EXCEPTION_BREAKPOINT</b>的
<b>EXCEPTION_DEBUG_EVENT</b>事件.當(dāng)debuggee開始執(zhí)行它的第一條指令時,我們的函數(shù)將接受到異常調(diào)試事件.它事實(shí)上是一個調(diào)試中斷(int
3h).如果我們以<b>DBG_EXCEPTION_NOT_HANDLED</b>調(diào)用<b>ContinueDebugEvent </b>來響應(yīng)調(diào)試事件,
Windows NT會拒絕執(zhí)行debuggee(因為它沒有異常處理).所以在這種情況下,要用<b>DBG_CONTINUE</b>標(biāo)志告訴Windows我們希望該線程繼續(xù)執(zhí)行.</p>
</li>
<li><b>繼續(xù)上面的步驟循環(huán)直到debuggee進(jìn)程退出</b>. 我們的程序必須在一個很象消息循環(huán)的無限循環(huán)中直到debuggee結(jié)束.該循環(huán)大體如下:
<p><b>.while TRUE<br>
invoke WaitForDebugEvent, addr DebugEvent, INFINITE<br>
.break .if DebugEvent.dwDebugEventCode==EXIT_PROCESS_DEBUG_EVENT<br>
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -