穿透Session 0 隔离(二)

     上一篇我们已经对Session 0 隔离有了进一步认识,如果在开发过程中确实需要服务与桌面用户进行交互,可以通过远程桌面服务的API 绕过Session 0 的隔离完成交互操作。

     对于简单的交互,服务可以通过WTSSendMessage 函数,在用户Session 上显示消息窗口。对于一些复杂的UI 交互,必须调用CreateProcessAsUser 或其他方法(WCF、.NET远程处理等)进行跨Session 通信,在桌面用户上创建一个应用程序界面。

WTSSendMessage 函数

     如果服务只是简单的向桌面用户Session 发送消息窗口,则可以使用WTSSendMessage 函数实现。首先,在上一篇下载的代码中加入一个Interop.cs 类,并在类中加入如下代码:

public static IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;    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 函数

     如果想通过服务向桌面用户Session 创建一个复杂UI 程序界面,则需要使用CreateProcessAsUser 函数为用户创建一个新进程用来运行相应的程序。打开Interop 类继续添加下面代码:

public static void CreateProcess(string app, string path)  {      bool result;      IntPtr hToken = WindowsIdentity.GetCurrent().Token;      IntPtr hDupedToken = IntPtr.Zero;        PROCESS_INFORMATION pi = new PROCESS_INFORMATION();      SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();      sa.Length = Marshal.SizeOf(sa);        STARTUPINFO si = new STARTUPINFO();      si.cb = Marshal.SizeOf(si);        int dwSessionID = WTSGetActiveConsoleSessionId();      result = WTSQueryUserToken(dwSessionID, out hToken);            if (!result)      {          ShowMessageBox("WTSQueryUserToken failed", "AlertService Message");      }        result = DuplicateTokenEx(            hToken,            GENERIC_ALL_ACCESS,            ref sa,            (int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,            (int)TOKEN_TYPE.TokenPrimary,            ref hDupedToken         );        if (!result)      {          ShowMessageBox("DuplicateTokenEx failed" ,"AlertService Message");      }        IntPtr lpEnvironment = IntPtr.Zero;      result = CreateEnvironmentBlock(out lpEnvironment, hDupedToken, false);        if (!result)      {          ShowMessageBox("CreateEnvironmentBlock failed", "AlertService Message");      }        result = CreateProcessAsUser(                           hDupedToken,                           app,                           String.Empty,                           ref sa, ref sa,                           false, 0, IntPtr.Zero,                           path, ref si, ref pi);        if (!result)      {          int error = Marshal.GetLastWin32Error();          string message = String.Format("CreateProcessAsUser Error: {0}", error);          ShowMessageBox(message, "AlertService Message");      }        if (pi.hProcess != IntPtr.Zero)          CloseHandle(pi.hProcess);      if (pi.hThread != IntPtr.Zero)          CloseHandle(pi.hThread);      if (hDupedToken != IntPtr.Zero)          CloseHandle(hDupedToken);  }    [StructLayout(LayoutKind.Sequential)]  public struct STARTUPINFO  {      public Int32 cb;      public string lpReserved;      public string lpDesktop;      public string lpTitle;      public Int32 dwX;      public Int32 dwY;      public Int32 dwXSize;      public Int32 dwXCountChars;      public Int32 dwYCountChars;      public Int32 dwFillAttribute;      public Int32 dwFlags;      public Int16 wShowWindow;      public Int16 cbReserved2;      public IntPtr lpReserved2;      public IntPtr hStdInput;      public IntPtr hStdOutput;      public IntPtr hStdError;  }    [StructLayout(LayoutKind.Sequential)]  public struct PROCESS_INFORMATION  {      public IntPtr hProcess;      public IntPtr hThread;      public Int32 dwProcessID;      public Int32 dwThreadID;  }    [StructLayout(LayoutKind.Sequential)]  public struct SECURITY_ATTRIBUTES  {      public Int32 Length;      public IntPtr lpSecurityDescriptor;      public bool bInheritHandle;  }    public enum SECURITY_IMPERSONATION_LEVEL  {      SecurityAnonymous,      SecurityIdentification,      SecurityImpersonation,      SecurityDelegation  }    public enum TOKEN_TYPE  {      TokenPrimary = 1,      TokenImpersonation  }    public const int GENERIC_ALL_ACCESS = 0x10000000;    [DllImport("kernel32.dll", SetLastError = true,      CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]  public static extern bool CloseHandle(IntPtr handle);    [DllImport("advapi32.dll", SetLastError = true,      CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]  public static extern bool CreateProcessAsUser(      IntPtr hToken,      string lpApplicationName,      string lpCommandLine,      ref SECURITY_ATTRIBUTES lpProcessAttributes,      ref SECURITY_ATTRIBUTES lpThreadAttributes,      bool bInheritHandle,      Int32 dwCreationFlags,      IntPtr lpEnvrionment,      string lpCurrentDirectory,      ref STARTUPINFO lpStartupInfo,      ref PROCESS_INFORMATION lpProcessInformation);    [DllImport("advapi32.dll", SetLastError = true)]  public static extern bool DuplicateTokenEx(      IntPtr hExistingToken,      Int32 dwDesiredAccess,      ref SECURITY_ATTRIBUTES lpThreadAttributes,      Int32 ImpersonationLevel,      Int32 dwTokenType,      ref IntPtr phNewToken);    [DllImport("wtsapi32.dll", SetLastError=true)]  public static extern bool WTSQueryUserToken(      Int32 sessionId,       out IntPtr Token);    [DllImport("userenv.dll", SetLastError = true)]  static extern bool CreateEnvironmentBlock(      out IntPtr lpEnvironment,       IntPtr hToken,       bool bInherit);

     在CreateProcess 函数中同时也涉及到DuplicateTokenEx、WTSQueryUserToken、CreateEnvironmentBlock 函数的使用,有兴趣的朋友可通过MSDN 进行学习。完成CreateProcess 函数创建后,就可以真正的通过它来调用应用程序了,回到Service1.cs 修改一下OnStart 我们来打开一个CMD 窗口。如下代码:

protected override void OnStart(string[] args)  {      Interop.CreateProcess("cmd.exe",@"C:\Windows\System32\");  }

     重新编译程序,启动AlertService 服务便可看到下图界面。至此,我们已经可以通过一些简单的方法对Session 0 隔离问题进行解决。大家也可以通过WCF 等技术完成一些更复杂的跨Session 通信方式,实现在Windows 7 及Vista 系统中服务与桌面用户的交互操作。

参考资料

1. WTSSendMessage Function
http://msdn.microsoft.com/en-us/library/aa383842(VS.85).aspx

2. CreateProcessAsUser Function
http://msdn.microsoft.com/en-us/library/ms682429(v=VS.85).aspx

3. WTSSendMessage (wtsapi32)
http://www.pinvoke.net/default.aspx/wtsapi32/WTSSendMessage.html

4. WTSQueryUserToken Function
http://msdn.microsoft.com/en-us/library/aa383840(VS.85).aspx

5. http://www.pinvoke.net/

代码下载

你可能感兴趣的:(穿透Session 0 隔离(二))