解决vista和win7在windows服务中互相桌面权限问题:穿透Session 0 隔离

 

 

 

在某国外大型汽车集团BI项目中,有一个子项目,要求通过大显示屏显示销售报表,程序须求活动启动和关闭。开发人士在付出进度中,发现在Win7的service中不可以平素操作UI进度,调查进程中,发现如下相关资料可供参考。

 

初稿地址:杀鸡取蛋vista和win7在windows服务中相互桌面权限问题:穿透Session 0
隔离

 

 

     服务(瑟维斯(Service)(Service))对于我们来说肯定不会陌生,它是Windows
操作系统重要的组成部分。大家可以把劳动想像成一种万分的应用程序,它随系统的“开启~关闭”而“开始~截至”其行事内容,在那里面无需任何用户参与。

Windows
服务在后台执行着各式各种义务,扶助着大家平时的桌面操作。有时候可能要求劳务与用户举行音讯或界面交互操作,那种方法在XP
时代是未曾问题的,但自从Vista 起首你会发现那种措施如同已不起效果。

Session 0 隔离实验

下边来做一个名叫Alert瑟维斯(Service)的服务,它的功能就是向用户暴发一个擢升对话框,大家看看这些服务在Windows
7 中会暴发如何境况。

 

[csharp] view
plain
 copy

 

  1. using System.ServiceProcess;  
  2. using System.Windows.Forms;  
  3.   
  4. namespace AlertService  
  5. {  
  6.     public partial class Service1 : ServiceBase  
  7.     {  
  8.         public Service1()  
  9.         {  
  10.             InitializeComponent();  
  11.         }  
  12.   
  13.         protected override void OnStart(string[] args)  
  14.         {  
  15.             MessageBox.Show(“A message from AlertService.”);  
  16.         }  
  17.   
  18.         protected override void OnStop()  
  19.         {  
  20.         }  
  21.     }  
  22. }  

 

次第编译后通过Installutil 将其加载到系统服务中:

图片 1

在服务特性中勾选“Allow service to interact with desktop”
,这样可以使AlertService 与桌面用户举行相互。

图片 2

在劳务管理器将官AlertService(Service) 服务“启动”,那时职分栏中会闪动一个图标:

图片 3

点击该图标会彰显下边窗口,提示有个程序(Alert瑟维斯(Service)(Service))正在准备展现音信,是或不是要求浏览该音讯:

图片 4

品味点击“View the
message”,便会来得下图界面(其实那个界面我一度不可以从当下桌面操作截图了,是通过Virtual
PC
截屏的,其原因请继续读书)。注意观察可以发现下图的桌面背景已经不是Windows
7 默许的桌面背景了,表明Alert瑟维斯(Service) 与桌面系统的Session
并分裂,那就是Session 0 隔离作用的结果。

图片 5

Session 0 隔离原理

在Windows XP、Windows Server 2003 或早期Windows
系统时代,当首个用户登录种类后服务和应用程序是在同一个Session
中运行的。那就是Session 0 如下图所示:

图片 6

 

 

唯独那种运行方式升高了系统安全风险,因为劳动是通过升高了用户权限运行的,而应用程序往往是这些不享有管理员身份的普通用户运行的,其中的险恶总而言之。

从Vista 开头Session 0 中只包括系统服务,其余应用程序则通过分离的Session
运行,将服务与应用程序隔离提升系统的安全性。如下图所示:

图片 7

如此使得Session 0 与其他Session
之间不可以进行交互,不可能通过服务向桌面用户弹出音信窗口、UI
窗口等音讯。那也就是为啥刚才自家说不行图已经不能由此当前桌面举行截图了。

图片 8

Session 检查

在事实上开发进度中,可以因而Process
Explorer
 检查服务或程序处于哪个Session,会不会遇上Session
0 隔离问题。大家在瑟维斯(Service)(Service)s 中找到之前加载的AlertService服务,右键属性查看其Session 状态。

图片 9

可看到AlertService 处于Session 0 中:

图片 10

再来看看Outlook 应用程序:

图片 11

很明朗在Windows 7
中劳动和应用程序是居于差其他Session,它们之间加隔了一个爱慕墙,在下篇小说上校介绍怎么样通过那堵珍重墙使服务与桌面用户展开互相操作。

 

假使在付出进度中确确实实须求服务与桌面用户展开相互,能够经过远程桌面服务的API
绕过Session 0 的隔断落成交互操作。

对此简易的并行,服务能够透过WTSSendMessage 函数,在用户Session
上展现新闻窗口。对于一些犬牙相错的UI
交互,必须调用CreateProcessAsUser或任何措施(WCF、.NET远程处理等)进行跨Session
通讯,在桌面用户上成立一个应用程序界面。

WTSSendMessage 函数

如若服务只是简单的向桌面用户Session
发送音信窗口,则足以接纳WTSSendMessage
函数已毕。首先,在上一篇下载的代码中投入一个Interop.cs
类,并在类中投入如下代码:

 

[csharp] view
plain
 copy

 

  1. public static IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;  
  2.   
  3. public static void ShowMessageBox(string message, string title)  
  4. {  
  5.     int resp = 0;  
  6.     WTSSendMessage(  
  7.         WTS_CURRENT_SERVER_HANDLE,   
  8.         WTSGetActiveConsoleSessionId(),  
  9.         title, title.Length,   
  10.         message, message.Length,   
  11.         0, 0, out resp, false);  
  12. }  
  13.   
  14. [DllImport(“kernel32.dll”, SetLastError = true)]  
  15. public static extern int WTSGetActiveConsoleSessionId();  
  16.   
  17. [DllImport(“wtsapi32.dll”, SetLastError = true)]  
  18. public static extern bool WTSSendMessage(  
  19.     IntPtr hServer,  
  20.     int SessionId,  
  21.     String pTitle,  
  22.     int TitleLength,  
  23.     String pMessage,  
  24.     int MessageLength,  
  25.     int Style,  
  26.     int Timeout,  
  27.     out int pResponse,  
  28.     bool bWait);  
  29. 在ShowMessageBox 函数中调用了WTSSendMessage 来发送音讯窗口,那样我们就可以在Service 的OnStart 函数中使用,打开瑟维斯(Service)(Service)1.cs 投入上边代码:  
  30. protected override void OnStart(string[] args)  
  31. {  
  32.     Interop.ShowMessageBox(“This a message from AlertService.”,  
  33.                            “AlertService Message”);  
  34. }  

 

 

编译程序后在劳务管理器中重新启航AlertService服务,从下图中可以观望音信窗口是在现阶段用户桌面显示的,而不是Session 0
中。

图片 12

CreateProcessAsUser 函数

若果想通过劳动向桌面用户Session 创制一个复杂UI
程序界面,则要求利用CreateProcessAsUser
函数为用户创立一个新历程用来运作相应的顺序。打开Interop
类继续增进上边代码:

 

[csharp] view
plain
 copy

 

  1. public static void CreateProcess(string app, string path)  
  2. {  
  3.     bool result;  
  4.     IntPtr hToken = WindowsIdentity.GetCurrent().Token;  
  5.     IntPtr hDupedToken = IntPtr.Zero;  
  6.   
  7.     PROCESS_INFORMATION pi = new PROCESS_INFORMATION();  
  8.     SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();  
  9.     sa.Length = Marshal.SizeOf(sa);  
  10.   
  11.     STARTUPINFO si = new STARTUPINFO();  
  12.     si.cb = Marshal.SizeOf(si);  
  13.   
  14.     int dwSessionID = WTSGetActiveConsoleSessionId();  
  15.     result = WTSQueryUserToken(dwSessionID, out hToken);  
  16.       
  17.     if (!result)  
  18.     {  
  19.         ShowMessageBox(“WTSQueryUserToken failed”, “AlertService Message”);  
  20.     }  
  21.   
  22.     result = DuplicateTokenEx(  
  23.           hToken,  
  24.           GENERIC_ALL_ACCESS,  
  25.           ref sa,  
  26.           (int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,  
  27.           (int)TOKEN_TYPE.TokenPrimary,  
  28.           ref hDupedToken  
  29.        );  
  30.   
  31.     if (!result)  
  32.     {  
  33.         ShowMessageBox(“DuplicateTokenEx failed” ,”AlertService Message”);  
  34.     }  
  35.   
  36.     IntPtr lpEnvironment = IntPtr.Zero;  
  37.     result = CreateEnvironmentBlock(out lpEnvironment, hDupedToken, false);  
  38.   
  39.     if (!result)  
  40.     {  
  41.         ShowMessageBox(“CreateEnvironmentBlock failed”, “AlertService Message”);  
  42.     }  
  43.   
  44.     result = CreateProcessAsUser(  
  45.                          hDupedToken,  
  46.                          app,  
  47.                          String.Empty,  
  48.                          ref sa, ref sa,  
  49.                          false, 0, IntPtr.Zero,  
  50.                          path, ref si, ref pi);  
  51.   
  52.     if (!result)  
  53.     {  
  54.         int error = Marshal.GetLastWin32Error();  
  55.         string message = String.Format(“CreateProcessAsUser Error: {0}”, error);  
  56.         ShowMessageBox(message, “AlertService Message”);  
  57.     }  
  58.   
  59.     if (pi.hProcess != IntPtr.Zero)  
  60.         CloseHandle(pi.hProcess);  
  61.     if (pi.hThread != IntPtr.Zero)  
  62.         CloseHandle(pi.hThread);  
  63.     if (hDupedToken != IntPtr.Zero)  
  64.         CloseHandle(hDupedToken);  
  65. }  
  66.   
  67. [StructLayout(LayoutKind.Sequential)]  
  68. public struct STARTUPINFO  
  69. {  
  70.     public Int32 cb;  
  71.     public string lpReserved;  
  72.     public string lpDesktop;  
  73.     public string lpTitle;  
  74.     public Int32 dwX;  
  75.     public Int32 dwY;  
  76.     public Int32 dwXSize;  
  77.     public Int32 dwXCountChars;  
  78.     public Int32 dwYCountChars;  
  79.     public Int32 dwFillAttribute;  
  80.     public Int32 dwFlags;  
  81.     public Int16 wShowWindow;  
  82.     public Int16 cbReserved2;  
  83.     public IntPtr lpReserved2;  
  84.     public IntPtr hStdInput;  
  85.     public IntPtr hStdOutput;  
  86.     public IntPtr hStdError;  
  87. }  
  88.   
  89. [StructLayout(LayoutKind.Sequential)]  
  90. public struct PROCESS_INFORMATION  
  91. {  
  92.     public IntPtr hProcess;  
  93.     public IntPtr hThread;  
  94.     public Int32 dwProcessID;  
  95.     public Int32 dwThreadID;  
  96. }  
  97.   
  98. [StructLayout(LayoutKind.Sequential)]  
  99. public struct SECURITY_ATTRIBUTES  
  100. {  
  101.     public Int32 Length;  
  102.     public IntPtr lpSecurityDescriptor;  
  103.     public bool bInheritHandle;  
  104. }  
  105.   
  106. public enum SECURITY_IMPERSONATION_LEVEL  
  107. {  
  108.     SecurityAnonymous,  
  109.     SecurityIdentification,  
  110.     SecurityImpersonation,  
  111.     SecurityDelegation  
  112. }  
  113.   
  114. public enum TOKEN_TYPE  
  115. {  
  116.     TokenPrimary = 1,  
  117.     TokenImpersonation  
  118. }  
  119.   
  120. public const int GENERIC_ALL_ACCESS = 0x10000000;  
  121.   
  122. [DllImport(“kernel32.dll”, SetLastError = true,  
  123.     CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]  
  124. public static extern bool CloseHandle(IntPtr handle);  
  125.   
  126. [DllImport(“advapi32.dll”, SetLastError = true,  
  127.     CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]  
  128. public static extern bool CreateProcessAsUser(  
  129.     IntPtr hToken,  
  130.     string lpApplicationName,  
  131.     string lpCommandLine,  
  132.     ref SECURITY_ATTRIBUTES lpProcessAttributes,  
  133.     ref SECURITY_ATTRIBUTES lpThreadAttributes,  
  134.     bool bInheritHandle,  
  135.     Int32 dwCreationFlags,  
  136.     IntPtr lpEnvrionment,  
  137.     string lpCurrentDirectory,  
  138.     ref STARTUPINFO lpStartupInfo,  
  139.     ref PROCESS_INFORMATION lpProcessInformation);  
  140.   
  141. [DllImport(“advapi32.dll”, SetLastError = true)]  
  142. public static extern bool DuplicateTokenEx(  
  143.     IntPtr hExistingToken,  
  144.     Int32 dwDesiredAccess,  
  145.     ref SECURITY_ATTRIBUTES lpThreadAttributes,  
  146.     Int32 ImpersonationLevel,  
  147.     Int32 dwTokenType,  
  148.     ref IntPtr phNewToken);  
  149.   
  150. [DllImport(“wtsapi32.dll”, SetLastError=true)]  
  151. public static extern bool WTSQueryUserToken(  
  152.     Int32 sessionId,   
  153.     out IntPtr Token);  
  154.   
  155. [DllImport(“userenv.dll”, SetLastError = true)]  
  156. static extern bool CreateEnvironmentBlock(  
  157.     out IntPtr lpEnvironment,   
  158.     IntPtr hToken,   
  159.     bool bInherit);  

 

在CreateProcess
函数中并且也提到到DuplicateTokenEx、WTSQueryUserToken、CreateEnvironmentBlock
函数的行使,有趣味的朋友可透过MSDN 举行学习。达成CreateProcess
函数创制后,就可以真正的通过它来调用应用程序了,回到Service1.cs
改动一下OnStart 我们来开辟一个CMD 窗口。如下代码:

 

[csharp] view
plain
 copy

 

  1. protected override void OnStart(string[] args)  
  2. {  
  3.     Interop.CreateProcess(“cmd.exe”,@”C:\Windows\System32\”);  
  4. }  

 

重新编译程序,启动AlertService服务便可寓目下图界面。至此,大家早已得以透过有些概括的办法对Session 0
隔离问题展开缓解。我们也可以通过WCF 等技术成功部分更扑朔迷离的跨Session
通讯方式,达成在Windows 7 及Vista 系统中劳动与桌面用户的竞相操作。

图片 13

 

相关文章