Reputation: 10390
Update
Error message with below attempt:
I am seeing this error output from TEST.EXE (retrieved with 2> in CMD line):
ERROR: Logon failure: unknown user name or bad password.
But not seeing any error from the process which is calling the EXE
Tried, as per the below suggestions (I think) to create a process which calls the process which I need:
public static void getProc()
{
SecureString ss = CreatePW();
ProcessStartInfo startInfo = new ProcessStartInfo("cmd", "\"/C cd C:\\users\\user.name\\desktop & TEST\"")
{
WindowStyle = ProcessWindowStyle.Hidden,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
WorkingDirectory = @"C:\windows\system32",
Verb = "runas",
Domain = "DOMAIN",
UserName = "zzkillcitrix",
Password = ss
};
string asd = "";
Process proc = Process.Start(startInfo);
proc.OutputDataReceived += (x, y) => asd += y.Data;
proc.BeginOutputReadLine();
proc.WaitForExit();
}
Where TEST.EXE is a build of the following(sends the argument to CMD):
public static void getProc()
{
SecureString ss = CreatePW();
ProcessStartInfo startInfo = new ProcessStartInfo("cmd", "Command_which_requires_user_authentication_goes_here")
{
WindowStyle = ProcessWindowStyle.Hidden,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
};
Process proc = Process.Start(startInfo);
proc.OutputDataReceived += (x, y) => asd += y.Data;
proc.BeginOutputReadLine();
proc.WaitForExit();
}
When running TEST.exe by double clicking, I am still seeing the CMD window. When running the above (top method), TEST.exe doesn't appear to be running, as I am not seeing the messagebox windows.
This is probably because the user credentials are not being passed to the EXE file.
I have a process which I call using the following ProcessStartInfo details:
ProcessStartInfo startInfo = new ProcessStartInfo("cmd", "/C tasklist /S " + server + " /FI \"SESSION eq " + sessID + "\" /FO CSV /NH")
{
WindowStyle = ProcessWindowStyle.Minimized,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
WorkingDirectory = @"C:\windows\system32",
Verb = "runas",
Domain = "DOMAIN",
UserName = "zzkillcitrix",
Password = ss,
}
From MSDN I know that when running a Process such as this as a user, the CreateNoWindow and WindowStyle properties do not work. This includes setting WindowStyle = ProcessWindowStyle.Hidden.
I am therefore trying to achieve a Console-less Process.Start by declaring the following:
[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
const int SW_HIDE = 0;
const int SW_SHOW = 5;
And then calling proc like so:
Process proc = Process.Start(startInfo);
var handle = proc.MainWindowHandle;
ShowWindow(handle, SW_HIDE);
proc.OutputDataReceived += (x, y) => procList.Add(y.Data);
proc.BeginOutputReadLine();
proc.WaitForExit();
When the process runs, a console window still flashes up briefly. Am I trying to hide a Window Handle where this isn't being recognised as a Console Window?
I have also tried calling the ShowWindow method, generally, at the beginning of the Program, but the console window still appears.
Would appreciate any guidance on this at all.
Upvotes: 1
Views: 1562
Reputation: 10390
The only way of doing this I could find that actually worked is by running the commands via a remote PowerShell session as a user who has full domain access:
static class Ps
{
private const string RemoteHost = "ADMINSERVER.DOMAIN1.co.uk";
private static string _errors;
/// <summary>
/// Executes the given command over a remote powershell session
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
public static Collection<PSObject> RemoteCommand(string args)
{
SupportMi.Trace = $"Remote Command({args}) {{";
var script = BuildScript(args);
var results = Execute(script);
SupportMi.Trace = $"RES: {results[0]} ERR: {_errors} }}";
return results;
}
/// <summary>
/// Takes a complete script and executes it over a powershell Runspace. In this case it is being
/// sent to the server, and the results of the execution are checked for any errors.
/// </summary>
/// <param name="script"></param>
/// <returns></returns>
private static Collection<PSObject> Execute(string script)
{
var results = new Collection<PSObject>();
// Using a runspace
using (var runspace = RunspaceFactory.CreateRunspace())
{
runspace.Open();
using (var pipeline = runspace.CreatePipeline())
{
pipeline.Commands.AddScript(script);
try
{
results = pipeline.Invoke();
var errors = pipeline.Error.Read(pipeline.Error.Count);
foreach (var error in errors)
{
_errors += error;
}
}
catch (Exception ex)
{
results.Add(new PSObject(ex.Message));
SupportMi.Trace = ex.Message;
}
}
}
return results;
}
/// <summary>
/// Takes a string argument to be sent to the remote powershell session and arranges it in the correct format,
/// ready to be sent to the server.
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
private static string BuildScript(string args)
{
// Build the script
var script = Creds() + Environment.NewLine +
$"Invoke-Command -session $sessions -ScriptBlock {{ {args} /U DOMAIN1\\adminusername /P adminpassword }}" + Environment.NewLine +
"Remove-PSSession -Session $sessions" + Environment.NewLine +
"exit";
return script;
}
/// <summary>
/// Returns the credentials for a remote PowerShell session in the form
/// of a few lines of PowerShell script.
/// </summary>
/// <returns></returns>
private static string Creds()
{
return "$pw = convertto-securestring -AsPlainText -Force -String \"adminpassword\"" + Environment.NewLine +
"$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist \"DOMAIN1\\adminusername\",$pw" + Environment.NewLine +
$"$sessions = New-PSSession -ComputerName {RemoteHost} -credential $cred";
}
}
So to maintain a console-less action we can call the RemoteCommand
method and pass in the command we want to send. RemoteCommand
will first build our query/command using BuildScript
and Creds
(for the admin credentials).
This is then sent to the Execute
method, which creates a new PowerShell RunSpace
, which can run the command.
Some caveats of using this method :
ADMINSERVER
)Upvotes: 0
Reputation: 6103
Change
WindowStyle = ProcessWindowStyle.Minimized
To
WindowStyle = ProcessWindowStyle.Hidden;
Upvotes: 1