/// <summary> /// Sets the period of time to wait for the associated process to exit, and blocks the current thread of execution /// until the time has elapsed or the process has exited. /// </summary> /// <param name="milliseconds"> /// The amount of time, in milliseconds, to wait for the associated process to exit. A value of /// 0 specifies an immediate return, and a value of -1 specifies an infinite wait. /// </param> /// <returns>true if the associated process has exited; otherwise, false.</returns> public bool WaitForExit(int milliseconds) { bool exited; using var handle = ProcessNative.OpenProcessHandle(ProcessNative.ProcessAccessFlags.Synchronize, ProcessId); if (handle.IsInvalid) { exited = true; } else { using var processWaitHandle = new ProcessWaitHandle(handle); if (processWaitHandle.WaitOne(milliseconds, false)) { exited = true; _signaled = true; if (ProcessNative.GetExitCodeProcess(handle, out var exitCode)) { ExitCode = (int)exitCode; } } else { exited = false; _signaled = false; } } if (exited) { RaiseOnExited(); } return(exited); }
/// <summary> /// Creates a new <see cref="ProcessHook"/> instance from the specified <paramref name="processId"/>. /// </summary> /// <param name="processId">The ID of the running process.</param> /// <exception cref="UnauthorizedAccessException"> /// Thrown when the AppDomain lacks the necessary elevation to obtain a /// handle on the target process. /// </exception> public ProcessHook(int processId) { ProcessId = processId; _handle = ProcessNative.OpenProcessHandle(ProcessNative.ProcessAccessFlags.QueryLimitedInformation | ProcessNative.ProcessAccessFlags.Synchronize, processId); if (_handle.IsInvalid) { throw new UnauthorizedAccessException($"Unable to access handle of process: {Marshal.GetLastWin32Error()}"); } }
/// <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); } }