Ejemplo n.º 1
0
        public static void RunAsDesktopUser(string fileName, string args)
        {
            if (string.IsNullOrWhiteSpace(fileName))
            {
                throw new ArgumentException("Value cannot be null or whitespace.", nameof(fileName));
            }

            // To start process as shell user you will need to carry out these steps:
            // 1. Enable the SeIncreaseQuotaPrivilege in your current token
            // 2. Get an HWND representing the desktop shell (GetShellWindow)
            // 3. Get the Process ID(PID) of the process associated with that window(GetWindowThreadProcessId)
            // 4. Open that process(OpenProcess)
            // 5. Get the access token from that process (OpenProcessToken)
            // 6. Make a primary token with that token(DuplicateTokenEx)
            // 7. Start the new process with that primary token(CreateProcessWithTokenW)

            var hProcessToken = IntPtr.Zero;

            // Enable SeIncreaseQuotaPrivilege in this process.  (This won't work if current process is not elevated.)
            try
            {
                var process = Unmanaged.GetCurrentProcess();
                if (!Unmanaged.OpenProcessToken(process, 0x0020, ref hProcessToken))
                {
                    return;
                }

                var tkp = new TOKEN_PRIVILEGES
                {
                    PrivilegeCount = 1,
                    Privileges     = new LUID_AND_ATTRIBUTES[1]
                };

                if (!Unmanaged.LookupPrivilegeValue(null, "SeIncreaseQuotaPrivilege", ref tkp.Privileges[0].Luid))
                {
                    return;
                }

                tkp.Privileges[0].Attributes = 0x00000002;

                if (!Unmanaged.AdjustTokenPrivileges(hProcessToken, false, ref tkp, 0, IntPtr.Zero, IntPtr.Zero))
                {
                    return;
                }
            }
            finally
            {
                Unmanaged.CloseHandle(hProcessToken);
            }

            // Get an HWND representing the desktop shell.
            // CAVEATS:  This will fail if the shell is not running (crashed or terminated), or the default shell has been
            // replaced with a custom shell.  This also won't return what you probably want if Explorer has been terminated and
            // restarted elevated.
            var hwnd = Unmanaged.GetShellWindow();

            if (hwnd == IntPtr.Zero)
            {
                return;
            }

            var hShellProcess      = IntPtr.Zero;
            var hShellProcessToken = IntPtr.Zero;
            var hPrimaryToken      = IntPtr.Zero;

            try
            {
                // Get the PID of the desktop shell process.
                uint dwPID;
                if (Unmanaged.GetWindowThreadProcessId(hwnd, out dwPID) == 0)
                {
                    return;
                }

                // Open the desktop shell process in order to query it (get the token)
                hShellProcess = Unmanaged.OpenProcess(ProcessAccessFlags.QueryInformation, false, dwPID);
                if (hShellProcess == IntPtr.Zero)
                {
                    return;
                }

                // Get the process token of the desktop shell.
                if (!Unmanaged.OpenProcessToken(hShellProcess, 0x0002, ref hShellProcessToken))
                {
                    return;
                }

                var dwTokenRights = 395U;

                // Duplicate the shell's process token to get a primary token.
                // Based on experimentation, this is the minimal set of rights required for CreateProcessWithTokenW (contrary to current documentation).
                if (!Unmanaged.DuplicateTokenEx(hShellProcessToken, dwTokenRights, IntPtr.Zero, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, TOKEN_TYPE.TokenPrimary, out hPrimaryToken))
                {
                    return;
                }

                // Start the target process with the new token.
                var si = new STARTUPINFO();
                var pi = new PROCESS_INFORMATION();
                if (!Unmanaged.CreateProcessWithTokenW(hPrimaryToken, 0, fileName, "\"" + fileName + "\" \"" + args + "\"", 0, IntPtr.Zero, Path.GetDirectoryName(fileName), ref si, out pi))
                {
                    return;
                }
            }
            finally
            {
                Unmanaged.CloseHandle(hShellProcessToken);
                Unmanaged.CloseHandle(hPrimaryToken);
                Unmanaged.CloseHandle(hShellProcess);
            }
        }