CreateProcessAsUser 으로 다른 권한으로 실행하기


관련 자료

https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-createprocessasusera

 

CreateProcessAsUserA function (processthreadsapi.h)

Creates a new process and its primary thread. The new process runs in the security context of the user represented by the specified token.

docs.microsoft.com

https://support.microsoft.com/ko-kr/help/165194/createprocessasuser-windowstations-and-desktops

 

https://support.microsoft.com/ko-kr/help/165194/createprocessasuser-windowstations-and-desktops

쿠키가 사용되고 있지 않습니다. 쿠키를 사용하고 페이지를 새로 고치세요.

support.microsoft.com

https://mynotepad.tistory.com/176

 

VIsta 이상 버전에서 Windows Service 에 등록된 프로그램에서 다른 프로그램 띄우기

Windows Vista 이상 버전에서는 세션 관리정책으로 인해서 Windows 의 Service 에 등록된 프로그램이 직접 다른 프로그램을 띄우지 못하게 막았다. 보안상의 이유로 이렇게 한 듯 한데. 이로 인해서 서비스로 등록..

mynotepad.tistory.com

 



 

 

 아래 코드를 통해서 다른 계정의 UI와 권한으로 실행할 수 있다.

 

public class ProcessLauncher
    {
        #region Structures
        [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;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct ProfileInfo
        {
            public int dwSize;
            public int dwFlags;
            public string lpUserName;
            public string lpProfilePath;
            public string lpDefaultPath;
            public string lpServerName;
            public string lpPolicyPath;
            public IntPtr hProfile;
        }
        #endregion

        #region Enumerations
        public enum SECURITY_IMPERSONATION_LEVEL
        {
            SecurityAnonymous,
            SecurityIdentification,
            SecurityImpersonation,
            SecurityDelegation
        }

        public enum TOKEN_TYPE
        {
            TokenPrimary = 1,
            TokenImpersonation
        }
        #endregion

        #region Constants
        //Use these for DesiredAccess
        private static uint STANDARD_RIGHTS_REQUIRED = 0x000F0000;
        private static uint STANDARD_RIGHTS_READ = 0x00020000;
        private static uint TOKEN_ASSIGN_PRIMARY = 0x0001;
        private static uint TOKEN_DUPLICATE = 0x0002;
        private static uint TOKEN_IMPERSONATE = 0x0004;
        private static uint TOKEN_QUERY = 0x0008;
        private static uint TOKEN_QUERY_SOURCE = 0x0010;
        private static uint TOKEN_ADJUST_PRIVILEGES = 0x0020;
        private static uint TOKEN_ADJUST_GROUPS = 0x0040;
        private static uint TOKEN_ADJUST_DEFAULT = 0x0080;
        private static uint TOKEN_ADJUST_SESSIONID = 0x0100;
        private static uint TOKEN_READ = (STANDARD_RIGHTS_READ | TOKEN_QUERY);
        private static uint TOKEN_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_QUERY_SOURCE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_SESSIONID);

        private const int MAXIMUM_ALLOWED = 0x2000000;

        //Process Creation Flags
        private const uint CREATE_UNICODE_ENVIRONMENT = 0x00000400;
        private const uint CREATE_NO_WINDOW = 0x08000000;
        private const uint CREATE_NEW_CONSOLE = 0x00000010;
        private const uint DETACHED_PROCESS = 0x00000008;

        private const UInt32 INFINITE = 0xFFFFFFFF;
        private const UInt32 WAIT_ABANDONED = 0x00000080;
        private const UInt32 WAIT_OBJECT_0 = 0x00000000;
        private const UInt32 WAIT_TIMEOUT = 0x00000102;
        #endregion

        #region Win32 API Imports



        [
           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, uint dwCreationFlags, IntPtr lpEnvrionment,
                               string lpCurrentDirectory, ref STARTUPINFO lpStartupInfo,
                               ref PROCESS_INFORMATION lpProcessInformation);

        [
           DllImport("advapi32.dll",
              EntryPoint = "DuplicateTokenEx")
        ]
        public static extern bool
           DuplicateTokenEx(IntPtr hExistingToken, uint dwDesiredAccess,
                            ref SECURITY_ATTRIBUTES lpThreadAttributes,
                            Int32 ImpersonationLevel, Int32 dwTokenType,
                            ref IntPtr phNewToken);

        [DllImport("advapi32.dll", SetLastError = true)]
        internal static extern bool OpenProcessToken(IntPtr ProcessHandle, uint DesiredAccess, ref IntPtr TokenHandle);

        [DllImport("kernel32.dll")]
        public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);

        [DllImport("kernel32.dll")]
        private static extern uint WTSGetActiveConsoleSessionId();

        [DllImport("wtsapi32.dll", SetLastError = true)]
        static extern bool WTSQueryUserToken(UInt32 sessionId, out IntPtr Token);

        [DllImport("kernel32.dll")]
        static extern bool GetExitCodeProcess(IntPtr hProcess, ref int lpExitCode);

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);

        [DllImport("userenv.dll", CharSet = CharSet.Auto, SetLastError = true)]
        static extern bool CreateEnvironmentBlock(out IntPtr lpEnvironment, IntPtr hToken, bool bInherit);

        [DllImport("userenv.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool DestroyEnvironmentBlock(IntPtr lpEnvironment);

        [DllImport("userenv.dll", SetLastError = true, CharSet = CharSet.Auto)]
        public static extern bool LoadUserProfile(IntPtr hToken, ref ProfileInfo lpProfileInfo);

        [DllImport("Userenv.dll", CallingConvention = CallingConvention.Winapi, SetLastError = true, CharSet = CharSet.Auto)]
        public static extern bool UnloadUserProfile(IntPtr hToken, IntPtr lpProfileInfo);

        #endregion

        public static void Start(ProcessStartInfo processStartInfo)
        {
            var primaryToken = IntPtr.Zero;
            var userToken = IntPtr.Zero;
            var hProcess = IntPtr.Zero;

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

            var profileInfo = new ProfileInfo();
            profileInfo.dwSize = Marshal.SizeOf(profileInfo);
            profileInfo.lpUserName = Environment.UserName;
            profileInfo.dwFlags = 1;


            try
            {
                primaryToken = GetCurrentWinLogonToken();
                userToken = GetCurrentUserToken();
                var si = new STARTUPINFO();
                si.cb = Marshal.SizeOf(si);
                si.lpDesktop = "WinSta0\\Default";
 
                IntPtr lpEnvironment = IntPtr.Zero;
                if (!CreateEnvironmentBlock(out lpEnvironment, GetCurrentUserToken(), false))
                    throw new Win32Exception(Marshal.GetLastWin32Error());

                //if (!LoadUserProfile(userToken, ref profileInfo))
                //    throw new Win32Exception(Marshal.GetLastWin32Error());

                if (!CreateProcessAsUser(
                                            primaryToken,
                                            null,
                                            string.Format("\"{0}\" {1}", processStartInfo.FileName.Replace("\"", "\"\""), processStartInfo.Arguments),
                                            ref sa, ref sa,
                                            false, CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT, lpEnvironment,
                                            null, ref si, ref pi
                                    ))
                    throw new Win32Exception(Marshal.GetLastWin32Error());

                DestroyEnvironmentBlock(lpEnvironment);

            }
            finally
            {
                if (pi.hProcess != IntPtr.Zero)
                    CloseHandle(pi.hProcess);
                if (pi.hThread != IntPtr.Zero)
                    CloseHandle(pi.hThread);
                if (primaryToken != IntPtr.Zero)
                    CloseHandle(primaryToken);
                //UnloadUserProfile(userToken, profileInfo.hProfile);
            }


        }

        private static IntPtr GetCurrentWinLogonToken()
        {
            int winlogonPid = 0;
            var hToken = IntPtr.Zero;
            var hDupedToken = IntPtr.Zero;
            var hProcess = IntPtr.Zero;

            var sa = new SECURITY_ATTRIBUTES();
            sa.Length = Marshal.SizeOf(sa);
            IntPtr pServer = IntPtr.Zero;

            uint dwSessionId = GetCurrentUserSessionId();

            // obtain the process id of the winlogon process that is running within the currently active session
            Process[] processes = Process.GetProcessesByName("winlogon");
            foreach (Process p in processes)
            {
                if (p.SessionId == dwSessionId)
                {
                    winlogonPid = p.Id;
                }
            }

            // obtain a handle to the winlogon process
            hProcess = OpenProcess(MAXIMUM_ALLOWED, false, winlogonPid);
            if (!OpenProcessToken(hProcess, TOKEN_DUPLICATE, ref hToken))
                throw new Win32Exception(Marshal.GetLastWin32Error());

            if (!DuplicateTokenEx(
                    hToken,
                    TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS,
                    ref sa,
                    (int)SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation,
                    (int)TOKEN_TYPE.TokenPrimary,
                    ref hDupedToken
                ))
                throw new Win32Exception(Marshal.GetLastWin32Error());

            return hDupedToken;
        }

        private static uint GetCurrentUserSessionId()
        {
            uint dwSessionId = 0;
            using (ITerminalServer localServer = (new TerminalServicesManager()).GetLocalServer())
            {
                foreach (ITerminalServicesSession current in localServer.GetSessions())
                {
                    if (current.ConnectionState == ConnectionState.Active)
                    {
                        dwSessionId = (uint)current.SessionId;
                        break;
                    }
                }
            }

            return dwSessionId;
        }

        private static IntPtr GetCurrentUserToken()
        {
            var hToken = IntPtr.Zero;
            var hDupedToken = IntPtr.Zero;

            var sa = new SECURITY_ATTRIBUTES();
            sa.Length = Marshal.SizeOf(sa);
            IntPtr pServer = IntPtr.Zero;

            uint dwSessionId = GetCurrentUserSessionId();

            if (!WTSQueryUserToken(dwSessionId, out hToken))
                throw new Win32Exception(Marshal.GetLastWin32Error());

            if (!DuplicateTokenEx(
                    hToken,
                    TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS,
                    ref sa,
                    (int)SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation,
                    (int)TOKEN_TYPE.TokenPrimary,
                    ref hDupedToken
                ))
                throw new Win32Exception(Marshal.GetLastWin32Error());

            return hDupedToken;
        }


    }

[코드] 특정 계정의 토큰 값으로 사용자 계정으로 프로세스 실행하는 코드

+ Recent posts