windows内核研究(软件调试-调试事件的处理)
软件调试
调试事件的处理
调试器如何对调试事件进行处理?
- 关联(把调试进程和被调试进程关联起来)
- 调试循环
代码测试:
#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的加载事件,然后又创建了几个线程,最后发现还捕获到了一个异常事件,这个异常事件是谁触发的呢?
我们打印一下捕获到的异常信息结构体
...
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,这个在以调试模式打开时才会写入
以附加的形式调试进程
#include<iostream>
#include<Windows.h>
#define DEBUGGR_PROCESS 29132int 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这些事件呢?答案是这是假的杜撰调试事件消息
要保证调试器无论是新打开一个进程还是附加的形式进行调试,我都能获取到所有的调试事件