[Win32]一个调试器的实现(二)调试事件的处理
来源:互联网 发布:js字符串格式化时间 编辑:程序博客网 时间:2024/05/22 03:06
来源网址:http://www.cnblogs.com/zplutor/archive/2011/03/06/1972540.html
作者:Zplutor's
上一篇文章说到了调试循环的写法,这回讲一下调试器应该如何处理各种调试事件。
RIP_EVENT
关于这种调试事件的文档资料非常少,即使提到也只是用“系统错误”或者“内部错误”一笔带过。既然如此,我们也不需要对其进行什么处理,只要输出一条信息或者干脆忽略它即可。
OUTPUT_DEBUG_STRING_EVENT
当被调试进程调用OutputDebugString时就会引发该类调试事件,OUTPUT_DEBUG_STRING_INFO结构体描述了关于该事件的详细信息。在MSDN中,对该结构体各字段的解释是:lpDebugStringData字段是字符串在被调试进程的进程空间内的地址;nDebugStringLength字段是以字符为单位的字符串的长度;fUnicode指示字符串是否Unicode编码的。根据我个人的实验观察,发现只有第一个字段的解释是对的。实际上,无论调用OutputDebugStringA还是OutputDebugStringW,字符串都会以ANSI编码来表示。如果是调用OutputDebugStringW,那么会先将字符串转换成ANSI编码之后再调用OutputDebugStringA(这个过程在MSDN内有描述)。所以fUnicode的值永远都是0,而nDebugStringLength是以字节为单位的字符串长度,而不是以字符为单位。
既然字符串是在被调试进程的地址空间内,我们就要使用ReadProcessMemory函数读取这个字符串。下面的代码展示了这个过程:
void OnOutputDebugString(const OUTPUT_DEBUG_STRING_INFO* pInfo) { BYTE* pBuffer = (BYTE*)malloc(pInfo->nDebugStringLength); SIZE_T bytesRead; ReadProcessMemory( g_hProcess, pInfo->lpDebugStringData, pBuffer, pInfo->nDebugStringLength, &bytesRead); int requireLen = MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, (LPCSTR)pBuffer, pInfo->nDebugStringLength, NULL, 0); TCHAR* pWideStr = (TCHAR*)malloc(requireLen * sizeof(TCHAR)); MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, (LPCSTR)pBuffer, pInfo->nDebugStringLength, pWideStr, requireLen); std::wcout << TEXT("Debuggee debug string: ") << pWideStr << std::endl; free(pWideStr); free(pBuffer);}
g_hProcess是类型为HANDLE的全局变量。在启动被调试进程之后就将它的句柄赋给了g_hProcess。
LOAD_DLL_DEBUG_EVENT
加载一个DLL模块之后引发该类调试事件,LOAD_DLL_DEBUG_INFO结构体描述了它的详细信息。lpImageName这个字段可能会使你想在调试器中输出DLL的文件名,然而这行不通。MSDN上的解释是,lpImageName的值是文件名字符串在被调试进程的进程空间内的地址,但是这个值可能为NULL,即使不为NULL,通过ReadProcessMemory读取到的内容也可能是NULL。所以,想通过这个字段获取DLL的文件名并不可靠。
那么,通过hFile字段来获取文件名如何?没有Windows API可以直接通过文件句柄获取文件名,想要这么做的话必须绕一个大圈子,详细的方法请参考:http://blog.csdn.net/bodybo/archive/2006/08/28/1131346.aspx。
实际上hFile是与dwDebugInfoFileOffset和nDebugInfoSize一起使用的,用于获取DLL文件的调试信息。一般情况下我们不需要这么做,所以只要调用CloseHandle关闭这个句柄即可。记住!关闭这个句柄非常重要,如果不这么做的话会引起资源泄漏。
我的想法是,先通过EnumProcessModules枚举被调试进程的模块,然后通过GetModuleInformation获取模块的基地址,将这个基地址与LOAD_DLL_DEBUG_INFO结构体的lpBaseOfDll字段进行比较,如果相等的话就通过GetModuleFileNameEx获取DLL的文件名。可是我在实验这个方法的时候EnumProcessModules总是返回FALSE,GetLastError返回299,这是什么原因呢?
这可能是因为当调试器在处理这类调试事件时,被调试进程还没有启动完毕,所需要的模块还未全部加载完成,所以无法获取它的模块信息。
UNLOAD_DLL_DEBUG_EVENT
卸载一个DLL模块的时候引发该类调试事件。一般情况下只要输出一条信息或者忽略它即可。
CREATE_PROCESS_DEBUG_EVENT
创建进程之后的第一个调试事件,CREATE_PROCESS_DEBUG_INFO结构体描述了该类调试事件的详细信息。该结构体有三个字段是句柄,分别是hFile,hProcess和hThread,同样要记得使用CloseHandle关闭它们!
EXIT_PROCESS_DEBUG_EVENT
被调试进程结束时引发此类调试事件,EXIT_PROCESS_DEBUG_INFO结构体描述了它的详细信息。或许你能做的只有输出dwExitCode这个字段的值。
CREATE_THREAD_DEBUG_EVENT
创建一个线程之后引发此类调试事件,CREATE_THREAD_DEBUG_INFO结构体描述了它的详细信息。同样要记住用CloseHandle关闭hThread字段!
EXIT_THREAD_DEBUG_EVENT
一个线程结束之后引发此类调试事件,EXIT_THREAD_DEBUG_INFO结构体描述了它的详细信息。对此同样也只能输出dwExitCode的值。
EXCEPTION_DEBUG_EVENT
发生异常时引发此类调试事件,EXCEPTION_DEBUG_INFO结构体描述了它的详细信息。对这种调试事件的处理是最麻烦的,因为异常的种类非常多,对每种异常的处理也不相同。另外,此类调试事件也是实现断点和单步执行的关键。
由于关于这类调试事件的篇幅太多,因此将其放到下一篇文章中讲解。
示例代码
这次的示例代码基本上跟上次的示例代码基本上一样,只是添加了本文中描述的内容,改动非常有限,所以如果觉得没必要的话就不要下载了。
http://files.cnblogs.com/zplutor/MiniDebugger2.rar
- [Win32]一个调试器的实现(二)调试事件的处理
- [Win32]一个调试器的实现(二)调试事件的处理
- [Win32]一个调试器的实现(二)调试事件的处理
- [Win32]一个调试器的实现(一)调试事件与调试循环
- [Win32]一个调试器的实现(一)调试事件与调试循环
- [Win32]一个调试器的实现(一)调试事件与调试循环
- [Win32]一个调试器的实现(一)调试事件与调试循环
- [Win32]一个调试器的实现(一)调试事件与调试循环
- [Win32]一个调试器的实现(五)调试符号
- [Win32]一个调试器的实现(五)调试符号
- [Win32]一个调试器的实现(三)异常
- [Win32]一个调试器的实现(六)显示源代码
- [Win32]一个调试器的实现(七)断点
- [Win32]一个调试器的实现(九)符号模型
- [Win32]一个调试器的实现(十)显示变量
- [Win32]一个调试器的实现(三)异常
- [Win32]一个调试器的实现(五)
- Win32]一个调试器的实现(六)显示源代码
- WebKit(WKNavigationDelegate)
- MyBatis学习之路(1)_环境搭建
- python数据库编程
- Data Structure(2)---算法时间复杂度
- 云之讯官方测试Demo音频版源码阅读
- [Win32]一个调试器的实现(二)调试事件的处理
- EasyUI validatebox 自定义验证
- NiftyNotification (Android Toast替代品)
- 关于数据修复
- SVN使用总结
- Android动画效果生动有趣的通知NiftyNotification(Android Toast替代品)
- Ubuntu配色方案
- 关于字符串是否是给定的乔姆斯基范式生成【DP+乔姆斯基范式理解。。。】
- 122 js 关闭子窗口刷新夫窗口 validate新增规则 新增比较时间规则