Session 0隔离

Windows 7的GA发布日期依然是10月22日,不到一个月的时间了,这意味着您的应用程序应该已经针对Windows 7做好了准备。在针对Windows 7准备您的应用程序的同时,请一定要验证您的程序在版本检查UAC数据重定向方面不存在问题。本文Session 0隔离介绍的则是另一个值得您注意的,可能导致应用程序兼容性问题的因素,尤其是在您的应用程序包含服务的情况下。如果您的服务在Windows Vista下工作正常,那么通常在Windows 7中也将可以正常使用(但您依然需要在Windows 7下对您的应用程序进行彻底的测试)。然而,如果在Windows Vista下就没有进行妥善的兼容性测试,那么建议您花些时间阅读本文。

首先简单介绍一下服务是什么吧。

服务是什么?

服务是Microsoft Windows操作系统的一个组成机制,您可以将服务想像成一种忽略当前用户上下文环境的“特殊的应用程序”。服务与“普通”的用户应用程序有很大不同,主要是因为您可以配置让服务在系统启动(引导)后就自动运行,直到系统关闭才结束,而并不需要用户登录。也就是说,就算没有用户登录,服务业可以正常运行。

我们也可以把服务看作是不需要用户的干预,直接在后台运行的“任务”。Windows上的服务需要负责所有不需要用户参与的后台活动,例如远程过程调用(RPC)服务,以及打印池服务,甚至还有网络位置感知服务等。

问题在哪里?

有些服务可能需要在用户界面上显示对话框,或需要与用户的应用程序通讯,这种类型的功能“通常”属于Windows XP服务,因为在Windows XP中,这样做很容易。如果您的服务恰好需要显示某些用户界面对象,例如对话框,或者需要与应用程序通讯,则在Windows 7下运行可能会遇到问题。

如果在Windows 7上运行需要显示对话框的服务,此时并不能看到所需的对话框,您将在任务栏上看到一个闪烁的图标。而且,如果单击这个闪烁图标,还会看到一个对话框。更具体来说,在Windows 7中运行时,您的服务可能会遇到下列一个或多个现象,这个服务可能会:

  • 正常运行,但无法完成既定工作,同时耗费大量CPU时钟和内存。
  • 正常运行,但其他进程无法与该服务通讯,该服务业无法与用户或其他应用程序/服务通讯。
  • 尝试通过Windows消息机制与用户应用程序通讯,但Windows消息机制无法实现目标。
  • 在任务栏上显示一个闪烁的图标,代表该服务希望与桌面交互。

上述所有现象都意味着,您的服务遇到了Windows 7服务的Session 0隔离问题,也就是说,服务和用户应用程序之间存在“物理上的”分离,但这只是冰山一角。首先,让我们先来定义您的服务在Windows 7下可能遇到的两种问题类型:

  • 服务无法显示UI,或显示替代UI(例如烦人的闪烁对话框):当服务尝试显示用户界面元素(哪怕该服务允许与桌面交互)时,替代层会使用下图所示的检测到交互式服务对话这样的对话框提示用户,这个通知对话框会显示在用户的桌面上。如果用户单击以查看该“信息”,显示内容会切换到安全模式下,虽然用户可以选择是否查看服务的UI对话框,但这种做法会打断工作流,导致严重的应用程序兼容性问题。另外,某些用户可能并不希望有对话框阻碍您的服务/应用程序获取用户的输入,并影响应用程序的正常执行。
  • **服务和应用程序所共享的对象变得不可见或无法访问:**当服务所创建的对象由标准应用程序(使用标准用户特权运行)访问时,在全局命名空间中无法找到该对象(也就是说,该对象是Session 0专用的)。这意味着其他应用程序将无法从全局命名空间访问所谓的“共享对象”,而更严格来说,无法直接从Session 0访问。另外,安全性上的某些变动可能会导致另一种情况,就算该对象是可见的,但也无法访问。这可能会影响其他进程(例如标准用户应用程序)与您的服务交互,自然也就妨碍了应用程序的执行。

很明显,Session 0隔离可能会导致一些非常严重的兼容性问题,因此本文将向您介绍用于判断您的服务是否存在该问题的相关方法,以及问题的解决方法。然而这里需要提醒您,将用户应用程序与服务隔离的主要原因是为了让恶意软件更加难以使用提升后的特权运行,因为提权后运行的恶意软件将比标准用户权限下运行时造成更大的破坏,这些内容会在下一节介绍。而正是因为这样才产生了越来越安全的Windows操作系统。

原因:Windows 7服务的Session 0隔离

在Windows XP、Windows Server 2003,以及更老版本的Windows操作系统中,服务和应用程序使用相同的会话(Session)运行,而这个会话是由第一个登录到控制台的用户启动的。该会话就叫做Session 0,如下图所示,在Windows Vista之前,Session 0不仅包含服务,也包含标准用户应用程序。

将服务和用户应用程序一起在Session 0中运行会导致安全风险,因为服务会使用提升后的权限运行,而用户应用程序使用用户特权(大部分都是非管理员用户)运行,这会使得恶意软件以某个服务为攻击目标,通过“劫持”该服务,达到提升自己权限级别的目的。

从Windows Vista开始,只有服务可以托管到Session 0中,用户应用程序和服务之间会被隔离,并需要运行在用户登录到系统时创建的后续会话中。例如第一个登录的用户创建 Session 1,第二个登录的用户创建Session 2,以此类推,如下图所示。

使用不同会话运行的实体(应用程序或服务)如果不将自己明确标注为全局命名空间,并提供相应的访问控制设置,将无法互相发送消息,共享UI元素,或共享内核对象。这一过程如下图所示:

有关Session 0隔离对Windows Vista中服务和驱动的影响的详细信息,请参考https://www.microsoft.com/whdc/system/vista/services.mspx ,该文也适用于Windows 7。

如何确定自己的服务是否会遇到上述某些问题?

至此,我们已经介绍了Windows服务的Session 0隔离相关的现象,并且解释了服务隔离的概念,同时介绍了这种机制会对您的服务和应用程序有何影响。下面是为了了解您的实际问题,以及着手解决之前,您应该进行的测试和其他操作。

测试1 – 验证服务(或任何其他进程)的会话分配

  1. 启动Process Explorer。
    1. 要下载Process Explorer或了解详细信息,请访问Microsoft TechNet上的Process Explorer网站。
  2. 确保Process Explorer显示了所有进程:
    1. 单击File 。
    2. 选择Show Details from All Processes。
  3. 找到第一个csrss.exe进程,这是一项服务,位于System Idle Process下方(见下图),查看其属性:
    1. 用鼠标右键单击该进程。
    2. 选择Properties。
    3. 打开Security选项卡。
    4. 记下该服务所运行的会话(通常是Session 0),及其完整性级别。
  4. 找到第二个csrss.exe进程,该进程位于Wininit.exe下方(见下图),按照上文介绍的步骤查看其属性:

    下面左图所示的是使用System级别Session 0运行的csrss.exe实例的进程属性,而下面右图所示的是以相同System级别运行,但位于不同会话,Session 2中的csrss.exe实例的进程属性:

    如果您的服务运行于Session 0之下,并且使用了System完整性级别,那么就无法直接显示UI。同时在与该服务共享内核对象或文件时也会遇到一定的问题。

测试2 – 确保对象的可访问性

  1. 启动Process Explorer。
  2. 确保Process Explorer显示了所有进程:
     1. 单击File
     2. 选择Show Details from All Processes
  3. 找到目标服务。
  4. 如果该服务包含了您已知需要与用户应用程序共享的对象,请在下方Handles窗格(按下Ctrl-H可打开,或者可从View菜单下打开)中打开对应的句柄。
    1. 用鼠标右键单击每个怀疑的句柄,然后选择Properties
    2. 打开Security选项卡即可看到允许访问被该句柄所引用的对象的用户和组。

下图所示的是一个每个人都能访问(“Synchronize”权限)的共享对象的例子,哪怕这是一个运行于Session 0的系统服务,也不会受到限制。

下图所示的则是只能被管理员以及SYSTEM组访问的共享对象的例子:

现在您知道了问题的起因,那么应该如何解决?

最难理解的部分已经说完了,知道并了解了这些内容后,您可能确定自己遇到了Session 0问题,其实解决起来也很简单。

这里就列出了几个用于解决此类问题的思路:

  • 如果服务需要通过发送消息的方式与用户交互,请使用WTSSendMessage函数。在功能上,该函数几乎与MessageBox相同,对于不需要过于完整的UI的服务,这是一种简单易行的方法,而且足够安全,因为所显示的信息框无法用于夺取对底层服务的控制。
  • 如果您的服务需要更完整的UI,请使用CreateProcessAsUser函数在发起请求的用户的桌面上创建进程。但是这里需要注意,您依然需要在新创建的进程和原始服务之间进行通讯,因此这里就需要用到下一点内容。
  • 如果需要双向交互,请使用Windows Communication Foundation(WCF),这是一种用于.NET Remoting命名管线或任何其他进程间通讯(IPC)的机制(Windows消息除外),可用于实现跨会话的通讯。如果需要提升,WCF和Remoting是一种比直接提升用户(假设UAC没有过关闭)更安全的做法。
  • 请确保需要跨会话共享的内核对象使用了带有Global\字符串的名称前缀,该字符串意味着这个对象属于会话全局(session-global)命名空间。

参考资源

有关该话题的详细信息,请参考Windows 7 Training Kit for Developers,其中包含了详细的白皮书和上手实验室。如果需要,您可以只下载Session 0隔离上手实验室。

下面列出了有关本文用到的工具的简单介绍:

Process Explorer– 用于监控Windows进程的工具,可显示进程完整性级别和对象安全信息。

有关该问题和其他问题的更多信息,还可以参考Channel 9的Windows 7话题

有关Windows 7的技术信息和上手体验,请下载Windows 7 Training Kit for Developers,在这里还可了解很多其他问题。