💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
## 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 ``` 如果你重新运行这个脚本,将看到由软件断点的异常处理函数打印的输出结果。我们已经创建了硬件断点和内存断点的处理模型。接下来我们要详细的实现这三种不同类型断点的 处理函数。