Win32 调试接口设计与实现浅析 [3] 创建调试子系统

原创 2004年07月07日 20:05:00

http://www.blogcn.com/user8/flier_lu/index.html?id=1618917

   在本系列文章的前两篇文章中,简要地介绍了 Win32 调试接口中用户态调试器结构和调试事件的相关知识

     Win32 调试接口设计与实现浅析 [1] 用户态调试器结构初探
     Win32 调试接口设计与实现浅析 [2] 调试事件

     在这一小节中,将进一步展开分析 NT 系统核心中对上述功能提供支持的调试子系统的创建过程。

     从前面两个小节我们可以了解到,Win32 调试接口对用户态调试器来说,实际上绝大多数工作都是通过一个调试界面端口"DbgUiApiPort"完成的。用户态调试器通过此端口完成对调试子系统的挂接,进而接收和处理调试事件。因此,对调试子系统的分析也将从此端口开始。

     首先来看看调试子系统以及调试界面和调试服务端口的创建过程。

     在《Windows 2000 内部揭密》的第四章中,Solomon详细地介绍了 NT 系统启动的整个过程。其中SMSS (Session Manager) 是启动程序 NTLDR 载入运行的第一个本机(Native)应用程序(不使用 Win32 子系统的 API),其被作为操作系统一部分受到信任,完成系统初始化工作。Win32 子系统 CSRSS(Client-Server Runtime SubSystem)和系统登陆进程 WinLogon 在 SMSS 初始化工作完成后被载入执行,以实际完成接受用户登陆运行的工作。其中 SMSS 系统初始化的工作就包括对调试子系统的初始化。一个启动后的进程树实例如下:
 

以下为引用:

 System(4)
     smss.exe(388)
         csrss.exe(436)
         winlogon.exe(460)
             services.exe(504)
             lsass.exe(516)
 



     smss.exe的入口函数(smserversmss.c:28)首先检查从 NTLDR 通过命令行传入的参数中是否有调试参数,如果有则将之分析后放入 SmpDebug 全局变量(smserversmsrvp.h:82)中;然后调用 SmpInit 函数(smserversminit.c:683)初始化Session Manager。
     SmpInit 函数在完成初始化工作、构造 SMSS 服务端口 "SmApiPort" 和两个用于处理向 SMSS 发送服务请求的线程后,会调用 SmpLoadDataFromRegistry 函数(smserversminit.c:934)从注册表 HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlSession Manager 键中载入 Session Manager 的相关参数。
     Session Manager配置注册表键下SubSystems子键的Required、Optional和Kmode三个键,定义了系统支持的子系统类型。通常情况下,Required包括Debug和Windows子系统;Optional包括可选的Posix子系统;Kmode定义核心子系统Windows在核心态的实现win32k.sys。而子系统名字又进一步指向实现子系统的可执行文件映象。一个典型的设置如下:
 

以下为引用:

 HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlSession ManagerSubSystems

 Required = "Debug Windows"
 Optional = "Posix"

 Kmode = "%SystemRoot%system32win32k.sys"

 Debug = ""

 Windows = "%SystemRoot%system32csrss.exe ObjectDirectory=Windows SharedSection=1024,3072,512 Windows=On SubSystemType=Windows ServerDll=basesrv,1 ServerDll=winsrv:UserServerDllInitialization,3 ServerDll=winsrv:ConServerDllInitialization,2 ProfileControl=Off MaxRequestThreads=16"

 Posix = "%SystemRoot%system32psxss.exe"
 



     其中Required中包括的子系统,SMSS将自动载入并初始化之。值得注意的是SubSystems子键中的Debug与Windows、Posix等其他子系统不同,并没有指向实际的可执行文件。因为调试子系统是由 SmpLoadDataFromRegistry 函数根据子系统名字是否为 debug,判断在调用执行载入子系统命令的 SmpExecuteCommand 函数(smserversminit.c:3235)时,是否带 SMP_DEBUG_FLAG 标志,表示当前需要载入的是调试子系统。而 SmpExecuteCommand 函数入口处一旦发现标志参数包含 SMP_DEBUG_FLAG 标志,就立刻调用 SmpLoadDbgSs 函数(smserversmdbg.c:108)实际载入调试子系统并直接返回,不再进一步解析和执行命令。

     完整的初始化调试子系统函数调用流程如下:
 

以下为引用:

   main                    (smserversmss.c:28)
   SmpInit                 (smserversminit.c:683)
   SmpLoadDataFromRegistry (smserversminit.c:934)
   SmpExecuteCommand       (smserversminit.c:3235)
   SmpLoadDbgSs            (smserversmdbg.c:108)
 



     Win2003下处理子系统载入的部分代码被放入一个独立的 SmpLoadSubSystemsForMuSession 函数中,而 debug 子系统则改为在每个Session中载入。也就是说传入 SmpExecuteCommand 函数的 SMP_DEBUG_FLAG 标志会导致此函数直接退出。<TBD>

   NT 下 SmpLoadDbgSs 函数中分别对调试子系统中用户态和核心态调试时间响应端口和处理线程做了初始化。伪代码如下:
 

以下为引用:

 NTSTATUS SmpLoadDbgSs(IN PUNICODE_STRING DbgSsName)
 {
   NTSTATUS st = DbgpInit(); // 初始化用户态调试器环境

   if(!NT_SUCCESS(st)) return st;

   st = DbgSsInitialize(...); // 初始化核心态调试器环境

   SmpDbgSsLoaded = TRUE; // 调试子系统已经成功载入

   return STATUS_SUCCESS;
 }
 



     DbgInit 函数(smserverdbginit.c:26)中首先完成对应用程序线程Hash表的初始化;然后构造一个具有所有访问权限的安全描述符;使用此安全描述符创建两个LPC端口对象"DbgSsApiPort"和"DbgUiApiPort",分别被用户态调试器用于连接调试服务和调试界面;最后创建两个线程分别处理这两个端口上的调试事件,线程由 DbgpSsApiLoop 函数(smserverdbgloop.c:123)和 DbgpUiApiLoop 函数(smserverdbgloop.c:288)完成实际事件处理工作。

     DbgSsInitialize 函数(ntosdlldllssstb.c:429)中则先建立与用户态调试器的调试服务端口的链接;然后初始化核心态调试器调试服务的相关全局变量;最后创建用户线程使用DbgSspSrvApiLoop函数(ntosdlldllssstb.c:737)处理核心调试事件。
     其中核心态调试器的相关实现本节暂且不涉及,等后面具体讨论核心态调试器的原理时再详细分析。

     下一节中将详细分析调试子系统中调试服务端口和调试界面端口的事件处理线程工作流程,以及如何与用户态调试器配合完成调试工作。

操作系统进程实验课程设计

题目:操作系统——进程实验 姓名:郑兆涵                         学校:烟台大学                      专业:计算机科学与技术(嵌入式方...
  • zzh_569754126
  • zzh_569754126
  • 2016年05月10日 22:00
  • 4295

使用ASP.Net WebAPI构建REST服务(一)——简单的示例

使用ASP.Net WebAPI构建REST服务(一)——简单的示例 由于给予REST的Web服务非常简单易用,它越来越成为企业后端服务集成的首选方法。本文这里介绍一下如何通过微软的Asp....
  • mengzhengjie
  • mengzhengjie
  • 2016年09月01日 10:43
  • 2465

VS2012 win32控制台调试总结

Visual studio2012 win32控制台开发
  • East_Man
  • East_Man
  • 2015年01月18日 13:49
  • 1519

Win32 核心 DPC 设计思想和实现思路浅析

Win32 核心 DPC 设计思想和实现思路浅析 http://www.blogcn.com/user8/flier_lu/index.html?id=1397656&run=.09D4C2F ...
  • wzsy
  • wzsy
  • 2011年08月26日 16:01
  • 927

创建libusb-win32编译环境,及快速调试方法

作者:ancheel 转自:http://www.amobbs.com/thread-4096300-1-1.html
  • whw8007
  • whw8007
  • 2014年08月28日 09:42
  • 2101

[Win32]一个调试器的实现(二)调试事件的处理

上一篇文章说到了调试循环的写法,这回讲一下调试器应该如何处理各种调试事件。   RIP_EVENT 关于这种调试事件的文档资料非常少,即使提到也只是用“系统错误”或者“内部错误”一笔带过。既然如...
  • lixiangminghate
  • lixiangminghate
  • 2016年09月23日 13:22
  • 469

[Win32]一个调试器的实现(五)调试符号

一个调试器应该可以跟踪被调试程序执行到了什么地方,显示下一条将要执行的语句,显示各个变量的值,设置断点,进行单步执行等等,这些功能都需要一个基础设施的支持,那就是调试符号。   什么是调试符号 ...
  • lixiangminghate
  • lixiangminghate
  • 2016年09月25日 21:54
  • 1268

[Win32]一个调试器的实现(二)调试事件的处理

上一篇文章说到了调试循环的写法,这回讲一下调试器应该如何处理各种调试事件。   RIP_EVENT 关于这种调试事件的文档资料非常少,即使提到也只是用“系统错误”或者“内部错误”一笔带过。既然如...
  • zhangyanquen
  • zhangyanquen
  • 2012年12月10日 21:52
  • 359

[Win32]一个调试器的实现(一)调试事件与调试循环

前言 程序员离不开调试器,它可以动态显示程序的执行过程,对于解决程序问题有极大的帮助。如果你和我一样对调试器的工作原理很感兴趣,那么这一系列文章很适合你,这些文章记录了我开发一个调试器雏形的过程...
  • ymzhou117
  • ymzhou117
  • 2012年10月08日 21:19
  • 661

[Win32]一个调试器的实现(一)调试事件与调试循环

来源网址:http://www.cnblogs.com/zplutor/archive/2011/03/04/1971279.html 作者:Zplutor's   前言 程序员离不开调试器,...
  • oBuYiSeng
  • oBuYiSeng
  • 2015年12月02日 15:21
  • 208
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Win32 调试接口设计与实现浅析 [3] 创建调试子系统
举报原因:
原因补充:

(最多只允许输入30个字)