c# - CreateProcessAsUser 在事件 session 中创建窗口

标签 c# windows-services pinvoke

我正在使用 Windows 服务中的 CreateProcessAsUser(我们能否切题并假设我有充分的理由这样做)。与其他人在这里提出的问题相反,我在我的事件终端 session ( session 1)中获得了一个窗口,而不是与服务( session 0)相同的 session - 这是不可取的。

我拨了Scott Allen's code ;并提出以下内容。显着的变化是“恢复 self ”、“CREATE_NO_WINDOW”和命令行参数支持。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Security.Principal;
using System.ComponentModel;
using System.IO;

namespace SourceCode.Runtime.ChildProcessService
{
    [SuppressUnmanagedCodeSecurity]
    class NativeMethods
    {
        [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;
        public const int CREATE_NO_WINDOW = 0x08000000;

        [
           DllImport("kernel32.dll",
              EntryPoint = "CloseHandle", SetLastError = true,
              CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)
        ]
        public static extern bool CloseHandle(IntPtr handle);

        [
           DllImport("advapi32.dll",
              EntryPoint = "CreateProcessAsUser", 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",
              EntryPoint = "DuplicateTokenEx")
        ]
        public static extern bool
           DuplicateTokenEx(IntPtr hExistingToken, Int32 dwDesiredAccess,
                            ref SECURITY_ATTRIBUTES lpThreadAttributes,
                            Int32 ImpersonationLevel, Int32 dwTokenType,
                            ref IntPtr phNewToken);

        public static Process CreateProcessAsUser(string filename, string args)
        {
            var hToken = WindowsIdentity.GetCurrent().Token;
            var hDupedToken = IntPtr.Zero;

            var pi = new PROCESS_INFORMATION();
            var sa = new SECURITY_ATTRIBUTES();
            sa.Length = Marshal.SizeOf(sa);

            try
            {
                if (!DuplicateTokenEx(
                        hToken,
                        GENERIC_ALL_ACCESS,
                        ref sa,
                        (int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,
                        (int)TOKEN_TYPE.TokenPrimary,
                        ref hDupedToken
                    ))
                    throw new Win32Exception(Marshal.GetLastWin32Error());

                var si = new STARTUPINFO();
                si.cb = Marshal.SizeOf(si);
                si.lpDesktop = "";

                var path = Path.GetFullPath(filename);
                var dir = Path.GetDirectoryName(path);

                // Revert to self to create the entire process; not doing this might
                // require that the currently impersonated user has "Replace a process
                // level token" rights - we only want our service account to need
                // that right.
                using (var ctx = WindowsIdentity.Impersonate(IntPtr.Zero))
                {
                    if (!CreateProcessAsUser(
                                            hDupedToken,
                                            path,
                                            string.Format("\"{0}\" {1}", filename.Replace("\"", "\"\""), args),
                                            ref sa, ref sa,
                                            false, 0, IntPtr.Zero,
                                            dir, ref si, ref pi
                                    ))
                        throw new Win32Exception(Marshal.GetLastWin32Error());
                }

                return Process.GetProcessById(pi.dwProcessID);
            }
            finally
            {
                if (pi.hProcess != IntPtr.Zero)
                    CloseHandle(pi.hProcess);
                if (pi.hThread != IntPtr.Zero)
                    CloseHandle(pi.hThread);
                if (hDupedToken != IntPtr.Zero)
                    CloseHandle(hDupedToken);
            }
        }
    }
}

现在假设该服务在“Domain\MyService”下运行,我当前以“Domain\Administrator”身份登录 - 我正在将控制台应用程序作为工作进程启动。当我使用客户端应用程序访问服务(服务未在控制台模式下启动,即它在 session 0 中)并执行调用 CreateProcessAsUser 的方法时,工作进程出现在我的桌面上。

现在我可以使它成为一个没有窗口的 Windows 应用程序来回避控制台窗口的创建;然而,在一天结束时,它仍在 session 1 中创建。

为什么不在与服务相同的 session 中创建控制台应用程序有什么想法吗?

最佳答案

您可能已经知道,隔离 session 0 是出于安全原因,您可以在此处阅读更多相关信息 http://msdn.microsoft.com/en-us/windows/hardware/gg463353.aspx

关于为什么您的控制台应用程序是在事件 session (例如 session 1)中创建的,这实际上直接链接回您的用户 token 。当您请求当前用户 token 时,此 token 会自动携带 session ID 信息 - 在本例中为登录终端服务 session ( session 1)。此 session ID 由 token 引用,然后在 DuplicateTokenEx 中复制,然后在 CreateProcessAsUser 调用中使用。为了强制在 session 0 中创建您的控制台应用程序,您需要显式调用 SetTokenInformation API (advapi32.dll),在调用 CreateProcessAsUser 之前传入您的 hDupedToken,如下所示

..................
UInt32 dwSessionId = 0;  // set it to session 0
SetTokenInformation(hDupedToken, TokenInformationClass.TokenSessionId, ref dwSessionId, (UInt32) IntPtr.Size);
.................
CreateProcessAsUser(hDupedToken, ....)

这里是关于 SetTokenInformation 的更多信息 http://msdn.microsoft.com/en-us/library/windows/desktop/aa379591(v=vs.85).aspx

关于c# - CreateProcessAsUser 在事件 session 中创建窗口,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9095909/

相关文章:

c# - Serilog JsonFormatter 是否支持 Formatting.Indented?

C# - 显示最大值、最小值和平均值

windows - 如何将 Windows GUI 应用程序作为服务运行?

tcp - 访问另一台设备的 Windows 服务

c# - 如何在 C# 中使用 struct 和 union

c# - 在 C# 中以编程方式锁定 Windows 工作站

c# - 在 C# 中为每个不同行运行 ExecuteNonQuery

c# - 如何获取枚举的属性

c# - Windows 服务未记录到事件日志

c# - 如何将 IntPtr 获取为 bool 值