## 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