I often need to launch external processes so I wrote a convenient method to easily do that. One of these processes needs to trigger UAC in order to ask the user for their permission. I did some research and found that setting the Verb property of the ProcessStartInfo object to runas in addition to setting the UseShellExecute to true should do the trick.
private static void StartProcess(string fileName, string arguments, bool elevated)
{
var start = new ProcessStartInfo
{
UseShellExecute = false,
CreateNoWindow = true,
Arguments = arguments,
FileName = fileName
};
if (elevated)
{
start.Verb = "runas";
start.UseShellExecute = true;
}
int exitCode = 0;
using (var proc = new Process { StartInfo = start })
{
proc.Start();
proc.WaitForExit();
exitCode = proc.ExitCode;
}
if (exitCode != 0)
{
var message = string.Format(
"Error {0} executing {1} {2}",
exitCode,
start.FileName,
start.Arguments);
throw new InvalidOperationException(message);
}
}
However, the Verb property is not available in netcore therefore I can't figure out how to achieve the same result. Any suggestion?
Related
Hi have a service which needs to execute it's updated shell script independent from the .Core process. I have tried /bin/bash, /bin/nohup and /bin/setsid. But every time the script stops the systemd service the script seems also to be stopped. How do I get this independent?
private static Process StartLinuxUpdateScript(string pathToUpdateScript, string pathToUpdateZip)
{
var process = new Process()
{
StartInfo = new ProcessStartInfo
{
FileName = "/bin/setsid",
UseShellExecute = true,
Arguments = $" bash {pathToUpdateScript} {pathToUpdateZip}"
}
};
return process;
}
maybe it helpfull:
public void RunProcess(string fileName, string arg)
{
var process = new Process
{
StartInfo =
{
RedirectStandardOutput = true,
RedirectStandardError = true,
FileName = fileName,
Arguments = arg ,
UseShellExecute = false,
CreateNoWindow = true
},
EnableRaisingEvents = true
};
process.Start();
string processOutput;
while ((processOutput = process.StandardError.ReadLine()) != null)
{
Console.WriteLine(processOutput);
}
process.Dispose();
}
I have an exe which I can run in console like below
util.exe argument1
I need to invoke this from a CSharp application which I can do like below
private string Command(string arg)
{
var p = new Process
{
StartInfo =
{
FileName = "util.exe",
Arguments = arg,
RedirectStandardOutput = true,
RedirectStandardInput = true,
CreateNoWindow = true,
UseShellExecute = false
}
};
var stdOutput = new StringBuilder();
p.OutputDataReceived += (sender, args) => stdOutput.AppendLine(args.Data);
p.Start();
p.BeginOutputReadLine();
p.WaitForExit();
return stdOutput.ToString();
}
var result = Command(argument1) //call
However there is an issue. util.exe authenticates user using the Windows logged in credentials. In the application I need to execute the commands as a different user(Like below)
util.exe login funcationaluser fuucntioanluserpwd
util.exe argument1
What is the right way for doing this? Can I reuse the process?
Edit: Please note the username and password is specific to util.exe , not a system username/password
The next C# method invokes rasdial VPN-Name name *. * means enter password from keyboard. But I want to pass the password to the process from C# application.
public bool connect(UserCredentials userCredentials)
{
// "Dial" to VPN
var psi = new ProcessStartInfo("rasdial")//("cmd")
{
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
//CreateNoWindow = true,
UseShellExecute = false, // Unusable if Streams redirected
Arguments = $"{InterfaceName} {userCredentials.name} *"
};
var proc = new Process {
StartInfo = psi
//,EnableRaisingEvents = true
};
proc.Start();
// PASS PASSWORD VIA STDIN. Does not work. Keyboard input FORCED!
//proc.WaitForInputIdle();
proc.StandardInput.WriteLine(userCredentials.pass);
proc.WaitForExit();
return true;// proc.ExitCode == 0; // Can throws "Not exited yet"
}
Is there a way to pass the password to rasdial?
I can invoke rasdial VPN-Name login passwd. Such method is security lack. DEPRECATED. Any schoolboy can monitor process's args.
One workaround
static Process runProcRasdial()
{
ProcessStartInfo psi = new ProcessStartInfo("cmd") {
RedirectStandardInput=true,
RedirectStandardOutput=false,
UseShellExecute=false
};
var proc = new Process(){
StartInfo = psi,
EnableRaisingEvents = true,
};
proc.Start();
proc.StandardInput.WriteLine("rasdial rapa /disconnect");
proc.StandardInput.WriteLine("rasdial rapa Ivan paswd!");
//proc.Exited += Proc_Exited1;
proc.StandardInput.WriteLine("exit");
proc.WaitForExit();
return proc;
}
If pack paswd! to SecureString if could look like something normal.
I wrote an app in C# (WPF) which takes remote hosts data (using Psexec).
The app requires you to be with high privileges (Administrator).
I have this kind of code in my app:
var proc = new Process {
StartInfo = new ProcessStartInfo {
FileName = "psexec.exe",
Arguments = "\\\\" + ip + " ipconfig",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true
}
};
proc.Start();
if (!proc.WaitForExit(60000))
proc.Kill();
output_error = proc.StandardError.ReadToEnd();
output_stan = proc.StandardOutput.ReadToEnd();
If i'm running the app from Visual Studio (In debug mode), i get an output, but when i'm running the app from the exe file the standard redirected output which is just empty.
Does anyone has a possible solution for this?
*The output which is redirected as an error is a standrad psexec output which says basiclly that the command worked just find (error 0).
Thx.
From MSDN:
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "Write500Lines.exe";
p.Start();
// To avoid deadlocks, always read the output stream first and then wait.
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
In particular note how you shouldn't wait before reading to the end of the stream otherwise you might be getting deadlocks.
I have modified your code to do it this way, and the following works fine for me:
static void Main(string[] args)
{
var proc = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "ping.exe",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true
}
};
proc.Start();
string output_error = proc.StandardError.ReadToEnd();
string output_stan = proc.StandardOutput.ReadToEnd();
proc.WaitForExit();
Trace.TraceInformation(output_stan);
}
I have a small command that allows me to query information from CMD, however it needs admin privileges (not at my application level but at the CMD level) Only reason I am going this route is because I couldn't get WMI to query bitlocker settings for the life of me and this project needs to get off of my desk.
if (bitA.Text == "Bitlocker Available")
{
Process cmd2 = new Process();
cmd2.StartInfo.Verb = "runas";
cmd2.StartInfo.FileName = "cmd.exe";
cmd2.StartInfo.Arguments = "/c ping 8.8.8.8";
cmd2.StartInfo.UseShellExecute = false;
cmd2.StartInfo.RedirectStandardOutput = true;
cmd2.StartInfo.RedirectStandardError = true;
cmd2.Start();
//* Read the output (or the error)
string output2 = cmd2.StandardOutput.ReadToEnd();
bitB.Text = output2;
cmd2.WaitForExit();
}
You can indicate the new process should be started with elevated permissions by setting the Verb property of your startInfo object to 'runas', as follows:
startInfo.Verb = "runas";
This will cause Windows to behave as if the process has been started from Explorer with the "Run as Administrator" menu command. The user will be prompted with the UAC to confirm they want to do it.
Edit: I see you have that verb set already. Are you asking if you can circumvent the UAC? That would be kind of silly, otherwise virus-writers and so forth could get around the security check with one line of code.
As I said in my comment, the issue is that the "runas" verb requires UseShellExecute to be true, but redirection requires UseShellExecute to be false. This makes the problem sort of tricky, but the key is start a process as admin that you can communicate with via some kind of IPC, then this process starts whatever process you want to redirect output from. It can even be the same executable just switching on the arguments received. If I was writing a library, I would probably embed a shim executable as an assembly resource. Here is a simple example using named pipes:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Security.Principal;
using System.Text;
using System.Threading.Tasks;
using System.IO.Pipes;
using System.IO;
namespace AdminRedirect
{
class Program
{
private static readonly int ProcessId = Process.GetCurrentProcess().Id;
static void Main(string[] args)
{
bool isAdmin = IsAdministrator();
Console.WriteLine("Id = {0}, IsAdmin = {1}", ProcessId, isAdmin);
if (!isAdmin)
{
Console.WriteLine("Press any key to spawn the admin process");
Console.ReadKey(intercept: true);
string pipeName = "mypipe-" + Guid.NewGuid();
Process cmd = new Process()
{
StartInfo =
{
Verb = "runas",
Arguments = pipeName,
FileName = typeof(Program).Assembly.Location,
UseShellExecute = true
}
};
using (var pipeStream = new NamedPipeServerStream(pipeName))
{
cmd.Start();
Console.WriteLine("Started {0}", cmd.Id);
pipeStream.WaitForConnection();
Console.WriteLine("Received connection from {0}", cmd.Id);
using (var reader = new StreamReader(pipeStream))
{
string line;
while((line = reader.ReadLine()) != null)
{
Console.WriteLine(line);
}
}
}
Console.WriteLine("Hit any key to end");
Console.ReadKey(intercept: true);
}
else
{
if (args.Length > 0)
{
string pipeName = args[0];
Console.WriteLine("Opening client pipe: {0}", pipeName);
using (var pipeStream = new NamedPipeClientStream(pipeName))
{
pipeStream.Connect();
using (var writer = new StreamWriter(pipeStream))
{
StartChildProcess(writer);
}
}
}
else
{
Console.WriteLine("We are admin and not piping, so just run");
StartChildProcess(Console.Out);
Console.WriteLine("Hit any key to end");
Console.ReadKey(intercept: true);
}
}
}
private static bool IsAdministrator()
{
WindowsIdentity identity = WindowsIdentity.GetCurrent();
WindowsPrincipal principal = new WindowsPrincipal(identity);
return principal.IsInRole(WindowsBuiltInRole.Administrator);
}
private static void StartChildProcess(TextWriter output)
{
var cmd = new Process()
{
StartInfo =
{
FileName = "cmd.exe",
Arguments = "/c ping 8.8.8.8",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true
}
};
cmd.Start();
string procOutput = cmd.StandardOutput.ReadToEnd();
output.Write("Id: {0}, Output:{1}", cmd.Id, procOutput);
}
}
}
To use runas you must have UseShellExecute be true, however to use any of the redirect methods you must have UseShellExecute be false. You can not have both at the same time.
If you need to redirect the output and elevate at the same time you must do the following steps:
Start another process you control1 using runas and UseShellExecute = true to generate a elevated process.
The new process starts ping.exe with UseShellExecute = false and redirects the outputs.
Use a form of IPC, like WCF over named pipes, to forward the output from your elevated 2nd process to your non elevated first process.
1: it could be your same EXE but passing some special command line arguments to put it in this new mode