vista和win7在windows服务中交互桌⾯权限问题解决⽅
法:穿透Session0隔离
Windows 服务在后台执⾏着各种各样任务,⽀持着我们⽇常的桌⾯操作。有时候可能需要服务与⽤户进⾏信息或界⾯交互操作,这种⽅式在XP 时代是没有问题的,但⾃从Vista 开始你会发现这种⽅式似乎已不起作⽤。
Session 0 隔离实验
下⾯来做⼀个名叫AlertService 的服务,它的作⽤就是向⽤户发出⼀个提⽰对话框,我们看看这个服务在Windows 7 中会发⽣什么情况。
using System.ServiceProcess;
using System.Windows.Forms;
namespace AlertService
{
public partial class Service1 : ServiceBase
{
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
MessageBox.Show("A message from AlertService.");
}
protected override void OnStop()
win7管理员身份运行
{
}
}
}
程序编译后通过Installutil 将其加载到系统服务中:
在服务属性中勾选“Allow service to interact with desktop” ,这样可以使AlertService 与桌⾯⽤户进⾏交互。
在服务管理器中将AlertService 服务“启动”,这时任务栏中会闪动⼀个图标:
点击该图标会显⽰下⾯窗⼝,提⽰有个程序(AlertService)正在试图显⽰信息,是否需要浏览该信息:
尝试点击“View the message”,便会显⽰下图界⾯(其实这个界⾯我已经不能从当前桌⾯操作截图了,是通过Virtual PC 截屏的,其原因请继续阅读)。注意观察可以发现下图的桌⾯背景已经不是Windows 7 默认的桌⾯背景了,说明AlertService 与桌⾯系统的Session 并不相同,这就是Session 0 隔离作⽤的结果。
Session 0 隔离原理
在Windows XP、Windows Server 2003 或早期Windows 系统时代,当第⼀个⽤户登录系统后服务和应⽤程序是在同⼀个Session 中运⾏的。这就是Session 0 如下图所⽰:
但是这种运⾏⽅式提⾼了系统安全风险,因为服务是通过提升了⽤户权限运⾏的,⽽应⽤程序往往是那些不具备管理员⾝份的普通⽤户运⾏的,其中的危险显⽽易见。
从Vista 开始Session 0 中只包含系统服务,其他应⽤程序则通过分离的Session 运⾏,将服务与应⽤程序隔离提⾼系统的安全性。如下图所⽰:
这样使得Session 0 与其他Session 之间⽆法进⾏交互,不能通过服务向桌⾯⽤户弹出信息窗⼝、UI 窗⼝等信息。这也就是为什么刚才我说那个图已经不能通过当前桌⾯进⾏截图了。
Session 检查
在实际开发过程中,可以通过检查服务或程序处于哪个Session,会不会遇到Session 0 隔离问题。我们在Services 中到之前加载的AlertService 服务,右键属性查看其Session 状态。
可看到AlertService 处于Session 0 中:
再来看看Outlook 应⽤程序:
很明显在Windows 7 中服务和应⽤程序是处于不同的Session,它们之间加隔了⼀个保护墙,在下篇⽂章中将介绍如何穿过这
堵保护墙使服务与桌⾯⽤户进⾏交互操作。
如果在开发过程中确实需要服务与桌⾯⽤户进⾏交互,可以通过远程桌⾯服务的API 绕过Session 0 的隔离完成交互操作。
对于简单的交互,服务可以通过WTSSendMessage 函数,在⽤户Session 上显⽰消息窗⼝。对于⼀些复杂的UI 交互,必须调⽤CreateProcessAsUser或其他⽅法(WCF、.NET远程处理等)进⾏跨Session 通信,在桌⾯⽤户上创建⼀个应⽤程序界⾯。
WTSSendMessage 函数
如果服务只是简单的向桌⾯⽤户Session 发送消息窗⼝,则可以使⽤WTSSendMessage 函数实现。⾸先,在上⼀篇下载的代码中加⼊⼀个Interop.cs 类,并在类中加⼊如下代码:
public static void ShowMessageBox(string message, string title)
{
int resp = 0;
WTSSendMessage(
WTS_CURRENT_SERVER_HANDLE,
WTSGetActiveConsoleSessionId(),
title, title.Length,
message, message.Length,
0, 0, out resp, false);
}
[DllImport("kernel32.dll", SetLastError = true)]
public static extern int WTSGetActiveConsoleSessionId();
[DllImport("wtsapi32.dll", SetLastError = true)]
public static extern bool WTSSendMessage(
IntPtr hServer,
int SessionId,
String pTitle,
int TitleLength,
String pMessage,
int MessageLength,
int Style,
int Timeout,
out int pResponse,
bool bWait);
在ShowMessageBox 函数中调⽤了WTSSendMessage 来发送信息窗⼝,这样我们就可以在Service 的OnStart 函数中使⽤,打开Service1.cs 加⼊下⾯代码:
protected override void OnStart(string[] args)
{
Interop.ShowMessageBox("This a message from AlertService.",
"AlertService Message");
}
编译程序后在服务管理器中重新启动AlertService 服务,从下图中可以看到消息窗⼝是在当前⽤户桌⾯显⽰的,⽽不是Session 0 中。
CreateProcessAsUser 函数
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论