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)处理核心调试事件。
     其中核心态调试器的相关实现本节暂且不涉及,等后面具体讨论核心态调试器的原理时再详细分析。

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

Win32 调试接口设计与实现浅析

所谓调试器实际上是一个很宽泛的概念,凡是能够以某种形式监控其他程序执行过程的程序,都可以泛称为调试器。在Windows平台上,根据调试器的实现原理大概可以将之分为三类:内核态调试器、用户态调试器和伪代...
  • xpzhang
  • xpzhang
  • 2008年12月21日 17:11
  • 1400

(转载)Win32 调试接口设计与实现浅析

  所谓调试器实际上是一个很宽泛的概念,凡是能够以某种形式监控其他程序执行过程的程序,都可以泛称为调试器。在Windows平台上,根据调试器的实现原理大概可以将之分为三类:内核态调试器、用户态调试器和...
  • Kendiv
  • Kendiv
  • 2005年04月27日 19:15
  • 2520

Win32 调试接口设计与实现浅析

 所谓调试器实际上是一个很宽泛的概念,凡是能够以某种形式监控其他程序执行过程的程序,都可以泛称为调试器。在Windows平台上,根据调试器的实现原理大概可以将之分为三类:内核态调试器、用户态调试器和伪...
  • whf727
  • whf727
  • 2009年11月09日 18:29
  • 1010

Win32 调试接口设计与实现浅析 [2] 调试事件

http://flier_lu.blogone.net/?id=1324316[2] 调试事件    前面说到 Win32 下的用户态调试器实际上就是一个while循环,循环体内先等待一个调试事件,然...
  • flier_lu
  • flier_lu
  • 2004年04月05日 22:53
  • 985

win32 编程之调试输出

在windows下编程时,不能再使用printf了,需要使用sprintf、wsprintf,或者采用MessageBox弹出对话框来输出相应的值。 #include int CDECL Mess...
  • fansofjava
  • fansofjava
  • 2017年04月17日 15:00
  • 325

Win32调试API学习心得(一)

    最近学习了一下WIN32的调试API,并做了一个简单的调试器,略有心得,特写出来希望对需要的朋友有所帮助.参考资料:lczlion:>               彭春华:>概述:   Win...
  • pankun
  • pankun
  • 2003年08月24日 15:33
  • 1603

VS2012 win32控制台调试总结

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

OpenRisc-36-ORPSoC整体架构分析

引言 之前,我们简单分析了ORPSoC的clock子系统,debug子系统,对FPGA的上电,复位,配置,初始化,用户模式等FPGA准备过程有了初步了解。 此外对ORPSoC的启动过程,包括程序的烧写...
  • rill_zhen
  • rill_zhen
  • 2013年07月19日 16:33
  • 3757

Win32编程调试信息的输出(Console)

这几天做项目的时候想实时的输出调试信息。最先想到的就是Console。但是遇到了一个问题,关闭Console时整个程序退出了。在网上查了查,在这里总结一下。 Win32项目里创建标准的输出窗体函数A...
  • likui360
  • likui360
  • 2015年07月08日 18:32
  • 936

Win32调试API(3)

理论:如果你以前使用过调试器,那么你应对跟踪比较熟悉。当"跟踪"一个程序时,程序在每执行一条指令后将会停止,这使你有机会去检查寄存器/内存中的值。这种单步运行的官方定义为跟踪(tracing)。单步运...
  • Civet148
  • Civet148
  • 2007年05月06日 20:10
  • 1235
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Win32 调试接口设计与实现浅析 [3] 创建调试子系统
举报原因:
原因补充:

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