internal static extern bool CreateProcessAsUser( IntPtr hToken, string lpApplicationName, string lpCommandLine, SecurityAttributes lpProcessAttributes, SecurityAttributes lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, StartupInfo lpStartupInfo, out ProcessInformation lpProcessInformation);
internal static extern bool CreateProcessAsUser( IntPtr hToken, string lpApplicationName, string lpCommandLine, SecurityAttributes lpProcessAttributes, SecurityAttributes lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, StartupInfo lpStartupInfo, out ProcessInformation lpProcessInformation);
public RestrictedProcess(string fileName, string workingDirectory, IEnumerable<string> arguments = null, int bufferSize = 4096) { // Initialize fields this.fileName = fileName; this.IsDisposed = false; // Prepare startup info and redirect standard IO handles var startupInfo = new StartupInfo(); this.RedirectStandardIoHandles(ref startupInfo, bufferSize); // Create restricted token var restrictedToken = this.CreateRestrictedToken(); // Set mandatory label this.SetTokenMandatoryLabel(restrictedToken, SecurityMandatoryLabel.Low); var processSecurityAttributes = new SecurityAttributes(); var threadSecurityAttributes = new SecurityAttributes(); this.processInformation = default(ProcessInformation); const uint CreationFlags = (uint)( CreateProcessFlags.CREATE_SUSPENDED | CreateProcessFlags.CREATE_BREAKAWAY_FROM_JOB | CreateProcessFlags.CREATE_UNICODE_ENVIRONMENT | CreateProcessFlags.CREATE_NEW_PROCESS_GROUP | CreateProcessFlags.DETACHED_PROCESS | // http://stackoverflow.com/questions/6371149/what-is-the-difference-between-detach-process-and-create-no-window-process-creat CreateProcessFlags.CREATE_NO_WINDOW) | (uint)ProcessPriorityClass.High; string commandLine; if (arguments != null) { var commandLineBuilder = new StringBuilder(); commandLineBuilder.AppendFormat("\"{0}\"", fileName); foreach (var argument in arguments) { commandLineBuilder.Append(' '); commandLineBuilder.Append(argument); } commandLine = commandLineBuilder.ToString(); } else { commandLine = fileName; } if (!NativeMethods.CreateProcessAsUser( restrictedToken, null, commandLine, processSecurityAttributes, threadSecurityAttributes, true, // In order to standard input, output and error redirection work, the handles must be inheritable and the CreateProcess() API must specify that inheritable handles are to be inherited by the child process by specifying TRUE in the bInheritHandles parameter. CreationFlags, IntPtr.Zero, workingDirectory, startupInfo, out this.processInformation)) { throw new Win32Exception(); } this.safeProcessHandle = new SafeProcessHandle(this.processInformation.Process); // This is a very important line! Without disposing the startupInfo handles, reading the standard output (or error) will hang forever. // Same problem described here: http://social.msdn.microsoft.com/Forums/vstudio/en-US/3c25a2e8-b1ea-4fc4-927b-cb865d435147/how-does-processstart-work-in-getting-output startupInfo.Dispose(); NativeMethods.CloseHandle(restrictedToken); }
public RestrictedProcess(string fileName, string workingDirectory, IEnumerable <string> arguments = null, int bufferSize = 4096) { // Initialize fields this.fileName = fileName; this.IsDisposed = false; // Prepare startup info and redirect standard IO handles var startupInfo = new StartupInfo(); this.RedirectStandardIoHandles(ref startupInfo, bufferSize); // Create restricted token var restrictedToken = this.CreateRestrictedToken(); // Set mandatory label this.SetTokenMandatoryLabel(restrictedToken, SecurityMandatoryLabel.Low); var processSecurityAttributes = new SecurityAttributes(); var threadSecurityAttributes = new SecurityAttributes(); this.processInformation = default(ProcessInformation); const uint CreationFlags = (uint)( CreateProcessFlags.CREATE_SUSPENDED | CreateProcessFlags.CREATE_BREAKAWAY_FROM_JOB | CreateProcessFlags.CREATE_UNICODE_ENVIRONMENT | CreateProcessFlags.CREATE_NEW_PROCESS_GROUP | CreateProcessFlags.DETACHED_PROCESS | // http://stackoverflow.com/questions/6371149/what-is-the-difference-between-detach-process-and-create-no-window-process-creat CreateProcessFlags.CREATE_NO_WINDOW) | (uint)ProcessPriorityClass.High; string commandLine; if (arguments != null) { var commandLineBuilder = new StringBuilder(); commandLineBuilder.AppendFormat("\"{0}\"", fileName); foreach (var argument in arguments) { commandLineBuilder.Append(' '); commandLineBuilder.Append(argument); } commandLine = commandLineBuilder.ToString(); } else { commandLine = fileName; } if (!NativeMethods.CreateProcessAsUser( restrictedToken, null, commandLine, processSecurityAttributes, threadSecurityAttributes, true, // In order to standard input, output and error redirection work, the handles must be inheritable and the CreateProcess() API must specify that inheritable handles are to be inherited by the child process by specifying TRUE in the bInheritHandles parameter. CreationFlags, IntPtr.Zero, workingDirectory, startupInfo, out this.processInformation)) { throw new Win32Exception(); } this.safeProcessHandle = new SafeProcessHandle(this.processInformation.Process); // This is a very important line! Without disposing the startupInfo handles, reading the standard output (or error) will hang forever. // Same problem described here: http://social.msdn.microsoft.com/Forums/vstudio/en-US/3c25a2e8-b1ea-4fc4-927b-cb865d435147/how-does-processstart-work-in-getting-output startupInfo.Dispose(); NativeMethods.CloseHandle(restrictedToken); }