windows内核研究(软件调试-调试事件的处理)

软件调试


调试事件的处理

调试器如何对调试事件进行处理?

  • 关联(把调试进程和被调试进程关联起来)
  • 调试循环

代码测试:

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这些事件呢?答案是这是假的杜撰调试事件消息

要保证调试器无论是新打开一个进程还是附加的形式进行调试,我都能获取到所有的调试事件

相关推荐
qwertyuiop_i11 天前
windows内核研究(驱动开发之内核编程)
驱动开发·内核开发·windows内核研究
qwertyuiop_i14 天前
windows内核研究(驱动开发-0环与3环的通信)
驱动开发·windows内核研究·3环程序与0环驱动通信
捕鲸叉6 个月前
Linux/C/C++下怎样进行软件性能分析(CPU/GPU/Memory)
c++·软件调试·软件验证
捕鲸叉6 个月前
在Windows/Linux/MacOS C++程序中打印崩溃调用栈和局部变量信息
c++·软件调试·软件验证·软件诊断
捕鲸叉6 个月前
MacOS/C/C++下怎样进行软件性能分析(CPU/GPU/Memory)
软件调试·软件验证
捕鲸叉7 个月前
怎样在Linux PC上调试另一台PC的内核驱动程序,以及另一台Arm/Linux上的程序和驱动程序
linux·arm开发·软件调试·诊断调试
捕鲸叉7 个月前
在MacOS上怎样远程调试PC的内核驱动程序
macos·软件调试·诊断调试
捕鲸叉7 个月前
Linux/MacOS中如何远程调试C/C++程序
linux·macos·软件调试
mahuifa9 个月前
C++(Qt)软件调试---内存泄漏分析工具MTuner (25)
c++·qt·内存泄漏·软件调试·mtuner