## 3.3 實現調試事件處理
為了讓我們的調試器能夠針對特定的事件采取相應的行動,我們必須給所有調試器能夠 捕捉到的調試事件,編寫處理函數。回去看看 WaitForDebugEvent() 函數,每當它捕捉到一 個調試事件的時候,就返回一個填充好了的 DEBUG_EVENT 結構。之前我們都忽略掉這個 結構,直接讓進程繼續執行下去,現在我們要用存儲在結構里的信息決定如何處理調試事件。 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;
};
```
在這個結構中有很多有用的信息。dwDebugEventCode 是最重要的,它表明了是什么事 件被 WaitForDebugEvent() 捕捉到了。同時也決定了,在聯合(union )u 里存儲的是什么類型 的值。u 里的變量由 dwDebugEventCode 決定,一一對應如下:
| Event Code | Event Code Value | Union u Value |
| --- | --- | --- |
| 0x1 | EXCEPTION_DEBUG_EVENT | u.Exception |
| 0x2 | CREATE_THREAD_DEBUG_EVENT | u.CreateThread |
| 0x3 | CREATE_PROCESS_DEBUG_EVENT | u.CreateProcessInfo |
| 0x4 | EXIT_THREAD_DEBUG_EVENT | u.ExitThread |
| 0x5 | EXIT_PROCESS_DEBUG_EVENT | u.ExitProcess |
| 0x6 | LOAD_DLL_DEBUG_EVENT | u.LoadDll |
| 0x7 | UNLOAD_DLL_DEBUG_EVENT | u.UnloadDll |
| 0x8 | OUPUT_DEBUG_STRING_EVENT | u.DebugString |
| 0x9 | RIP_EVENT | u.RipInfo |
Table 3-1:調試事件
通過觀察 dwDebugEventCode 的值,再通過上面的表就能找到與之相對應的存儲在 u 里 的變量。讓我們修改調試循環,通過獲得的事件代碼的值,顯示當前發生的事件信息。用這些信息,我們能夠了解到調試器啟動或者附加一個線程后的整個流程。繼續更新 my_debugger.py 和 our my_test.py 腳本。
```
#my_debugger.py
...
class debugger():
def init (self):
self.h_process = None
self.pid = None
self.debugger_active = False
self.h_thread = None
self.context = None
...
def get_debug_event(self):
debug_event = DEBUG_EVENT()
continue_status= DBG_CONTINUE
if kernel32.WaitForDebugEvent(byref(debug_event),INFINITE):
# Let's obtain the thread and context information
self.h_thread = self.open_thread(debug_event.dwThread)
self.context = self.get_thread_context(self.h_thread)
print "Event Code: %d Thread ID: %d" % (debug_event.dwDebugEventCode, debug_event.dwThre
kernel32.ContinueDebugEvent(
debug_event.dwProcessId,
debug_event.dwThreadId,
continue_status )
#my_test.py
import my_debugger
debugger = my_debugger.debugger()
pid = raw_input("Enter the PID of the process to attach to: ")
debugger.attach(int(pid))
debugger.run()
debugger.detach()
```
如果你用的是 calc.exe,輸出將如下所示:
```
Enter the PID of the process to attach to: 2700
Event Code: 3 Thread ID: 3976
Event Code: 6 Thread ID: 3976
Event Code: 6 Thread ID: 3976
Event Code: 6 Thread ID: 3976
Event Code: 6 Thread ID: 3976
Event Code: 6 Thread ID: 3976
Event Code: 6 Thread ID: 3976
Event Code: 6 Thread ID: 3976
Event Code: 6 Thread ID: 3976
Event Code: 6 Thread ID: 3976
Event Code: 2 Thread ID: 3912
Event Code: 1 Thread ID: 3912
Event Code: 4 Thread ID: 3912
```
Listing 3-2: 當附加到 cacl.exe 時的事件代碼
基于腳本的輸出,我們能看到 CREATE_PROCESS_EVENT (0x3)事件是第一個發生的, 接 下 來 的 是 一 堆 的 LOAD_DLL_DEBUG_EVENT (0x6) 事 件 , 然 后 CREATE_THREAD_DEBUG_EVENT (0x2) 創 建 一 個 新 線 程 。 接 著 就 是 一 個 EXCEPTION_DEBUG_EVENT (0x1)例外事件,它由 windows 設置的斷點所引發的,允許在 進程啟動前觀察進程的狀態。最后一個事件是 EXIT_THREAD_DEBUG_EVENT (0x4),它 由進程 3912 結束只身產生。
例外事件是非常重要,例外可能包括斷點,訪問異常,或者內存訪問錯誤(例如嘗試寫 到一個只讀的內存區)。所有這些都很重要,但是讓我們捕捉先捕捉第一個 windows 設置的 斷點。打開 my_debugger.py 加入以下代碼:
```
#my_debugger.py
...
class debugger():
def init (self):
self.h_process = None
self.pid = None
self.debugger_active = False
self.h_thread = None
self.context = None
self.exception = None
self.exception_address = None
...
def get_debug_event(self):
debug_event = DEBUG_EVENT()
continue_status= DBG_CONTINUE
if kernel32.WaitForDebugEvent(byref(debug_event),INFINITE):
# Let's obtain the thread and context information
self.h_thread = self.open_thread(debug_event.dwThreadId)
self.context = self.get_thread_context(self.h_thread)
print "Event Code: %d Thread ID: %d" % (debug_event.dwDebugEventCode, debug_event.dwThreadId)
# If the event code is an exception, we want to
# examine it further.
if debug_event.dwDebugEventCode == EXCEPTION_DEBUG_EVENT:
# Obtain the exception code
exception = debug_event.u.Exception.ExceptionRecord.ExceptionCod
self.exception_address = debug_event.u.Exception.ExceptionRecord.ExceptionAdd
if exception == EXCEPTION_ACCESS_VIOLATION:
print "Access Violation Detected."
# If a breakpoint is detected, we call an internal
# handler.
elif exception == EXCEPTION_BREAKPOINT:
continue_status = self.exception_handler_breakpoint()
elif ec == EXCEPTION_GUARD_PAGE:
print "Guard Page Access Detected."
elif ec == EXCEPTION_SINGLE_STEP:
print "Single Stepping."
kernel32.ContinueDebugEvent(
debug_event.dwProcessId,
debug_event.dwThreadId,
continue_status )
...
def exception_handler_breakpoint():
print "[*] Inside the breakpoint handler."
print "Exception Address: 0x%08x" % self.exception_address
return DBG_CONTINUE
```
如果你重新運行這個腳本,將看到由軟件斷點的異常處理函數打印的輸出結果。我們已經創建了硬件斷點和內存斷點的處理模型。接下來我們要詳細的實現這三種不同類型斷點的 處理函數。
- 序
- 1 搭建開發環境
- 1.1 操作系統準備
- 1.2 獲取和安裝 Python2.5
- 1.3 配置 Eclipse 和 PyDev
- 2 調試器設計
- 2.1 通用 CPU 寄存器
- 2.2 棧
- 2.3 調試事件
- 2.4 斷點
- 3 自己動手寫一個 windows 調試器
- 3.2 獲得 CPU 寄存器狀態
- 3.3 實現調試事件處理
- 3.4 全能的斷點
- 4 PyDBG---純 PYTHON 調試器
- 4.1 擴展斷點處理
- 4.2 處理訪問違例
- 4.3 進程快照
- 5 IMMUNITY----最好的調試器
- 5.1 安裝 Immunity 調試器
- 5.2 Immunity Debugger 101
- 5.3 Exploit 開發
- 5.4 搞定反調試機制
- 6 HOOKING
- 6.1 用 PyDbg 實現 Soft Hooking
- 6.2 Hard Hooking
- 7 Dll 和代碼注入
- 7.1 創建遠線程
- 7.2 邪惡的代碼
- 8 FUZZING
- 8.1 Bug 的分類
- 8.2 File Fuzzer
- 8.3 改進你的 Fuzzer
- 9 SULLEY
- 9.1 安裝 Sulley
- 9.2 Sulley primitives
- 9.3 獵殺 WarFTPD
- 10 Fuzzing Windows 驅動
- 10.1 驅動通信
- 10.2 用 Immunity fuzzing 驅動
- 10.4 構建 Driver Fuzzer
- 11 IDAPYTHON --- IDA 腳本
- 11.1 安裝 IDAPython
- 11.2 IDAPython 函數
- 11.3 腳本例子
- 12 PyEmu
- 12.1 安裝 PyEmu
- 12.2 PyEmu 一覽
- 12.3 IDAPyEmu