• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            S.l.e!ep.¢%

            像打了激速一樣,以四倍的速度運(yùn)轉(zhuǎn),開(kāi)心的工作
            簡(jiǎn)單、開(kāi)放、平等的公司文化;尊重個(gè)性、自由與個(gè)人價(jià)值;
            posts - 1098, comments - 335, trackbacks - 0, articles - 1
              C++博客 :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理
            利用Win32 Debug API打造自己的Debugger
            2006-10-21 23:43
            很多朋友都?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, // 線(xiàn)程安全屬性
            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)線(xiàn)程ID
            DWORD dwContinueStatus // 線(xiàn)程繼續(xù)的標(biāo)志
            );
            函數(shù)解析:該函數(shù)主要用于Debugger在Debug Loop調(diào)試循環(huán)中,處理完Debug Event,通知目標(biāo)進(jìn)/線(xiàn)程繼續(xù)運(yùn)作。通常情況下,目標(biāo)進(jìn)程ID和目標(biāo)線(xiàn)程ID這兩個(gè)參數(shù),都是CreateProcess調(diào)用后,ProcessInfo結(jié)構(gòu)中所包含的信息。該函數(shù)通過(guò)目標(biāo)進(jìn)程/線(xiàn)程ID來(lái)唯一標(biāo)識(shí)目標(biāo)進(jìn)/線(xiàn)程,并且通過(guò)設(shè)置不同的ContinueStatus來(lái)通知目標(biāo)進(jìn)/線(xiàn)程繼續(xù)運(yùn)行的動(dòng)作。最主要的ContinueStatus有兩種選擇:一個(gè)是DBG_CONTINUE,表明調(diào)試事件已經(jīng)被Debugger處理完畢,目標(biāo)進(jìn)/線(xià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)線(xiàn)程句柄
            LPCONTEXT lpContext // CONTEXT結(jié)構(gòu)
            );
            BOOL SetThreadContext(
            HANDLE hThread, // 目標(biāo)線(xiàn)程句柄
            CONST CONTEXT *lpContext // CONTEXT結(jié)構(gòu)
            );
            函數(shù)解析:這兩個(gè)函數(shù)分別用來(lái)得到和設(shè)置目標(biāo)線(xiàn)程的寄存器內(nèi)容。請(qǐng)注意,在Windows操作系統(tǒng)中,操作系統(tǒng)調(diào)度的最小單位粒度是線(xiàn)程而不是進(jìn)程,所以,籠統(tǒng)地說(shuō):設(shè)置某進(jìn)程的寄存器內(nèi)容是錯(cuò)誤的,因?yàn)橐粋€(gè)進(jìn)程可能對(duì)應(yīng)多個(gè)線(xiàn)程。因此在和寄存器打交道的時(shí)候,一定要指明是哪個(gè)線(xiàn)程所對(duì)應(yīng)的寄存器。該函數(shù)參數(shù)中,由hThread參數(shù)也就是線(xiàn)程句柄參數(shù)指定目標(biāo)線(xiàn)程,該參數(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)程的所有線(xià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
            線(xiàn)程創(chuàng)建
            CREATE_PROCESS_DEBUG_EVENT CREATE_PROCESS_DEBUG_INFO
            進(jìn)程創(chuàng)建
            EXIT_THREAD_DEBUG_EVENT EXIT_THREAD_DEBUG_INFO
            線(xiàn)程退出
            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 線(xiàn)程的棧空間溢出
            一般而言,我們的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)行處理。現(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代碼中增加新的選擇路線(xiàn),程序的代碼很容易就變得臃腫不堪且難以維護(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 線(xiàn)程創(chuàng)建處理子過(guò)程
            handle_thread_exit 線(xiàn)程推出處理子過(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)試一些很特殊的程序是能收到非常好的效果的。可以說(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!

            久久久av波多野一区二区| 精品久久一区二区三区| 精品少妇人妻av无码久久| 青青草原综合久久| 综合网日日天干夜夜久久| 91久久福利国产成人精品| 麻豆AV一区二区三区久久| 久久久久国产一级毛片高清板| 欧美黑人又粗又大久久久| 亚洲精品99久久久久中文字幕| 秋霞久久国产精品电影院| 亚洲国产精品无码久久| 无码乱码观看精品久久| 国产91久久综合| 国产精品久久毛片完整版| 亚洲精品无码久久久影院相关影片| 久久久久亚洲精品天堂久久久久久| A狠狠久久蜜臀婷色中文网| 亚洲国产精品无码久久| 国产精品久久久久蜜芽| 欧美一级久久久久久久大片| 久久激情亚洲精品无码?V| 精品综合久久久久久888蜜芽| 精品综合久久久久久97| 亚洲欧洲久久久精品| 国内精品久久久久久久影视麻豆 | 国产精品热久久毛片| 久久久久久毛片免费播放| 漂亮人妻被黑人久久精品| 国产亚洲美女精品久久久2020| 波多野结衣久久一区二区| 久久夜色精品国产噜噜亚洲a| 欧美久久综合九色综合| 久久免费观看视频| 欧美精品福利视频一区二区三区久久久精品| 久久中文娱乐网| 国产精品久久久久久久久免费| 久久精品国产日本波多野结衣 | 久久成人国产精品| 久久99精品久久久久久hb无码| 久久久久高潮毛片免费全部播放|