调试事件

调试事件是在正在调试的过程中发生的事件,导致系统通知调试器。 调试事件包括创建进程、创建线程、加载动态链接库(DLL)、卸载 DLL、发送输出字符串以及生成异常。

如果在调试器等待某个调试器时发生调试事件,系统会使用描述该事件的信息填充由 WaitForDebugEvent 指定的 DEBUG_EVENT 结构。

当系统通知调试器的调试事件时,它还会挂起受影响进程中的所有线程。 在调试器继续使用 ContinueDebugEvent继续调试事件之前,线程不会恢复执行。 在调试进程时,可能会发生以下调试事件。

调试事件 描述
CREATE_PROCESS_DEBUG_EVENT
每当在正在调试的进程中创建新进程或每当调试器开始调试已处于活动状态的进程时生成。 系统在进程开始在用户模式下执行之前生成此调试事件,并在系统为新进程生成任何其他调试事件之前生成此调试事件。
DEBUG_EVENT 结构包含 CREATE_PROCESS_DEBUG_INFO 结构。 此结构包括新进程的句柄、进程的映像文件的句柄、进程的初始线程的句柄,以及描述新进程的其他信息。
进程的句柄具有PROCESS_VM_READ和PROCESS_VM_WRITE访问权限。 如果调试器对这些类型的线程具有访问权限,则可以使用 ReadProcessMemoryWriteProcessMemory 函数读取和写入进程的内存。 如果系统以前报告了EXIT_PROCESS_DEBUG_EVENT事件,则当调试器调用 ContinueDebugEvent 函数时,系统将关闭此句柄。
进程的映像文件的句柄具有GENERIC_READ访问权限,并已打开进行读取共享。 调试器在处理CREATE_PROCESS_DEBUG_EVENT时应关闭此句柄。
进程初始线程的句柄具有对线程THREAD_GET_CONTEXT、THREAD_SET_CONTEXT和THREAD_SUSPEND_RESUME访问权限。 如果调试器对这些类型的线程具有访问权限,则可以通过使用 GetThreadContextSetThreadContext 函数读取和写入线程,并且可以使用 SuspendThreadResumeThread 函数来暂停和恢复线程。 如果系统以前报告了EXIT_PROCESS_DEBUG_EVENT事件,则当调试器调用 ContinueDebugEvent 函数时,系统将关闭此句柄。
CREATE_THREAD_DEBUG_EVENT
每当在正在调试的进程中创建新线程或每当调试器开始调试已处于活动状态的进程时生成。 在新线程开始在用户模式下执行之前生成此调试事件。
DEBUG_EVENT 结构包含 CREATE_THREAD_DEBUG_INFO 结构。 此结构包括新线程的句柄和线程的起始地址。 句柄具有对线程的THREAD_GET_CONTEXT、THREAD_SET_CONTEXT和THREAD_SUSPEND_RESUME访问权限。 如果调试器对这些类型的线程具有访问权限,则可以通过使用 GetThreadContextSetThreadContext 函数读取和写入线程,并且可以使用 SuspendThreadResumeThread 函数来暂停和恢复线程。
如果系统之前报告了EXIT_THREAD_DEBUG_EVENT事件,则当调试器调用 ContinueDebugEvent 函数时,系统将关闭新线程的句柄。
EXCEPTION_DEBUG_EVENT
每当在正在调试的进程中发生异常时生成。 可能的异常包括尝试访问不可访问的内存、执行断点指令、尝试除以零或 结构化异常处理中记录的任何其他异常。
DEBUG_EVENT 结构包含 EXCEPTION_DEBUG_INFO 结构。 此结构描述导致调试事件的异常。
除了标准异常条件外,控制台进程调试期间还可以执行其他异常代码。 当 CTRL+C 输入到处理 CTRL+C 信号并正在调试的控制台进程中时,系统将生成DBG_CONTROL_C异常代码。 此异常代码不应由应用程序处理。 应用程序不应使用异常处理程序来处理它。 仅出于调试器的利益而引发,并且仅在调试器附加到控制台进程时使用。
如果未调试进程,或者调试器通过未处理的DBG_CONTROL_C异常(通过 gn 命令)传递,则会搜索应用程序的处理程序函数列表,如 SetConsoleCtrlHandler 函数所述。
如果调试器处理DBG_CONTROL_C异常(通过 gh 命令),则应用程序不会注意到 CTRL+C,除非在类似代码中。
while ((inputChar = getchar()) != EOF) ...
因此,调试器不能用于停止此类代码中的读取等待终止。
EXIT_PROCESS_DEBUG_EVENT
每当正在调试的进程中的最后一个线程退出时生成。 此调试事件在系统卸载进程的 DLL 并更新进程的退出代码后立即发生。
DEBUG_EVENT 结构包含指定退出代码的 EXIT_PROCESS_DEBUG_INFO 结构。
调试器在收到此调试事件时解除分配与进程关联的任何内部结构。 系统将调试器的句柄关闭到退出的进程和所有进程的线程。 调试器不应关闭这些句柄。
在接收此事件调用的调试器 ContinueDebugEvent之前,无法完成进程关闭的内核模式部分。 在此之前,进程句柄处于打开状态,并且虚拟地址空间未释放,因此调试器可以检查子进程。 若要在进程关闭的内核模式部分完成时接收通知,请复制使用 CREATE_PROCESS_DEBUG_EVENT 返回的句柄,调用 ContinueDebugEvent,然后等待重复的进程句柄发出信号。
EXIT_THREAD_DEBUG_EVENT
每当作为正在调试的进程一部分的线程退出时生成。 系统在更新线程退出代码后立即生成此调试事件。
DEBUG_EVENT 结构包含指定退出代码的 EXIT_THREAD_DEBUG_INFO 结构。
如果退出线程是进程的最后一个线程,则不会发生此调试事件。 在这种情况下,将改为发生EXIT_PROCESS_DEBUG_EVENT调试事件。
调试器在收到此调试事件时解除分配与线程关联的任何内部结构。 系统将调试器的句柄关闭到退出的线程。 调试器不应关闭此句柄。
LOAD_DLL_DEBUG_EVENT
每当正在调试的进程加载 DLL 时生成。 当系统加载程序解析指向 DLL 的链接或调试的进程使用 LoadLibrary 函数时,会发生此调试事件。 此调试事件仅在系统首次将 DLL 附加到进程的虚拟地址空间时发生。
DEBUG_EVENT 结构包含 LOAD_DLL_DEBUG_INFO 结构。 此结构包括新加载的 DLL 的句柄、DLL 的基址以及描述 DLL 的其他信息。 调试器应在处理LOAD_DLL_DEBUG_EVENT时关闭 DLL 句柄。
通常,调试器会在收到此调试事件时加载与 DLL 关联的符号表。
OUTPUT_DEBUG_STRING_EVENT
在被调试的进程使用 时生成
OutputDebugString 函数。 DEBUG_EVENT 结构包含 OUTPUT_DEBUG_STRING_INFO 结构。 此结构指定调试字符串的地址、长度和格式。
UNLOAD_DLL_DEBUG_EVENT
每当正在调试的进程使用 FreeLibrary 函数卸载 DLL 时生成。 此调试事件仅在上次从进程的地址空间(即 DLL 的使用计数为零时)卸载 DLL 时发生。
DEBUG_EVENT 结构包含 UNLOAD_DLL_DEBUG_INFO 结构。 此结构指定卸载 DLL 的进程地址空间中的 DLL 基址。
通常,调试器在收到此调试事件时卸载与 DLL 关联的符号表。
进程退出时,系统会自动卸载进程的 DLL,但不生成UNLOAD_DLL_DEBUG_EVENT调试事件。
RIP_EVENT
每当被调试的进程在系统调试器的控制之外时生成。
DEBUG_EVENT 结构包含 RIP_INFO 结构。 此结构指定错误和错误类型。