穿透Session 0 隔离(二)

转载 2013年12月03日 17:51:50

原地址:http://www.cnblogs.com/gnielee/archive/2010/04/08/1707169.html

     上一篇我们已经对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)
            {
                AutoRun.SetLog("DuplicateTokenEx 失败");
                //ShowMessageBox("DuplicateTokenEx failed", "AlertService Message");
            }

            IntPtr lpEnvironment = IntPtr.Zero;
            result = CreateEnvironmentBlock(out lpEnvironment, hDupedToken, false);

            if (!result)
            {
                AutoRun.SetLog("CreateEnvironmentBlock 失败");
                //ShowMessageBox("CreateEnvironmentBlock failed", "AlertService Message");
            }

            result = CreateProcessAsUser(
                                 hDupedToken,
                                 path + 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);
                AutoRun.SetLog(message);
                //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);

        [DllImport("kernel32.dll")]
        public static extern int WTSGetActiveConsoleSessionId();

     在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/

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

服务(Service)对于大家来说一定不会陌生,它是Windows 操作系统重要的组成部分。我们可以把服务想像成一种特殊的应用程序,它随系统的“开启~关闭”而“开始~停止”其工作内容,在这期间无需任何...
  • whatday
  • whatday
  • 2014年11月05日 12:57
  • 1270

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

文章来源:http://www.cnblogs.com/therock/articles/2261371.html 服务(Service)对于大家来说一定不会陌生,它是Windows 操作系统重要的...
  • daiafei
  • daiafei
  • 2015年02月06日 16:32
  • 1018

windows7及以上如何穿透Session0隔离?

服务(Service)对于大家来说一定不会陌生,它是Windows 操作系统重要的组成部分。我们可以把服务想像成一种特殊的应用程序,它随系统的“开启~关闭”而“开始~停止”其工作内容,在这期间无需任何...

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

在某国外大型汽车公司BI项目中,有一个子项目,需要通过大屏幕展示销售报表,程序需要自动启动和关闭。开发人员在开发过程中,发现在Win7的service中不能直接操作UI进程,调查过程中,发现如下相关资...
  • kissqw
  • kissqw
  • 2012年02月16日 14:33
  • 16216

穿透Session 0 隔离(一)

服务(Service)对于大家来说一定不会陌生,它是Windows 操作系统重要的组成部分。我们可以把服务想像成一种特殊的应用程序,它随系统的“开启~关闭”而“开始~停止”其工作内容,在这期间无需任何...

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

解决vista和win7在windows服务中交互桌面权限问题:穿透Session 0 隔离 分类: .NET 2012-02-16 14:33 1161人阅读 评论(2) 收藏 ...

穿透Session 0 隔离(一)

服务(Service)对于大家来说一定不会陌生,它是Windows 操作系统重要的组成部分。我们可以把服务想像成一种特殊的应用程序,它随系统的“开启~关闭”而“开始~停止”其工作内容,在这期间无需任何...

Windows Vista 下 Session 0 隔离的故事

  • 2008年12月02日 21:21
  • 987KB
  • 下载

关于win7的session0隔离的总结

微软做这个session隔离的原因我就不追查了,反正让人有蛋蛋的忧伤。       在vista以前的系统,后台服务和当前用户进程都运行在同一个session中(本地系统登录时的第一个session...

Windows中Session 0隔离对服务程序和驱动程序的影响

Windows中Session 0隔离对服务程序和驱动程序的影响
  • kaylc
  • kaylc
  • 2011年02月09日 15:53
  • 1992
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:穿透Session 0 隔离(二)
举报原因:
原因补充:

(最多只允许输入30个字)