/// <summary> /// /// </summary> /// <param name="startInfo"></param> /// <returns></returns> internal static bool LaunchUriDeferred(WardenStartInfo startInfo) { var(fileName, arguments, workingDirectory) = ValidateUri(startInfo); if (startInfo.AsUser) { if (Api.StartProcessAndBypassUac(fileName, arguments, workingDirectory, out _)) { return(true); } throw new WardenLaunchException(string.Format(Resources.Exception_Process_Not_Start, startInfo.FileName, startInfo.Arguments)); } var processStartInfo = new ProcessStartInfo { FileName = fileName, Arguments = arguments, WorkingDirectory = workingDirectory, UseShellExecute = true }; using (var process = Process.Start(processStartInfo)) { if (process == null) { throw new WardenLaunchException(string.Format(Resources.Exception_Process_Not_Start, startInfo.FileName, startInfo.Arguments)); } } return(true); }
/// <summary> /// /// </summary> /// <param name="startInfo"></param> /// <returns></returns> internal static WardenProcess LaunchWin32App(WardenStartInfo startInfo) { if (!new FileInfo(startInfo.FileName).Exists) { throw new WardenLaunchException($"Unable to launch {startInfo.FileName} -- the file is missing."); } if (startInfo.AsUser) { if (!Api.StartProcessAndBypassUac(startInfo.FileName, startInfo.Arguments, startInfo.WorkingDirectory, out var procInfo)) { throw new WardenLaunchException(string.Format(Resources.Exception_Process_Not_Start, startInfo.FileName, startInfo.Arguments)); } return(WardenProcess.GetProcessFromId((int)procInfo.dwProcessId, startInfo.Filters)); } var processStartInfo = new ProcessStartInfo { FileName = startInfo.FileName, Arguments = startInfo.Arguments, WorkingDirectory = startInfo.WorkingDirectory, UseShellExecute = true }; using (var process = Process.Start(processStartInfo)) { if (process == null) { throw new WardenLaunchException(Resources.Exception_Process_Not_Launched_Unknown); } return(WardenProcess.GetProcessFromId(process.Id, startInfo.Filters)); } }
/// <summary> /// Combines the Package Family Name and Application ID into a valid AUMID string and then launches the app. /// </summary> /// <param name="startInfo"></param> /// <returns>If the app is launched successfully a WardenProcess is returned.</returns> internal static WardenProcess LaunchApp(WardenStartInfo startInfo) { var aumid = $"{startInfo.PackageFamilyName}!{startInfo.ApplicationId}"; var processId = Launch(aumid, startInfo.Arguments); if (processId <= 0) { throw new WardenLaunchException(string.Format(Resources.Exception_Could_Not_Find_Process_Id, aumid)); } return(WardenProcess.GetProcessFromId(processId, startInfo.Filters)); }
/// <summary> /// Launches an application URI Scheme and waits for the target process to appear /// </summary> /// <param name="startInfo"></param> /// <param name="cancelToken">A cancellation token to configure how long Warden will wait.</param> /// <returns></returns> internal static async Task <bool> LaunchUri(WardenStartInfo startInfo, CancellationTokenSource cancelToken) { var(fileName, arguments, workingDirectory) = ValidateUri(startInfo); if (startInfo.AsUser) { if (!Api.StartProcessAndBypassUac(fileName, arguments, workingDirectory, out _)) { throw new WardenLaunchException(string.Format(Resources.Exception_Process_Not_Start, fileName, arguments)); } } else { var processStartInfo = new ProcessStartInfo { FileName = fileName, Arguments = arguments, WorkingDirectory = workingDirectory, UseShellExecute = true }; using (var process = Process.Start(processStartInfo)) { if (process == null) { throw new WardenLaunchException(string.Format(Resources.Exception_Process_Not_Start, startInfo.FileName, startInfo.Arguments)); } } } while (!cancelToken.IsCancellationRequested) { using (var process = ProcessUtils.GetProcess(startInfo.TargetFileName)) { if (process != null) { return(true); } } //aggressive poll await Task.Delay(TimeSpan.FromMilliseconds(100), cancelToken.Token); } return(false); }
/// <summary> /// Attempts to create a process outside of session zero. /// </summary> /// <param name="startInfo"></param> /// <returns></returns> internal static WardenProcess CreateProcessAsUser(WardenStartInfo startInfo) { if (!new FileInfo(startInfo.FileName).Exists) { throw new WardenLaunchException($"Unable to launch {startInfo.FileName} -- the file is missing."); } if (startInfo.RaisePrivileges) { if (Api.StartProcessAsPrivilegedUser(startInfo.FileName, startInfo.Arguments, startInfo.WorkingDirectory, out var privInfo)) { return(WardenProcess.GetProcessFromId(privInfo, startInfo.Filters, startInfo.Track)); } throw new WardenLaunchException("Unable to start process as privileged user"); } if (Api.StartProcessAsUser(startInfo.FileName, startInfo.Arguments, startInfo.WorkingDirectory, out var procInfo)) { return(WardenProcess.GetProcessFromId(procInfo, startInfo.Filters, startInfo.Track)); } throw new WardenLaunchException("Unable to start process as user"); }
/// <summary> /// Creates a new process object the interactive desktop session. /// </summary> /// <param name="info"> /// The <see cref="WardenStartInfo"/> that contains the information that is used to start the process, /// including the PackageFamilyName, ApplicationId, and any command-line arguments. /// </param> /// <returns>A <see cref="WardenProcess"/> instance that is associated with the created process.</returns> internal static WardenProcess?AsUser(WardenStartInfo info) { var environmentBlockHandle = IntPtr.Zero; var startInfo = new StartupInfo(); var processInformation = new ProcessInformation(); startInfo.cb = Marshal.SizeOf <StartupInfo>(); try { if (!TryGetInteractiveUserToken(out var sessionId, out var interactiveUserToken)) { throw new Win32Exception("GetSessionUserToken failed."); } // By default CreateProcessAsUser creates a process on a non-interactive window station, meaning // the window station has a desktop that is invisible and the process is incapable of receiving // user input. To remedy this we set the lpDesktop parameter to indicate we want to enable user // interaction with the new process. startInfo.wShowWindow = (short)SW.SW_SHOW; startInfo.lpDesktop = InteractiveWindowStation; using var registryHandle = LoadUserProfile((int)sessionId, interactiveUserToken, out _); if (registryHandle.IsInvalid) { throw new Win32Exception("LoadUserProfile failed."); } // copy the users env block if (!CreateEnvironmentBlock(ref environmentBlockHandle, interactiveUserToken, false)) { throw new Win32Exception("CreateEnvironmentBlock failed."); } const int creationFlags = CREATE_UNICODE_ENVIRONMENT | DETACHED_PROCESS; if (!CreateProcessAsUser(interactiveUserToken, info.FileName, info.Arguments, null, null, false, creationFlags, environmentBlockHandle, info.WorkingDirectory, ref startInfo, out processInformation)) { throw new Win32Exception("CreateProcessAsUser failed"); } UnloadUserProfile(interactiveUserToken, registryHandle); return(WardenProcess.GetProcessById(processInformation.ProcessId, info.Track, info.FilteredImages)); } finally { DestroyEnvironmentBlock(environmentBlockHandle); CloseHandle(processInformation.ThreadHandle); CloseHandle(processInformation.ProcessHandle); CloseHandle(startInfo.hStdError); CloseHandle(startInfo.hStdInput); CloseHandle(startInfo.hStdOutput); CloseHandle(startInfo.lpReserved2); } }
/// <summary> /// Creates a new process object with the privileges of LocalSystem in the interactive desktop session. /// </summary> /// <param name="info"> /// The <see cref="WardenStartInfo"/> that contains the information that is used to start the process, /// including the PackageFamilyName, ApplicationId, and any command-line arguments. /// </param> /// <returns>A <see cref="WardenProcess"/> instance that is associated with the created process.</returns> internal static WardenProcess?AsLocalSystem(WardenStartInfo info) { var environmentBlockHandle = IntPtr.Zero; var startInfo = new StartupInfo(); var processInformation = new ProcessInformation(); startInfo.cb = Marshal.SizeOf <StartupInfo>(); try { if (!TryGetInteractiveUserToken(out var sessionId, out var interactiveUserToken)) { throw new Win32Exception("GetSessionUserToken failed."); } // By default CreateProcessAsUser creates a process on a non-interactive window station, meaning // the window station has a desktop that is invisible and the process is incapable of receiving // user input. To remedy this we set the lpDesktop parameter to indicate we want to enable user // interaction with the new process. startInfo.wShowWindow = (short)SW.SW_SHOW; startInfo.lpDesktop = InteractiveWindowStation; using var registryHandle = LoadUserProfile((int)sessionId, interactiveUserToken, out _); if (registryHandle.IsInvalid) { throw new Win32Exception("LoadUserProfile failed."); } // copy the users env block if (!CreateEnvironmentBlock(ref environmentBlockHandle, interactiveUserToken, false)) { throw new Win32Exception("CreateEnvironmentBlock failed."); } var logonProcessId = GetWinLogonProcessId(sessionId); if (logonProcessId == 0) { throw new Win32Exception($"Unable to find the WinLogon process ID for session '{sessionId}'."); } using var processHandle = ProcessNative.OpenProcessHandle(ProcessNative.ProcessAccessFlags.MaximumAllowed, logonProcessId); if (processHandle.IsInvalid) { throw new Win32Exception("Unable to obtain a valid handle for winlogon.exe"); } if (!ProcessNative.OpenProcessToken(processHandle, TOKEN_ALL_ACCESS, out var processToken)) { throw new Win32Exception("Unable to open the process token for winlogon.exe"); } // ReSharper disable once UseObjectOrCollectionInitializer var securityAttributes = new SecurityAttributes(); securityAttributes.Length = Marshal.SizeOf <SecurityAttributes>(); // copy the access token of the explorer process; the newly created token will be a primary token if (!DuplicateTokenEx(processToken, MAXIMUM_ALLOWED, securityAttributes, SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, TokenType.Primary, out var winLogonToken)) { throw new Win32Exception("Unable to duplicate the winlogon.exe process token"); } const int creationFlags = CREATE_UNICODE_ENVIRONMENT | DETACHED_PROCESS; if (!CreateProcessAsUser(winLogonToken, info.FileName, info.Arguments, securityAttributes, securityAttributes, false, creationFlags, environmentBlockHandle, info.WorkingDirectory, ref startInfo, out processInformation)) { throw new Win32Exception($"CreateProcessAsUserW failed: {Marshal.GetLastWin32Error()}"); } UnloadUserProfile(interactiveUserToken, registryHandle); return(WardenProcess.GetProcessById(processInformation.ProcessId, info.Track, info.FilteredImages)); } finally { DestroyEnvironmentBlock(environmentBlockHandle); CloseHandle(processInformation.ThreadHandle); CloseHandle(processInformation.ProcessHandle); CloseHandle(startInfo.hStdError); CloseHandle(startInfo.hStdInput); CloseHandle(startInfo.hStdOutput); CloseHandle(startInfo.lpReserved2); } }
/// <summary> /// /// </summary> /// <param name="startInfo"></param> /// <returns></returns> private static (string FileName, string Arguments, string WorkingDirectory) ValidateUri(WardenStartInfo startInfo) { var unwrappedUri = UnwrapUri(startInfo.FileName, new[] { startInfo.Arguments }); if (!unwrappedUri.HasValue) { throw new WardenLaunchException($"Unable to unwrap the following URI: {startInfo.FileName}"); } var launcher = unwrappedUri.Value.LauncherFileName; if (string.IsNullOrWhiteSpace(launcher) || !new FileInfo(launcher).Exists) { throw new WardenLaunchException($"Unable to run the launcher for URI {startInfo.FileName} because it does not exist.."); } var launcherArguments = unwrappedUri.Value.LauncherArguments; var launcherWorkingDir = PathUtils.GetDirectoryName(launcher); if (string.IsNullOrWhiteSpace(launcher) || !new DirectoryInfo(launcherWorkingDir).Exists) { throw new WardenLaunchException($"Unable to determine the working directory for the following URI: {startInfo.FileName}."); } if (string.IsNullOrWhiteSpace(startInfo.TargetFileName) || !new FileInfo(startInfo.TargetFileName).Exists) { throw new WardenLaunchException($"Unable to launch URI {startInfo.FileName} because target file {startInfo.TargetFileName} does not exist."); } return(launcher, launcherArguments, launcherWorkingDir); }
/// <summary> /// Starts a process using the operating system shell. /// </summary> /// <param name="startInfo"> /// The <see cref="WardenStartInfo"/> that contains the information that is used to start the process, /// including the file name and any command-line arguments. /// </param> public static void Start(WardenStartInfo startInfo) { var emptyObject = new object(); object shellWindows = null; object desktopWindow = null; object desktopBrowser = null; object desktopView = null; object backgroundFolderView = null; object applicationDispatch = null; var shellWindowsType = Type.GetTypeFromCLSID(ShellWindowsServer, false); if (shellWindowsType == null) { throw new Exception("This operation is not available in this environment."); } try { shellWindows = Activator.CreateInstance(shellWindowsType); desktopWindow = ((IShellWindows)shellWindows).FindWindowSW(ref emptyObject, ref emptyObject, ShellWindowsClass.Desktop, out _, ShellWindowsFindOptions.NeedDispatch); ((IServiceProvider)desktopWindow).QueryService(TopLevelBrowser, typeof(IShellBrowser).GUID, out desktopBrowser); ((IShellBrowser)desktopBrowser).QueryActiveShellView(out desktopView); ((IShellView)desktopView).GetItemObject(ShellViewGetItemObject.Background, typeof(IDispatch).GUID, out backgroundFolderView); applicationDispatch = ((IShellFolderViewDual)backgroundFolderView).Application; var showFlags = new object(); switch (startInfo.WindowStyle) { case WindowStyle.Normal: showFlags = ShellDispatchExecuteShowFlags.Normal; break; case WindowStyle.Hidden: showFlags = ShellDispatchExecuteShowFlags.Hidden; break; case WindowStyle.Minimized: showFlags = ShellDispatchExecuteShowFlags.Minimized; break; case WindowStyle.Maximized: showFlags = ShellDispatchExecuteShowFlags.Maximized; break; } ((IShellDispatch2)applicationDispatch).ShellExecute(startInfo.FileName, startInfo.Arguments, startInfo.WorkingDirectory, startInfo.RaisePrivileges ? "runas" : "open", showFlags); } catch (Exception e) { throw new Exception("Failed to start application.", e); } finally { if (applicationDispatch != null) { Marshal.ReleaseComObject(applicationDispatch); } if (backgroundFolderView != null) { Marshal.ReleaseComObject(backgroundFolderView); } if (desktopView != null) { Marshal.ReleaseComObject(desktopView); } if (desktopBrowser != null) { Marshal.ReleaseComObject(desktopBrowser); } if (desktopWindow != null) { Marshal.ReleaseComObject(desktopWindow); } if (shellWindows != null) { Marshal.ReleaseComObject(shellWindows); } } }