public TokenSwitchRenderer(Connection connection, ElevationRequest elevationRequest) { if (Settings.SecurityEnforceUacIsolation && !elevationRequest.NewWindow) { throw new Exception("TokenSwitch mode not supported when SecurityEnforceUacIsolation is set."); } _connection = connection; _elevationRequest = elevationRequest; Environment.SetEnvironmentVariable("prompt", Environment.ExpandEnvironmentVariables(elevationRequest.Prompt)); ProcessApi.CreateProcessFlags dwCreationFlags = ProcessApi.CreateProcessFlags.CREATE_SUSPENDED; if (elevationRequest.NewWindow) { dwCreationFlags |= ProcessApi.CreateProcessFlags.CREATE_NEW_CONSOLE; } string exeName, args; if (elevationRequest.IntegrityLevel == IntegrityLevel.MediumPlus && ArgumentsHelper.UnQuote(elevationRequest.FileName.ToUpperInvariant()) != Environment.GetEnvironmentVariable("COMSPEC").ToUpperInvariant()) { // Now, we have an issue with this method: The process launched with the new token throws Access Denied if it tries to read its own token. // Kind of dirty workaround is to wrap the call with a "CMD.exe /c ".. this intermediate process will then // launching the command with a fresh new (desired) token and we know cmd wont try to read it's substitute token (throwing Access Denied). exeName = Environment.GetEnvironmentVariable("COMSPEC"); args = $"/s /c \"{elevationRequest.FileName} {elevationRequest.Arguments}\""; } else { // Hack not needed if we are already calling CMD exeName = elevationRequest.FileName; args = elevationRequest.Arguments; } _process = ProcessFactory.CreateProcessAsUserWithFlags(exeName, args, dwCreationFlags, out _processInformation); elevationRequest.TargetProcessId = _processInformation.dwProcessId; if (!elevationRequest.NewWindow) { ConsoleApi.SetConsoleCtrlHandler(ConsoleHelper.IgnoreConsoleCancelKeyPress, true); } }
public static extern bool CreateProcessWithTokenW(IntPtr hToken, LogonFlags dwLogonFlags, string lpApplicationName, string lpCommandLine, ProcessApi.CreateProcessFlags dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);
internal static SafeProcessHandle CreateProcessAsUserWithFlags(string lpApplicationName, string args, ProcessApi.CreateProcessFlags dwCreationFlags, out PROCESS_INFORMATION pInfo) { var sInfoEx = new ProcessApi.STARTUPINFOEX(); sInfoEx.StartupInfo.cb = Marshal.SizeOf(sInfoEx); IntPtr lpValue = IntPtr.Zero; var pSec = new ProcessApi.SECURITY_ATTRIBUTES(); var tSec = new ProcessApi.SECURITY_ATTRIBUTES(); pSec.nLength = Marshal.SizeOf(pSec); tSec.nLength = Marshal.SizeOf(tSec); var command = $"{lpApplicationName} {args}"; Logger.Instance.Log($"{nameof(CreateProcessAsUser)}: {lpApplicationName} {args}", LogLevel.Debug); if (!ProcessApi.CreateProcess(null, command, ref pSec, ref tSec, false, dwCreationFlags, IntPtr.Zero, null, ref sInfoEx, out pInfo)) { throw new Win32Exception((int)ConsoleApi.GetLastError()); } return(new SafeProcessHandle(pInfo.hProcess, true)); }