調試器編寫第一講,調試器基本框架
作者:IBinary
出處:http://www.cnblogs.com/iBinary/
版權所有,歡迎保留原文鏈接進行轉載:)
今天開始調試器第一講,調試器的基本框架,我們用過很多調試器,比如 WinDbg,OllyDbg,那為什么我們還要自己編寫調試器哪?
原因是,OllyDbg等等的各種調試器都太容易被針對了,寫調試器,主要是理解別人怎么反調試,並且我們怎么在安全開發的時候,讓我們的軟件針對調試器.今天就開始調試器第一講,調試器的基本框架
很多人認為調試器怎么寫,沒思路,其實調試器就是調用API,熟練運用這些API,則可以進行軟件調試
一丶寫調試器注意的問題
首先,我們思考一個問題,我們要調試我們的程序,要怎么讓我們的程序知道被調試了
是這樣的,微軟已經幫我們提供了API了,比如我們常用WriteProcessMemory這個API,想一下,微軟怎么可能提供在別人進程里面寫內存的API哪?
其實這個就是調試器用的,只不過被我們玩壞了.
那么我們MSDN搜索一下這個API,就可以找到所有和調試器相關的API
可以在下方看到,所以和調試器相關的API了.
API就怎么多,熟練運用即可.
二丶調試器API各個API的意思
這里介紹下各個API的意思,並不細講,等到用到的時候才會細講怎么用.主要是熟悉一下,算是翻譯一下API吧.
/* ContinueDebugEvent :看名字就知道,繼續調試事件,意思就是調試程序的時候有事件來,你處理完了要繼續. DebugActiveProcess :調試程序,附加進程,這個API就是,如果我們的程序打開了,那么調用這個API,傳入進程ID是可以附加調試的. DebugActiveProcessStop :停止調試器,調試的指定進程,也就是調試器要停止對某一個進程的調試 debugBreak :如果程序處於調試的狀態,,如果發生斷點異常(下斷點),允許線程,通知我們的調試器來調試,處理這個異常.否則系統接收
DebugBreakProcess :在指定的進程中,產生一個斷點異常
DebugSetProcessKillOnExit :設置調試的線程,退出的時候進行的操作. FatalExit :強制退出調用的進程,並將控制權交給調試器 FlushInstructionCache :刷新指令的高速緩存 GetThreadContext :獲取寄存器的信息,具體獲取那個,要設置標志位,標志位在注釋中(MSDN查不到) GetThreadSelectorEntry :獲取指定選擇器和線程的描述的入口表. IsDebuggerPresent :判斷進程是否在調試器下面運行(和我們前面說的反調試那個差不多,都是判斷調試是否運行) OutPutDebugString :調試輸出字符串. ReadProcessMemory :讀取指定進程的某塊數據 SetThreadContext :設置寄存器 WaitForDebugEvent :等待調試事件 WriteProcessMemory :往指定進程寫某塊數據 */
三丶查閱MSDN,看下調試器的說明
我們要調試一個程序,第一步就要創建這個程序,那么創建程序是用CreateProcess,那么我們看下MSDN有沒有特別說明
隨便進去一個調試的API,看到下方說了一個基本的調試,點擊去看看.
第一個說,關於基本調試,也就是介紹一下
第二個說,調試的參考,我們先看下第一個怎么說把
可以看到,他告訴了我們,關於基本調試個各種步驟.
第一個: 說的是,調試的函數,也就是上面的我們那些調試API
第二個: 說的是,調試的時候,進程,線程,和異常函數的各種特性,也就是說調試進程,線程,還有異常的時候,該怎么做.
第三個: 告訴了我們使用基本的調試函數可以創建一個基本的調試器.這些函數,可以下斷點,異常等等.
第四個: 這個則是告訴了我們,調試程序的時候來的各種事件.
看下第二個把,調試的時候的各種特性.
說的是,進程函數,線程函數,異常函數,我們現在首要任務是調試進程,所以看第一個
告訴了我們,要調試一個進程,你要用CreateProcess,並且要設置標志為上面畫框框的地方.
下面還說了,我們要還可以通過OpenPeocess獲得一個進程ID,通過DebugActiveProcess附加這個進程調試.
那么我們現在知道了,要調試一個程序,首先要創建進程,並且給出特定的參數.
四丶編寫調試器的基本框架(匯編編寫,C/C++一樣編寫)
我們知道了,要調試一個程序,要先創建進程,然后我們應該要等待事件的到來,進行處理,如果是否繼續處理,交給
ContinueDebugEvent
開始編寫程序:
RadAsm創建控制台程序
匯編代碼
1.創建調試進程
LOCAL @si:STARTUPINFO LOCAL @di:PROCESS_INFORMATION mov @si.cb,sizeof STARTUPINFO ;創建調試進程 invoke CreateProcess,offset g_szAppName,NULL,NULL,NULL,FALSE,DEBUG_PROCESS,NULL,NULL,addr @si,addr @di
CTRL + D 調試,看下計算器是否啟動.
2.調用調試函數,等待事件到來.
注重第一個參數,說了一個DEBUG_EVENT,看下這個結構體
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;
說了異常來的時候,這個結構體會有異常的代碼,進程的ID,線程的ID,以及根據不同異常,產生不同的結構體
(因為是共用體,所以什么異常來了,就會有不同的結構體,保存了不同的異常信息)
舉個例子,第一個:
異常信息是EXCEPTION_DEBUG_EVENT 表示調試的時候異常信息事件來了,我們看下它的結構體是什么
typedef struct _EXCEPTION_DEBUG_INFO { EXCEPTION_RECORD ExceptionRecord; DWORD dwFirstChance; } EXCEPTION_DEBUG_INFO, *LPEXCEPTION_DEBUG_INFO;
它的結構體則保存了和異常信息,什么的,(和篩選器異常的結構體差不多.)
上面一個框是一個參數,下面說了,只有線程,被創建調試進程的時候才能用,也就是創建調試進程使用.
下面有例子,抄例子
我就不截圖看了.
直接編寫匯編代碼:
assume ecx:ptr DEBUG_EVENT ;EXCEPTION_DEBUG_EVENT 代表我不處理,DBG_CONTINUE代表我處理,這就是OD的F9運行起來的功能 .while TRUE invoke WaitForDebugEvent,addr @DebugEv,INFINITE ;等待事件到來 lea ecx,@DebugEv ;結果放到ecx當中,因為ecx已經假設為DebugEvent結構體了 mov ebx,[ecx].dwDebugEventCode ;將結構體的異常代碼給ebx,然后下方通過ebx判斷是哪個事件來了 .if ebx == EXCEPTION_DEBUG_EVENT ;檢測事件是什么 invoke crt_puts,offset g_szEcpt mov @dwContinueStatus,EXCEPTION_DEBUG_EVENT .elseif ebx == CREATE_THREAD_DEBUG_EVENT invoke crt_puts,offset g_szEcpt mov @dwContinueStatus,EXCEPTION_DEBUG_EVENT .elseif ebx == CREATE_PROCESS_DEBUG_EVENT invoke crt_puts,offset g_szEcpt mov @dwContinueStatus,EXCEPTION_DEBUG_EVENT .elseif ebx == EXIT_THREAD_DEBUG_EVENT invoke crt_puts,offset g_szEcpt mov @dwContinueStatus,EXCEPTION_DEBUG_EVENT .elseif ebx == EXIT_PROCESS_DEBUG_EVENT mov @dwContinueStatus,EXCEPTION_DEBUG_EVENT .elseif ebx == LOAD_DLL_DEBUG_EVENT mov @dwContinueStatus,EXCEPTION_DEBUG_EVENT .elseif ebx == UNLOAD_DLL_DEBUG_EVENT mov @dwContinueStatus,EXCEPTION_DEBUG_EVENT .elseif ebx == OUTPUT_DEBUG_STRING_EVENT mov @dwContinueStatus,EXCEPTION_DEBUG_EVENT .endif invoke ContinueDebugEvent,[ecx].dwProcessId, [ecx].dwThreadId, ;繼續處理事件 @dwContinueStatus .endw
代碼很簡單,利用waitforDebugEvent獲取異常代碼,然后異常代碼給ebx,通過ebx模擬switch,看看那個異常事件回來.當然,匯編代碼會放到課堂資料中,帶着C代碼一起發布,這里只是簡單解釋一下.
五丶異常事件是什么
上面說了,異常事件和ebx(異常代碼比較)那么分別代表什么意思?
EXCEPTION_DEBUG_EVENT :被調試的調試程序的時候來,會在調試的程序中下一個int3斷點.如果被調試的時候,則回來,屬於系統斷點
CREATE_THREAD_DEBUG_EVENT :被調試的程序創建線程的時候會來
CREATE_PROCESS_DEBUG_EVENT :被調試的程序創建進程的時候會來
EXIT_THREAD_DEBUG_EVENT :被調試的程序退出線程的時候會來
EXIT_PROCESS_DEBUG_EVENT :被調試的程序退出進程的時候會來
LOAD_DLL_DEBUG_EVENT :被調試的程序加載DLL的時候會來
UNLOAD_DLL_DEBUG_EVENT :被調試的程序卸載DLL會來
OUTPUT_DEBUG_STRING_EVENT :被調試的程序調試輸出的時候會來
RIP_EVENT :64位系統的事件,如果編寫32位調試器,這個則不重要.
今天主要是講了一個框架,具體可以回去自己寫一下就明白,很簡單.
課堂資料:
鏈接:http://pan.baidu.com/s/1jHDJOUU 密碼:mbn4
作者:IBinary
出處:http://www.cnblogs.com/iBinary/
版權所有,歡迎保留原文鏈接進行轉載:)