Home  >  Article  >  Database  >  读《软件调试》第九章

读《软件调试》第九章

WBOY
WBOYOriginal
2016-06-07 15:56:081045browse

今日读了张银奎老师的《软件调试》,前面的CPU和硬件相关的部分离得比较远,所以从第九章操作系统读起,今天的读书笔记: 9.2采集调试消息 调试事件分为8种 typedef enum _DBGKM_APINUMBER { DbgkmExceptionApi = 0, // 异常 DbgkmCreateThreadApi = 1, //

今日读了张银奎老师的《软件调试》,前面的CPU和硬件相关的部分离得比较远,所以从第九章操作系统读起,今天的读书笔记:

9.2采集调试消息
调试事件分为8种
typedef enum _DBGKM_APINUMBER
{
DbgkmExceptionApi = 0, // 异常
DbgkmCreateThreadApi = 1, // 创建线程
DbgkmCreateProcessApi = 2, // 创建进程
DbgkmExitThreadApi = 3, // 退出线程
DbgkmExitProcessApi = 4, // 进程退出
DbgkmLoadDllApi = 5, // 映射DLL
DbgkmUnloadDllApi = 6, // 反映射DLL
DbgkmErrorReportApi = 7, // 内部错误
DbgkmMaxApiNumber = 8, // 这组常量的最大值
} DBGKM_APINUMBER;


9.2.2 进程和线程创建消息
操作系统就支持向调试系统发送消息,这个我是没有想到的,具体过程如下:
创建用户态windows线程时,首先为线程建立必要的内核对象和数据结构,并分配栈(stack)空间,这些工作完成后,
该线程处于挂起状态(CREATE_SUSPEND), 而后进程管理器会通知环境子系统,环境子系统会作必要的设置和登记,最后
进程管理器会调用PspUserThreadStartup例程,准备启动该线程。
为了支持调试,PspUserThreadStartup总是会调用调试子系统的内核函数DbgkCreateThread,以便让调试子系统得到处理机会。


DbgkCreateThread会检查新创建线程所在的进程是否正在被调试(根据DebugPort是否为空),如果不是,便立即返回,
如果是,则会继续检查该进程的用户态运行时间(UserTime)是否为0,目的是判断该线程是否是进程中的第一个线程,如果是,
则通过DbgkSendApiMessage()函数向DebugPort发送DbgkmCreateProcessApi消息,如果不是,
则发送DbgkmCreateThreadApi消息。
调试器收到的进程创建(CREATE_PROCESS_DEBUG_EVENT,值为3)和线程创建(CREATE_THREAD_DEBUG_EVENT,值为2)事件就是源于这两个消息。


9.2.3 进程和线程退出消息 --- 与上面类似


9.2.4 模块映射和反映射消息
DLL(Dynamic-link Library)是Windows中使用最多的技术之一。如:
Windows内核文件NTOSKRNL.EXE虽然是EXE后缀,其实质是一个DLL;
NTDLL.DLL是连接用户态和操作系统内核的桥梁,用户态代码通过它访问内核服务;
Windows子系统DLL(KERNEL32.DLL,ADVAPI32.DLL,USER32.DLL,GDI32.DLL)是Windows API的载体;


观察进程中的DLL:
1.运行notepad.exe
2.启动VC6,通过Build>Start Debug>Attatch to Process...菜单弹出Attach Process对话框,然后选择notepad.
3. 通过Debug>Modules...菜单弹出模块列表,便可以看到notepad进程中的DLL了。
第二列是该模块在进程空间中的地址(虚拟地址,均小于0x80000000),可见这些模块都是位于用户空间中的。


存在于多个进程空间中的DLL,是否会重复占用内存?
否!当LoadLibrary()和LoadLibraryEx() API加载一个DLL时,会首先判断该DLL是否已经加载过,如果是,则不会重复加载,
只是将该DLL对应的内存页面映射(map)到目标进程的内存空间,并把该DLL的引用次数加1.
当进程退出或调用FreeLibrary() API要卸载一个DLL时,Windows会从进程的虚拟内存空间中把该DLL的映射删除(unmap),
并递减该DLL的引用次数,如果引用次数变为0,那么该DLL会被彻底移出内存。


9.2.5 异常消息
为了支持调试,系统会把被调试程序中发生的所有异常发送给调试器。
内核中KiDispatchException函数是分发异常的枢纽,它会给每个异常安排最多两轮被处理的机会,
对于每一轮处理机会,它都会调用调试子系统的DbgkForwardException函数来通知调试子系统。


总结:
系统的进程管理器、内存管理器和异常分发函数会调用调试子系统的Dbgk采集例程,来向调试子系统通报调试消息,
这些例程被调用后会根据当前进程的DebugPort字段来判断当前进程是否处于被调试状态。
如果不是,便忽略这次调用,直接返回;
如果是,便产生一个DBGKM_APIMSG结构,然后调用下一节将介绍的DbgkSendApiMessage函数来发送调试消息。

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn