软件调试
调试事件的处理
调试器如何对调试事件进行处理?
- 关联(把调试进程和被调试进程关联起来)
- 调试循环
代码测试:
cpp
#include<iostream>
#include<Windows.h>
#define DEBUGGR_PROCESS L"C:\\Users\\BananaLi\\Desktop\\其他软件\\dbgview64.exe"
int main() {
BOOL nIsContinue = TRUE;
DEBUG_EVENT debugEvent = { 0 };
BOOL bRet = TRUE;
// 1.创建调试进程
STARTUPINFO startUpInfo = { 0 };
PROCESS_INFORMATION pInfo = { 0 };
GetStartupInfo(&startUpInfo);
bRet = CreateProcess(DEBUGGR_PROCESS, NULL, NULL, NULL, TRUE, DEBUG_PROCESS || DEBUG_ONLY_THIS_PROCESS, NULL, NULL, &startUpInfo, &pInfo);
if (bRet == FALSE) {
printf("创建调试进程失败,错误码:%d\n",GetLastError());
return 0;
}
// 调试循环(主框架)
while (nIsContinue) {
// 2.等待调试事件
bRet = WaitForDebugEvent(&debugEvent, INFINITE);
if (bRet == FALSE) {
printf("等待调试事件失败,错误码:%d\n", GetLastError());
return 0;
}
switch (debugEvent.dwDebugEventCode) {
case EXCEPTION_DEBUG_EVENT: {
printf("捕获到异常事件\n");
// 处理异常
break;
}
case CREATE_PROCESS_DEBUG_EVENT: {
printf("创建进程事件\n");
break;
}
case EXIT_PROCESS_DEBUG_EVENT: {
printf("退出进程事件\n");
nIsContinue = FALSE;
break;
}
case CREATE_THREAD_DEBUG_EVENT: {
printf("创建线程事件\n");
break;
}
case EXIT_THREAD_DEBUG_EVENT: {
printf("退出线程事件\n");
break;
}
case LOAD_DLL_DEBUG_EVENT: {
printf("加载DLL事件\n");
break;
}
case UNLOAD_DLL_DEBUG_EVENT: {
printf("卸载DLL事件\n");
break;
}
default:
break;
// 让被调试程序继续运行
bRet = ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, DBG_EXCEPTION_NOT_HANDLED);
}
}
return 0;
}
可以看到已经为我们捕获到了调试事件

通过上面的打印信息可以发现,进程在一创建时,就会把创建进程的事件添加到事件链表中,紧接着就是一堆DLL的加载事件,然后又创建了几个线程,最后发现还捕获到了一个异常事件,这个异常事件是谁触发的呢?
我们打印一下捕获到的异常信息结构体
cpp
...
case EXCEPTION_DEBUG_EVENT: {
printf("捕获到异常事件\n");
printf("异常代码:%llx\n", debugEvent.u.Exception.ExceptionRecord.ExceptionCode);
printf("异常地址:%llx\n", debugEvent.u.Exception.ExceptionRecord.ExceptionAddress);
printf("异常参数个数:%llx\n", debugEvent.u.Exception.ExceptionRecord.NumberParameters);
// 处理异常
break;
}
...

我们用dbg看一下这个地址
系统断点
在创建进程时
- LdrInitializeThunk
- LdrpInitializeProcess
- DbgBreakPoint()方法会写入一个int3,这个在以调试模式打开时才会写入
以附加的形式调试进程
cpp
#include<iostream>
#include<Windows.h>
#define DEBUGGR_PROCESS 29132
int main() {
BOOL nIsContinue = TRUE;
DEBUG_EVENT debugEvent = { 0 };
BOOL bRet = TRUE;
// 1.创建调试进程
STARTUPINFO startUpInfo = { 0 };
PROCESS_INFORMATION pInfo = { 0 };
GetStartupInfo(&startUpInfo);
//bRet = CreateProcess(DEBUGGR_PROCESS, NULL, NULL, NULL, TRUE, DEBUG_PROCESS || DEBUG_ONLY_THIS_PROCESS, NULL, NULL, &startUpInfo, &pInfo);
//if (bRet == FALSE) {
// printf("创建调试进程失败,错误码:%d\n",GetLastError());
// return 0;
//}
if (!DebugActiveProcess(DEBUGGR_PROCESS)) {
return 0;
}
// 调试循环(主框架)
while (nIsContinue) {
// 2.等待调试事件
bRet = WaitForDebugEvent(&debugEvent, INFINITE);
if (bRet == FALSE) {
printf("等待调试事件失败,错误码:%d\n", GetLastError());
return 0;
}
switch (debugEvent.dwDebugEventCode) {
case EXCEPTION_DEBUG_EVENT: {
printf("捕获到异常事件\n");
printf("异常代码:%llx\n", debugEvent.u.Exception.ExceptionRecord.ExceptionCode);
printf("异常地址:%llx\n", debugEvent.u.Exception.ExceptionRecord.ExceptionAddress);
printf("异常参数个数:%llx\n", debugEvent.u.Exception.ExceptionRecord.NumberParameters);
// 处理异常
break;
}
case CREATE_PROCESS_DEBUG_EVENT: {
printf("创建进程事件\n");
break;
}
case EXIT_PROCESS_DEBUG_EVENT: {
printf("退出进程事件\n");
nIsContinue = FALSE;
break;
}
case CREATE_THREAD_DEBUG_EVENT: {
printf("创建线程事件\n");
break;
}
case EXIT_THREAD_DEBUG_EVENT: {
printf("退出线程事件\n");
break;
}
case LOAD_DLL_DEBUG_EVENT: {
printf("加载DLL事件\n");
break;
}
case UNLOAD_DLL_DEBUG_EVENT: {
printf("卸载DLL事件\n");
break;
}
default:
break;
}
// 让被调试程序继续运行
bRet = ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, DBG_EXCEPTION_NOT_HANDLED);
}
return 0;
}

可以发现是一样的,但是有一个疑问,我们之前在打开一个进程进行调试时,当前进程的创建,加载DLL,这些我们能捕获到这没问题,但是我们附加这个进程时,为什么也能捕获到当前被调试进程的创建,加载DLL这些事件呢?答案是这是假的杜撰调试事件消息
要保证调试器无论是新打开一个进程还是附加的形式进行调试,我都能获取到所有的调试事件