CreateProcessAsUser Создание окна в активном сеансе - PullRequest
12 голосов
/ 01 февраля 2012

Я использую CreateProcessAsUser из службы Windows (, пожалуйста, мы можем остаться в теме и предположить, что у меня есть очень веская причина для этого ).Вопреки тому, что все остальные просят здесь, я получаю окно в моем сеансе активного терминала (сеанс 1) вместо того же сеанса, что и служба (сеанс 0), что нежелательно.

Я присвоил Код Скотта Аллена ;и придумал следующее.Заметные изменения: «Возврат к себе», «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);
            }
        }
    }
}

Теперь предположим, что служба работает в «Домене \ MyService», и я в данный момент вошел в систему как«Домен \ Администратор» - и я загружаю консольное приложение как рабочий процесс.Когда я использую клиентское приложение для доступа к службе (служба не запускается в режиме консоли, т. Е. Находится в сеансе 0) и выполняю метод, который вызывает CreateProcessAsUser, рабочий процесс появляется на моем рабочем столе.

Теперь я могу сделать это Windows-приложением без окна, чтобы отодвинуть шаг создания окна консоли;однако в конце дня он все еще создается в сеансе 1.

Есть идеи, почему консольное приложение не создается в том же сеансе, что и служба?

Ответы [ 3 ]

6 голосов
/ 08 февраля 2012

Как вы, наверное, уже знаете, изоляция сеанса 0 по соображениям безопасности, и вы можете прочитать больше об этом здесь http://msdn.microsoft.com/en-us/windows/hardware/gg463353.aspx

Относительно того, почему ваше консольное приложение создано в активном сеансе (например,сеанс 1), это на самом деле связано непосредственно с вашим токеном.Когда вы запрашиваете токен текущего пользователя, этот токен автоматически переносит с собой информацию идентификатора сеанса - в данном случае это сеанс служб терминалов входа в систему (сеанс 1).Этот идентификатор сеанса является ссылкой для токена, который затем реплицируется в DuplicateTokenEx и затем используется в вызове CreateProcessAsUser.Чтобы принудительно создать консольное приложение в сеансе 0, вам нужно сделать явный вызов API SetTokenInformation (advapi32.dll), переданный в ваш hDupedToken, перед вызовом CreateProcessAsUser, как показано ниже

..................
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

1 голос
/ 14 июня 2016

Мне удалось реализовать начальную запись как рабочее решение с моей стороны, однако я не могу найти способ скрыть окно консоли. Я попытался STARTF_USESHOWWINDOW и 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);
        }
    }
0 голосов
/ 08 февраля 2012

Попробуйте возиться с именованным параметром CharSet, равным MarshalAs, StructLayout и DllImport.Вам может потребоваться добавить MarshalAs к различным строкам, чтобы сделать это.Не беспокойтесь Unicode: вы не используете это.Я рекомендую сначала установить их все на CharSet.Ansi.Запустите все тесты, которые вы уже пробовали, то есть настройку рабочего стола и все такое забавное.В случае сбоя переключите их все на авто.Если это все еще не работает, удалите их все.

Предполагая, что ничего из этого не работает, переключитесь на CreateUserProcessW и CharSet.Unicode, чтобы вы знали, что получаете.Если подумать, просто перейдите к этому шагу.

Если вам нужно установить UnmanagedType с MarshalAs для строк, вам нужно UnmanagedType.LPStr для Ansi, UnmanagedType.LPTStr для Auto и UnmanagedType.LPWStrдля Юникода.Во всяком случае, сделайте это для всех ваших строк.

...