久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

利用Win32 Debug API打造自己的調(diào)試器Debugger

 看風(fēng)景D人 2013-12-26
很多朋友都?jí)粝胗凶约旱腄ebugger程序,今天我們就來(lái)自己制作一個(gè),。作為一個(gè)Debugger程序,,其最基本的功能框架其實(shí)就是完成2件事情:
啟動(dòng)目標(biāo)程序,。
實(shí)時(shí)監(jiān)控目標(biāo)程序的運(yùn)行,,并做出相應(yīng)的應(yīng)對(duì),。
我們要打造自己的Debugger程序,,實(shí)際上也只需要完成這兩個(gè)功能就可以了,。當(dāng)然,要完成這兩個(gè)特定的功能,,我們不可能從頭開(kāi)始造輪子,,要首先看看操作系統(tǒng)給我們提供了什么樣的基礎(chǔ)設(shè)施:
由于我們是在Windows平臺(tái)上工作,自然離不開(kāi)微軟公司提供的文檔大全——MSDN,。翻開(kāi)MSDN,,定位到“Debugging and Error Handling”,一些最基本的Windows Debug信息都在這里面,。不過(guò)與其他欄目相比,,這個(gè)欄目的信息明顯顯得單薄許多——也許越是底層、強(qiáng)大的技術(shù),,微軟越不想公開(kāi)吧,。
初步瀏覽之后,我們可以確定,,對(duì)于我們的Debugger而言,,最重要的Debug API有如下幾個(gè):
CreateProcess —— 用于創(chuàng)建被調(diào)試進(jìn)程
WaitForDebugEvent —— Debug Loop(調(diào)試循環(huán))的主要構(gòu)成函數(shù)
ContinueDebugEvent —— 用于構(gòu)成Debug Loop
GetThreadContext —— 得到被調(diào)試進(jìn)程的寄存器信息
SetThreadContext —— 設(shè)置被調(diào)試進(jìn)程的寄存器信息
ReadProcessMemory —— 得到被調(diào)試進(jìn)程的內(nèi)存內(nèi)容
WriteProcessMemory —— 設(shè)置被調(diào)試進(jìn)程的內(nèi)存內(nèi)容
最重要的數(shù)據(jù)結(jié)構(gòu)有如下幾個(gè):
CONTEXT —— 寄存器結(jié)構(gòu)
STARTUPINFO —— Start信息
PROCESS_INFORMATION —— 進(jìn)程相關(guān)信息
DEBUG_EVENT —— Debug Event(調(diào)試事件)結(jié)構(gòu)
可以說(shuō),,我們的Debugger程序就是利用這幾個(gè)API函數(shù)結(jié)合下面的幾個(gè)數(shù)據(jù)結(jié)構(gòu),完成我們指定的功能,。那么下面就讓我們先來(lái)看看這幾個(gè)API和數(shù)據(jù)結(jié)構(gòu)的具體含義:

 


Debug API解析
在這里,,我們將對(duì)上面所述的幾個(gè)Debug調(diào)試API做一個(gè)檢閱式的考察,大概介紹一下每個(gè)API的應(yīng)用領(lǐng)域,。而將這些API應(yīng)用到具體實(shí)踐中去,,將會(huì)在下一部分“實(shí)例解析”中,給出詳細(xì)的說(shuō)明,。
1.CreateProcess,。
函數(shù)原型:BOOL CreateProcess(
LPCTSTR lpApplicationName, // 要?jiǎng)?chuàng)建的進(jìn)程模塊名
LPTSTR lpCommandLine, // 命令行字符串
LPSECURITY_ATTRIBUTES lpProcessAttributes, // 進(jìn)程安全屬性
LPSECURITY_ATTRIBUTES lpThreadAttributes, // 線程安全屬性
BOOL bInheritHandles, // 句柄繼承選項(xiàng)
DWORD dwCreationFlags, // 進(jìn)程創(chuàng)建選項(xiàng)
LPVOID lpEnvironment, // 進(jìn)程環(huán)境塊數(shù)據(jù)指針
LPCTSTR lpCurrentDirectory, // 當(dāng)前目錄名
LPSTARTUPINFO lpStartupInfo, // 啟動(dòng)信息
LPPROCESS_INFORMATION lpProcessInformation // 進(jìn)程信息
);
函數(shù)解析:該函數(shù)是Windows平臺(tái)提供的最基本的創(chuàng)建進(jìn)程的函數(shù)。每當(dāng)我們雙擊一個(gè)EXE可執(zhí)行文件,,Windows內(nèi)核就會(huì)自動(dòng)調(diào)用該函數(shù)創(chuàng)建我們雙擊的文件所對(duì)應(yīng)的進(jìn)程,。該函數(shù)中,最重要的參數(shù)有三個(gè):一個(gè)是進(jìn)程模塊名,,指明了要?jiǎng)?chuàng)建哪個(gè)進(jìn)程,;一個(gè)是進(jìn)程創(chuàng)建選項(xiàng),指明了要如何創(chuàng)建目標(biāo)進(jìn)程,;對(duì)于Debugger程序而言,,最常用的創(chuàng)建選項(xiàng)就是:DEBUG_PROCESS|DEBUG_ONLY_THIS_PROCESS。最后還有一個(gè)就是進(jìn)程信息,,我們調(diào)用CreateProcess創(chuàng)建了進(jìn)程以后,,Windows會(huì)將新創(chuàng)建的進(jìn)程的相關(guān)信息全部放到ProcessInfo信息塊中,我們?cè)贒ebug Loop調(diào)試循環(huán)中使用進(jìn)程信息塊中的數(shù)據(jù)與目標(biāo)進(jìn)程交互,,監(jiān)視和控制目標(biāo)進(jìn)程的動(dòng)作,。
2.WaitForDebugEvent。
函數(shù)原型:BOOL WaitForDebugEvent(
LPDEBUG_EVENT lpDebugEvent, // Debug Event(調(diào)試事件指針)
DWORD dwMilliseconds // 超時(shí)設(shè)置
);
函數(shù)解析:該函數(shù)構(gòu)成了Debug Loop調(diào)試循環(huán)的主體,,一個(gè)Debugger程序在創(chuàng)建出目標(biāo)進(jìn)程后,,一般都會(huì)緊接著循環(huán)調(diào)用該函數(shù)等待目標(biāo)進(jìn)程的各種調(diào)試信息,這個(gè)循環(huán)調(diào)用WaitForDebugEvent的過(guò)程,,我們就稱(chēng)之為Debug Loop調(diào)試循環(huán),。調(diào)試循環(huán)是所有Debugger程序的主體部分,Debugger幾乎所有的監(jiān)視,、控制,、調(diào)整的功能都是在調(diào)試循環(huán)內(nèi)完成的。一般來(lái)說(shuō),,此處的超時(shí)設(shè)置都設(shè)置為-1也就是無(wú)窮等待下去,,該函數(shù)是非阻塞函數(shù),在沒(méi)有Debug Event發(fā)生,處于等待的過(guò)程中,,僅消耗極其微小的系統(tǒng)資源,。
3.ContinueDebugEvent。
函數(shù)原型:BOOL ContinueDebugEvent(
DWORD dwProcessId, // 目標(biāo)進(jìn)程ID
DWORD dwThreadId, // 目標(biāo)線程ID
DWORD dwContinueStatus // 線程繼續(xù)的標(biāo)志
);
函數(shù)解析:該函數(shù)主要用于Debugger在Debug Loop調(diào)試循環(huán)中,,處理完Debug Event,,通知目標(biāo)進(jìn)/線程繼續(xù)運(yùn)作。通常情況下,,目標(biāo)進(jìn)程ID和目標(biāo)線程ID這兩個(gè)參數(shù),,都是CreateProcess調(diào)用后,ProcessInfo結(jié)構(gòu)中所包含的信息,。該函數(shù)通過(guò)目標(biāo)進(jìn)程/線程ID來(lái)唯一標(biāo)識(shí)目標(biāo)進(jìn)/線程,,并且通過(guò)設(shè)置不同的ContinueStatus來(lái)通知目標(biāo)進(jìn)/線程繼續(xù)運(yùn)行的動(dòng)作。最主要的ContinueStatus有兩種選擇:一個(gè)是DBG_CONTINUE,,表明調(diào)試事件已經(jīng)被Debugger處理完畢,,目標(biāo)進(jìn)/線程可以照常繼續(xù)運(yùn)行,;另一個(gè)是DBG_EXCEPTION_NOT_HANDLED,,表明Debugger并未處理該調(diào)試事件,目標(biāo)進(jìn)程收到該標(biāo)志位后,,將會(huì)將調(diào)試事件沿著Windows異常調(diào)用鏈繼續(xù)往下發(fā)送,。直至該調(diào)試事件被處理完為止——當(dāng)然,如果目標(biāo)進(jìn)程發(fā)出的Debug Event沒(méi)有任何調(diào)試器能夠處理,,那最后Windows只有祭出自己的殺手锏:應(yīng)用程序XXX異常,,即將被關(guān)閉。
3.GetThreadContext & SetThreadContext,。
函數(shù)原型:BOOL GetThreadContext(
HANDLE hThread, // 目標(biāo)線程句柄
LPCONTEXT lpContext // CONTEXT結(jié)構(gòu)
);
BOOL SetThreadContext(
HANDLE hThread, // 目標(biāo)線程句柄
CONST CONTEXT *lpContext // CONTEXT結(jié)構(gòu)
);
函數(shù)解析:這兩個(gè)函數(shù)分別用來(lái)得到和設(shè)置目標(biāo)線程的寄存器內(nèi)容,。請(qǐng)注意,在Windows操作系統(tǒng)中,,操作系統(tǒng)調(diào)度的最小單位粒度是線程而不是進(jìn)程,,所以,籠統(tǒng)地說(shuō):設(shè)置某進(jìn)程的寄存器內(nèi)容是錯(cuò)誤的,,因?yàn)橐粋€(gè)進(jìn)程可能對(duì)應(yīng)多個(gè)線程,。因此在和寄存器打交道的時(shí)候,一定要指明是哪個(gè)線程所對(duì)應(yīng)的寄存器,。該函數(shù)參數(shù)中,,由hThread參數(shù)也就是線程句柄參數(shù)指定目標(biāo)線程,該參數(shù)的來(lái)源也通常是CreateProcess調(diào)用后所得到的ProcessInfomation中的hThread成員,。而CONTEXT結(jié)構(gòu)則是根據(jù)所在機(jī)器硬件平臺(tái)的不同而有不同的定義,。Windows操作系統(tǒng)在Intel、MIPS、Alpha和PowerPC平臺(tái)上,,各有不同的CONTEXT定義,,每種定義都忠實(shí)而完整地反映出了目標(biāo)CPU的寄存器分布情況。不過(guò)雖然CONTEXT結(jié)構(gòu)在不同的CPU平臺(tái)上有不同的表現(xiàn)形式,,但是最基本的Intel X86架構(gòu)在各個(gè)CPU上面的表現(xiàn)都是相同的,,因此,只要Debugger代碼未牽涉到各個(gè)CPU非常Specifc的細(xì)節(jié),,還是可以跨CPU平臺(tái)使用的,。
4.ReadProcessMemory & WriteProcessMemory。
函數(shù)原型:BOOL ReadProcessMemory(
HANDLE hProcess, // 進(jìn)程句柄
LPCVOID lpBaseAddress, // 欲讀取內(nèi)存基地址
LPVOID lpBuffer, // 數(shù)據(jù)緩沖區(qū)指針
SIZE_T nSize, // 欲讀取的內(nèi)存內(nèi)容長(zhǎng)度
SIZE_T * lpNumberOfBytesRead // 實(shí)際讀取的內(nèi)存內(nèi)容長(zhǎng)度
);
BOOL WriteProcessMemory(
HANDLE hProcess, // 進(jìn)程句柄
LPVOID lpBaseAddress, // 欲寫(xiě)入內(nèi)存基地址
LPCVOID lpBuffer, // 數(shù)據(jù)緩沖區(qū)指針
SIZE_T nSize, // 欲寫(xiě)入的內(nèi)存內(nèi)容長(zhǎng)度
SIZE_T * lpNumberOfBytesWritten // 實(shí)際寫(xiě)入的內(nèi)存內(nèi)容長(zhǎng)度
);
函數(shù)解析:這兩個(gè)函數(shù)分別用于“讀取”和“寫(xiě)入”目標(biāo)進(jìn)程的內(nèi)存地址空間,。與寄存器操作不同,,Windows對(duì)內(nèi)存的分配粒度是以進(jìn)程為單位的。由于自Intel 386以后,,所有的Intel x86系列CPU都采用的保護(hù)模式,,因此在保護(hù)模式下,Windows為每一個(gè)應(yīng)用程序——也就是每一個(gè)進(jìn)程都虛擬出一個(gè)擁有4GB大小內(nèi)存的“虛擬機(jī)器”,, 隸屬于該進(jìn)程的所有線程都共享這4GB的地址空間,。因此,與上面寄存器操作不同,,讀取,、寫(xiě)入內(nèi)存操作時(shí),我們需要的是進(jìn)程的句柄——當(dāng)然,,該句柄也來(lái)源于CreateProcess后得到的ProcessInfomation結(jié)構(gòu),。有了進(jìn)程句柄以后,我們還需要一個(gè)基地址和一個(gè)長(zhǎng)度參數(shù)來(lái)確定我們的Debugger程序所需要讀取的內(nèi)存范圍,。當(dāng)然,,此處的基地址數(shù)值上應(yīng)該對(duì)應(yīng)于Windows虛擬出來(lái)的那4GB的平坦地址空間內(nèi)的地址——也就是經(jīng)過(guò)了段選擇和頁(yè)選擇過(guò)程后的地址數(shù)值。


DEBUG_EVENT全面剖析
在整個(gè)調(diào)試循環(huán)中,,Debugger和目標(biāo)進(jìn)程之間調(diào)試信息的交互完全是通過(guò)調(diào)用WaitForDebugEvent時(shí),,傳遞的DEBUG_EVENT結(jié)構(gòu)參數(shù)。該結(jié)構(gòu)的定義初看起來(lái)似乎很簡(jiǎn)單:
typedef struct _DEBUG_EVENT {
DWORD dwDebugEventCode;
DWORD dwProcessId;
DWORD dwThreadId;
union {
EXCEPTION_DEBUG_INFO Exception;
CREATE_THREAD_DEBUG_INFO CreateThread;
CREATE_PROCESS_DEBUG_INFO CreateProcessInfo;
EXIT_THREAD_DEBUG_INFO ExitThread;
EXIT_PROCESS_DEBUG_INFO ExitProcess;
LOAD_DLL_DEBUG_INFO LoadDll;
UNLOAD_DLL_DEBUG_INFO UnloadDll;
OUTPUT_DEBUG_STRING_INFO DebugString;
RIP_INFO RipInfo;
} u;
} DEBUG_EVENT, *LPDEBUG_EVENT;
不過(guò)就是一個(gè)union聯(lián)合域加上三個(gè)普通的信息數(shù)據(jù),??杉?xì)細(xì)推敲之下,這個(gè)結(jié)構(gòu)所實(shí)現(xiàn)的功能卻絕對(duì)不簡(jiǎn)單——讓我們做一個(gè)最簡(jiǎn)單的思考,,作為Debugger,,至少應(yīng)該能夠收到:
目標(biāo)進(jìn)程啟動(dòng)
目標(biāo)進(jìn)程發(fā)生異常
目標(biāo)進(jìn)程退出
這三個(gè)最基本的調(diào)試信息。而且每個(gè)信息所對(duì)應(yīng)的信息類(lèi)型應(yīng)該都不太一樣,,比如:
目標(biāo)進(jìn)程啟動(dòng)時(shí),,我們需要的是目標(biāo)進(jìn)程的模塊名字,權(quán)限設(shè)置等啟動(dòng)信息
目標(biāo)進(jìn)程異常時(shí),我們應(yīng)該能夠知道異常的地址,,異常的原因(這里面分類(lèi)又很多),,異常的嚴(yán)重程度
目標(biāo)進(jìn)程退出時(shí),我們應(yīng)該能夠知道進(jìn)程的退出值,,用來(lái)確定進(jìn)程是否屬于正常退出
上面所列出的,,還只是一些最基本的要素而已,如果要構(gòu)成整個(gè)Windows Debug API的數(shù)據(jù)交互層,,那情況還要復(fù)雜得多,。將這么多錯(cuò)綜復(fù)雜的信息全部用一個(gè)結(jié)構(gòu)表示,其難度可想而知,。
微軟公司在這里選擇的方法是通過(guò)dwDebugEventCode標(biāo)識(shí)最基本信息,,然后通過(guò)union聯(lián)合域?qū)⒏鞣N信息全部包羅進(jìn)去的方法。這種做法的優(yōu)點(diǎn)是在WaitForDebugEvent調(diào)用時(shí),,只需要傳遞一個(gè)統(tǒng)一的結(jié)構(gòu)參數(shù)即可,;缺點(diǎn)就是Debug_Event結(jié)構(gòu)內(nèi)部信息的異常復(fù)雜,給程序設(shè)計(jì)帶來(lái)不小的麻煩,。
我們?cè)谑褂肈ebug_Event結(jié)構(gòu)時(shí),,首先要得到dwDebugEventCode的值,從而確定union聯(lián)合域內(nèi)是什么內(nèi)容,。兩者之間的對(duì)應(yīng)關(guān)系如下表所示:


dwDebugEventCode的值
Union聯(lián)合域類(lèi)型 調(diào)試信息
EXCEPTION_DEBUG_EVENT EXCEPTION_DEBUG_INFO
應(yīng)用程序發(fā)生異常
CREATE_THREAD_DEBUG_EVENT CREATE_THREAD_DEBUG_INFO
線程創(chuàng)建
CREATE_PROCESS_DEBUG_EVENT CREATE_PROCESS_DEBUG_INFO
進(jìn)程創(chuàng)建
EXIT_THREAD_DEBUG_EVENT EXIT_THREAD_DEBUG_INFO
線程退出
EXIT_PROCESS_DEBUG_EVENT EXIT_PROCESS_DEBUG_INFO
進(jìn)程退出
LOAD_DLL_DEBUG_EVENT LOAD_DLL_DEBUG_INFO
Dll載入
UNLOAD_DLL_DEBUG_EVENT UNLOAD_DLL_DEBUG_INFO
Dll卸載
OUTPUT_DEBUG_STRING_EVENT OUTPUT_DEBUG_STRING_INFO
輸出Debug字符串
RIP_EVENT RIP_INFO 系統(tǒng)調(diào)試錯(cuò)誤
上表僅僅是Debug_Event結(jié)構(gòu)數(shù)據(jù)解析的第一層,,當(dāng)union聯(lián)合域取得各個(gè)不同的值時(shí),這些第二層的數(shù)據(jù)結(jié)構(gòu)并不比這一層簡(jiǎn)單多少——不過(guò)幸虧大部分的時(shí)間我們解析Debug_Event結(jié)構(gòu)只需要解析到第二層就可以了,。
一般情況下,我們最感興趣的DebugEventCode就是EXCEPTION_DEBUG_EVENT,,而不巧的是,,EXCEPTION_DEBUG_EVENT所對(duì)應(yīng)的EXCEPTION_DEBUG_INFO結(jié)構(gòu)也是所有union聯(lián)合域結(jié)構(gòu)中最復(fù)雜的一個(gè),因此,,有必要在此再對(duì)EXCEPTION_DEBUG_INFO這個(gè)二級(jí)數(shù)據(jù)結(jié)構(gòu)作進(jìn)一步的詳細(xì)說(shuō)明:
typedef struct _EXCEPTION_DEBUG_INFO {
EXCEPTION_RECORD ExceptionRecord;
DWORD dwFirstChance;
} EXCEPTION_DEBUG_INFO, *LPEXCEPTION_DEBUG_INFO;
dwFirstChange:如果為0,,表示是該異常從前未被處理過(guò),也就是當(dāng)前我們的Debugger程序處于Windows異常處理鏈條的頭部,。
ExceptionRecord:該結(jié)構(gòu)內(nèi)部的信息是EXCEPTION_DEBUG_INFO結(jié)構(gòu)的真實(shí)存儲(chǔ)地點(diǎn),。因此,繼續(xù)剖析該結(jié)構(gòu),。
typedef struct _EXCEPTION_RECORD {
DWORD ExceptionCode;
DWORD ExceptionFlags;
struct _EXCEPTION_RECORD *ExceptionRecord;
PVOID ExceptionAddress;
DWORD NumberParameters;
ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
} EXCEPTION_RECORD, *PEXCEPTION_RECORD;
首先:要注意到,,該結(jié)構(gòu)內(nèi)部有一個(gè)類(lèi)型為EXCEPTION_RECORD*的數(shù)據(jù)成員,這是C語(yǔ)言里經(jīng)典的串聯(lián)數(shù)據(jù)的方式:用一個(gè)指針域連接各個(gè)數(shù)據(jù)成員,,可以構(gòu)成經(jīng)典的“鏈表”數(shù)據(jù)結(jié)構(gòu)(英文里管這種做法叫做Chain),。
其次:ExceptionCode成員標(biāo)識(shí)了該結(jié)構(gòu)所代表的EXCEPTION_RECORD的類(lèi)型,Windows內(nèi)部定義了如下20種異常行為:


值 含義
EXCEPTION_ACCESS_VIOLATION 存取越界 
EXCEPTION_ARRAY_BOUNDS_EXCEEDED 由硬件監(jiān)測(cè)到的數(shù)組訪問(wèn)越界 
EXCEPTION_BREAKPOINT 觸發(fā)斷點(diǎn) 
EXCEPTION_DATATYPE_MISALIGNMENT 數(shù)據(jù)未對(duì)齊
EXCEPTION_FLT_DENORMAL_OPERAND 浮點(diǎn)操作數(shù)范圍越界(太小或太大)
EXCEPTION_FLT_DIVIDE_BY_ZERO 浮點(diǎn)操作除數(shù)為0
EXCEPTION_FLT_INEXACT_RESULT 浮點(diǎn)運(yùn)算結(jié)果不能用小數(shù)正常表示.
EXCEPTION_FLT_INVALID_OPERATION 其他未知的浮點(diǎn)數(shù)錯(cuò)誤
EXCEPTION_FLT_OVERFLOW 浮點(diǎn)操作太大溢出
EXCEPTION_FLT_STACK_CHECK 浮點(diǎn)棧溢出.
EXCEPTION_FLT_UNDERFLOW 浮點(diǎn)操作太小溢出
EXCEPTION_ILLEGAL_INSTRUCTION 執(zhí)行非法指令
EXCEPTION_IN_PAGE_ERROR 存取未存在的內(nèi)存頁(yè)
EXCEPTION_INT_DIVIDE_BY_ZERO 除數(shù)為0
EXCEPTION_INT_OVERFLOW 整數(shù)操作,最高位溢出
EXCEPTION_INVALID_DISPOSITION 錯(cuò)誤的異常處理程序地址
EXCEPTION_NONCONTINUABLE_EXCEPTION 遇上不能繼續(xù)執(zhí)行的異常
EXCEPTION_PRIV_INSTRUCTION 當(dāng)前模式下,,不能執(zhí)行該指令
EXCEPTION_SINGLE_STEP 單步跟蹤斷點(diǎn)觸發(fā)
EXCEPTION_STACK_OVERFLOW 線程的??臻g溢出
一般而言,我們的Debugger程序處理得最多的異常情況就是EXCEPTION_BREAKPOINT和EXCEPTION_SINGLE_STEP,。例如:我們需要在目標(biāo)進(jìn)程運(yùn)行到0x00400000地址的時(shí)候?qū)δ繕?biāo)進(jìn)程進(jìn)行一些操作,,那么我們只要設(shè)法讓目標(biāo)程序運(yùn)行到0x00400000的時(shí)候,向我們的Debugger程序發(fā)出異常信號(hào),,那么我們的Debugger程序就能收到該信號(hào),,并進(jìn)而通過(guò)前面介紹的Set/GetThreadContext和Read/WriteProcessMemory函數(shù)對(duì)目標(biāo)進(jìn)程進(jìn)行控制操作。


一個(gè)最初步的Debugger框架
既然上面所述的內(nèi)容現(xiàn)在已經(jīng)足夠我們寫(xiě)出一個(gè)最基本的Debugger了,,那么我們就來(lái)“實(shí)戰(zhàn)演習(xí)”一番,,先寫(xiě)出一個(gè)最基本的Debugger程序,該程序完成的功能異常簡(jiǎn)單:利用CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS作為標(biāo)志,,創(chuàng)建一個(gè)進(jìn)程,,并讓目標(biāo)進(jìn)程能夠照常地運(yùn)作起來(lái)。
首先跳入我們腦海的代碼大概如下所示:
::CreateProcess (_T("Msg.exe"), NULL, NULL, NULL, NULL, DEBUG_PROCESS|DEBUG_ONLY_THIS_PROCESS,
NULL, NULL, &sif, &pi) ;
//這個(gè)下面就是大名鼎鼎的Debug框架,!
do
{
::WaitForDebugEvent (&DBEvent, INFINITE) ;
dwState = DBG_EXCEPTION_NOT_HANDLED ;
case EXIT_PROCESS_DEBUG_EVENT :
{
STOP = TRUE ;
break ;
}
if (!STOP)
{
::ContinueDebugEvent(pi.dwProcessId, pi.dwThreadId, dwState) ;
}
} while (!STOP) ;
編譯,,雙擊,很不幸,!Windows給了我們
為什么會(huì)出現(xiàn)上面這種錯(cuò)誤情況,?微軟的文檔里面并沒(méi)有對(duì)Debugger具體應(yīng)該如何工作做出詳細(xì)的說(shuō)明。不過(guò)通過(guò)跟蹤上面的程序,,我們發(fā)現(xiàn)每次Msg.exe目標(biāo)應(yīng)用程序啟動(dòng)前,,都會(huì)向我們的Debugger程序發(fā)送一個(gè)EXCEPTION_BREAKPOINT的斷點(diǎn)信號(hào),而我們的Debug Loop并沒(méi)有對(duì)該信號(hào)進(jìn)行處理?,F(xiàn)在我們加入對(duì)該信號(hào)的處理過(guò)程,,使我們的Debugger不返回DBG_EXCEPTION_NOT_HANDLED而返回DBG_CONTINUE,具體到代碼中,,就是在:
case EXIT_PROCESS_DEBUG_EVENT :
這一句之前,,加入:
case EXCEPTION_DEBUG_EVENT:
{
switch (DBEvent.u.Exception.ExceptionRecord.ExceptionCode)
{
case EXCEPTION_BREAKPOINT:
{
dwState = DBG_CONTINUE ;
break ;
}
}
break ;
}
編譯、鏈接,,試運(yùn)行,,一切OK。Msg.exe很正常地彈出了MessageBox,,



從C到C++
從上面那個(gè)最簡(jiǎn)單的調(diào)試框架代碼不難看出,,每當(dāng)我們要增加一個(gè)處理判斷的異常類(lèi)型,都要在日益龐大復(fù)雜的switch – case代碼中增加新的選擇路線,,程序的代碼很容易就變得臃腫不堪且難以維護(hù),。而實(shí)際上整個(gè)代碼的工作骨架并沒(méi)有發(fā)生實(shí)質(zhì)性的改變,,變化的僅僅是針對(duì)各種不同的異常情況我們所需要的不同處理子過(guò)程——這不禁讓我們想起了Template設(shè)計(jì)模式——我們可以將在Debug Loop中分解Debug_Event的代碼放進(jìn)框架代碼中,然后在框架代碼中調(diào)用不同的hook虛函數(shù),,這樣,,我們要擴(kuò)展自己的Debugger功能時(shí),只需要從已有的debug_base基類(lèi)繼承,,重寫(xiě)hook虛方法即可,。
隨本文附帶的Debug_Base.h中有debug_base類(lèi),該類(lèi)就實(shí)現(xiàn)了如上所述的Debugger Template模板,。該類(lèi)最簡(jiǎn)單的使用如下所示:
Debugger::debug_base debugger ;
debugger.run_debug_loop(std::tstring(TEXT("Msg.exe"))) ;
以上這兩段代碼,,就實(shí)現(xiàn)了上一節(jié)所提到的那個(gè)最簡(jiǎn)單的調(diào)試器所完成的功能——載入被調(diào)試程序并且照常運(yùn)行。
Debug_base提供有如下幾個(gè)hook虛函數(shù):
hook函數(shù)名 函數(shù)作用
handle_first_exception Windows內(nèi)核首次發(fā)送EXCEPTION_BREAKPOINT時(shí)的處理子過(guò)程
handle_exception_breakpoint 調(diào)試過(guò)程中EXCEPTION_BREAKPOINT的處理子過(guò)程
handle_single_step 調(diào)試過(guò)程中EXCEPTION_SINGLE_STEP的處理子過(guò)程
handle_process_create 進(jìn)程創(chuàng)建處理子過(guò)程
handle_process_exit 進(jìn)程退出處理子過(guò)程
handle_thread_create 線程創(chuàng)建處理子過(guò)程
handle_thread_exit 線程推出處理子過(guò)程
handle_dll_load Dll載入時(shí)得到調(diào)用
handle_dll_unload Dll卸載的時(shí)候得到調(diào)用
handle_debug_wstring 被調(diào)試程序調(diào)用OutputDebugString得到調(diào)用
實(shí)際的編程過(guò)程中,,我們只需要從debug_base派生出自己的新類(lèi),,并重寫(xiě)其中需要的虛函數(shù)即可編譯得到自己的Debugger!
例如:我們想讓我們的Debugger具有如下功能:
在程序入口點(diǎn)處中斷,,提示已經(jīng)到達(dá)入口點(diǎn)
截獲被調(diào)試程序的Debug字符串,,并輸出
那么我們可以這樣設(shè)計(jì)我們的新類(lèi):
class MyDebugger : public Debugger::debug_base
{
virtual void handle_first_exception(const PROCESS_INFORMATION& pi)
{
::MessageBox(NULL, TEXT("首次中斷"), TEXT("Debugger Worked"), NULL) ;
return ;
}


virtual void handle_debug_wstring(std::wstring& debug_wstring)
{
::MessageBox(NULL, ATL::CW2T(debug_wstring.c_str()), TEXT("Debug String"), NULL) ;
return ;
}
} ;
而主程序并不需要做任何改變,該Debugger運(yùn)行后,,可以接受被調(diào)試程序的調(diào)試信息,,并輸出:



結(jié)束語(yǔ)
Windows借助于Debug API,為我們提供了一個(gè)功能全面的Ring 3級(jí)別的調(diào)試平臺(tái),,但是由于文檔資料,、示例代碼的缺乏,迄今為止,,Debug API的應(yīng)用面還很窄,。其實(shí)如果能真正把握住動(dòng)態(tài)調(diào)試的精髓,利用Debug API打造一個(gè)自己的專(zhuān)有調(diào)試器調(diào)試一些很特殊的程序是能收到非常好的效果的,??梢哉f(shuō),任何一款脫殼機(jī)或者是內(nèi)存注冊(cè)機(jī)都只不過(guò)是Debug API的一個(gè)具體應(yīng)用,。
當(dāng)然,由于篇幅所限,,本文也不可能更加詳盡深入地探索Windows Debugger的世界,,我們的debug_base類(lèi)還很初步——甚至還不具備在程序運(yùn)行時(shí)增加調(diào)試斷點(diǎn)的功能,不過(guò)只要理解了Debugger的工作原理,,結(jié)合MicroSoft和Intel的一些底層的編程資料,,各種功能都可以慢慢添加、完善,。希望讀者朋友在學(xué)習(xí)逆向工程的過(guò)程中,,注意基礎(chǔ)知識(shí)的積累,、整理、消化,,不斷地提升自己對(duì)計(jì)算機(jī)系統(tǒng)的理解水平,,最終才能修成一代宗師,打造出made in china的Soft-ice和OllyDbg,!


    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn),。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類(lèi)似文章 更多