CreateProcessAsUser正在活动会话中创建窗口

本文关键字:创建 窗口 会话 活动 CreateProcessAsUser | 更新日期: 2023-09-27 18:22:11

我正在使用windows服务中的CreateProcessAsUser(请继续讨论主题,并假设我有充分的理由这样做)。与其他人在这里所问的相反,我在我的活动终端会话(会话1)中获得了一个窗口,而不是与服务(会话0)相同的会话,这是不可取的。

我盗用了斯科特·艾伦的密码;并提出了以下内容。值得注意的变化是"恢复到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"身份登录,并且我正在作为工作进程启动控制台应用程序。当我使用客户端应用程序访问服务(该服务不是在控制台模式下启动的,即它处于会话0中)并执行调用CreateProcessAsUser的方法时,工作进程会出现在我的桌面上。

现在我可以让它成为一个windows应用程序,没有创建控制台窗口的步骤;然而,最终它仍在会话1中创建。

你知道为什么控制台应用程序没有和服务在同一个会话中创建吗?

CreateProcessAsUser正在活动会话中创建窗口

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

关于为什么在活动会话(例如会话1)中创建控制台应用程序,这实际上直接链接回了您的用户令牌。当您请求当前用户令牌时,该令牌会自动携带会话id信息——在这种情况下,它是登录终端服务会话(会话1)。此会话id由令牌引用,然后在DuplicateTokenEx中复制,然后在CreateProcessAsUser调用中使用。为了强制在会话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

我能够将最初的帖子作为一个工作解决方案来实现,但是,我似乎找不到隐藏控制台窗口的方法。我尝试了STARTF_USSHOWWINDOW和SW_HIDE,但我的命令窗口仍然弹出。知道为什么吗?

    public const int STARTF_USESHOWWINDOW = 0x0000000;
    public const int SW_HIDE = 0;

   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 = String.Empty; 
            si.dwFlags = STARTF_USESHOWWINDOW;
            si.wShowWindow = SW_HIDE;
            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))
            {
                UInt32 dwSessionId = 1;  // set it to session 0
                SetTokenInformation(hDupedToken, TOKEN_INFORMATION_CLASS.TokenSessionId,
                    ref dwSessionId, (UInt32)IntPtr.Size);
                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);
        }
    }

试着摆弄MarshalAsStructLayoutDllImport的CharSet命名参数。为此,您可能需要将MarshalAs添加到各种字符串中。不用担心Unicode:您没有使用它。我建议先将它们全部设置为CharSet.Ansi。运行你已经尝试过的所有测试——也就是说,设置桌面和所有有趣的东西。如果它崩溃了,请将它们全部切换到自动。如果仍然不起作用,请将它们全部删除。

假设这些都不起作用,那么切换到CreateUserProcessWCharSet.Unicode,这样你就知道你得到了什么。仔细想想,就跳到这一步。

如果需要为字符串设置UnmanagedTypeMarshalAs,则需要为Ansi设置UnmanagedType.LPStr,为Auto设置UnmanagedType.LPTStr,为Unicode设置UnmanagedType.LPWStr。事实上,无论如何,对你所有的字符串都要这样做。