ACCESS釜底抽薪vista和win7在windows服务中彼此桌面权限问题:穿透Session 0 隔离

 

 

 

当某某国外大型汽车企业BI项目中,有一个子项目,需要通过大屏幕展示销售报表,程序用活动启动和倒闭。开发人员在开进程中,发现以Win7的service中未能够一直操作UI进程,调查过程中,发现如下相关材料可供参考。

 

原文地址:化解vista和win7在windows服务遭遇相互桌面权限问题:穿透Session 0
隔离

 

 

     服务(Service)对于豪门来说肯定不见面生,它是Windows
操作系统重要之有些。我们可把劳务想像成一种植新鲜的应用程序,它本系统的“开启~关闭”而“开始~停止”其行事内容,在即时之间任需任何用户与。

Windows
服务以后台执行着各式各样任务,支持方咱一般的桌面操作。有时候可能用服务以及用户展开信息或者界面交互操作,这种办法在XP
时代是未曾问题的,但于Vista 开始你见面发觉这种方式如早已非从作用。

Session 0 隔离实验

下面来做一个名叫AlertService
的劳务,它的意图就是是朝着用户发一个唤起对话框,我们看这个服务以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 将那加载到网服务中:

ACCESS 1

每当劳动特性被勾选“Allow service to interact with desktop”
,这样可使AlertService 与桌面用户进行互。

ACCESS 2

以服务管理器中将AlertService 服务“启动”,这时任务栏中会闪动一个图标:

ACCESS 3

点击该图标会显示下面窗口,提示有只次(AlertService)正在准备显示信息,是否用浏览该消息:

ACCESS 4

品点击“View the
message”,便会显示下图界面(其实是界面我既休能够起此时此刻桌面操作截图了,是由此Virtual
PC
截屏的,其原因要继续读)。注意观察可以发现下图的桌面背景已经休是Windows
7 默认的桌面背景了,说明AlertService 与桌面系统的Session
并不相同,这就算是Session 0 隔离作用的结果。

ACCESS 5

Session 0 隔离原理

在Windows XP、Windows Server 2003 或早期Windows
系统时,当第一单用户登录体系后服务以及应用程序是当跟一个Session
中运作的。这便是Session 0 如下图所示:

ACCESS 6

 

 

但是这种运行方式提高了系统安全风险,因为服务是经提升了用户权限运行的,而应用程序往往是那些无拥有管理员身份的普通用户运行的,其中的安危显而易见。

于Vista 开始Session 0 中单独包含系统服务,其他应用程序则透过分离的Session
运行,将服务及应用程序隔离提高系统的安全性。如下图所示:

ACCESS 7

然让Session 0 与外Session
之间无法展开互动,不克通过服务往桌面用户弹来消息窗口、UI
窗口等信息。这为尽管是为何才自己说深图已休克透过时桌面进行截图了。

ACCESS 8

Session 检查

当其实开发过程被,可以由此Process
Explorer 检查服务要程序处于哪个Session,会无会见赶上Session
0 隔离问题。我们当Services 中找到之前加载的AlertService
服务,右键属性查看其Session 状态。

ACCESS 9

可看到AlertService 处于Session 0 中:

ACCESS 10

重复来探Outlook 应用程序:

ACCESS 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 函数中采用,打开Service1.cs 参加下面代码:  
  30. protected override void OnStart(string[] args)  
  31. {  
  32.     Interop.ShowMessageBox(“This a message from AlertService.”,  
  33.                            “AlertService Message”);  
  34. }  

 

 

编译程序后每当劳务管理器中还开动AlertService
服务,从生图中可看消息窗口是当眼前用户桌面显示的,而无是Session 0
中。

ACCESS 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 系统遭到劳动和桌面用户之相互操作。

ACCESS 13

 

相关文章