Why is my application failing when launched remotely? - c#

I have a Windows application that sits on four different servers, its purpose is to look through a directory, find .wav files and copy them to a remote location. It works perfectly when ran from directory it's in on the server, but I don't like going to four different servers to execute the application. So below, I have a console app that is really just telling the user to store the IDs for the wav files in the right location so the four applications can see them, then executes the various programs. The problem is all four apps crash immediately after launch, I think it has something to do with how I'm executing the command, but I can't figure it out. Any ideas?
class Program
{
static void Main()
{
Console.SetWindowSize(90, 35);
Console.WriteLine(#"Place the ID of the interaction(s) you wish to recover in \\SERVER\c$\IDKeys.txt." + Environment.NewLine);
Console.WriteLine("Press Enter to find interactions.");
Console.ReadLine();
exeCmd();
}
static void exeCmd()
{
string[] executable =
{
#"\\Server1\C$\App\findwavs.exe",
#"\\Server2\C$\App\findwavs.exe",
#"\\Server3\C$\App\findwavs.exe",
#"\\Server4\C$\App\findwavs.exe"
};
foreach (string exe in executable)
{
Process proc = new Process();
proc.StartInfo.FileName = exe;
proc.Start();
}
}
}

The code you have executes the executable on the local machine.
Using #"\\Server1\C$\App\findwavs.exe does not mean that the executable will run on Server1, it just means that your local machine will look in this network path to obtain the executable file, but will run it locally.
You can use PsExec to execute an application on a remote machine.
Consider the following code that uses PsExec to run an executable on a remote machine:
string application = "c:\\app\\findwavs.exe";
string arguments = ""; //use this if you want to pass any command line arguments to the findwavs.exe executable
string location_of_psexec = "c:\\pstools\\psexec.exe"; //location of Psexec.exe on local machine
string remote_machine = "Server1"; //name of remote machine
Process process = new Process();
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.FileName = location_of_psexec;
process.StartInfo.Arguments = string.Format("\\\\{0} -c \"{1}\" \"{2}\"", remote_machine, application, arguments);
This code assumes that the application path (e.g. "c:\app\findwavs.exe") is relative to your local machine, not the remote machine.
If you want to use a path relative to the remote machine, simply remove the -c from the value of process.StartInfo.Arguments
UPDATE:
Quoting from the above reference:
If you omit a user name, the process will run in the context of your account on the remote system, but will not have access to network resources (because it is impersonating). Specify a valid user name in the Domain\User syntax if the remote process requires access to network resources or to run in a different account. Note that the password and command are encrypted in transit to the remote system.
To make you application able to access network resources, use the -u and -p switches to supply a username and a password.

Running your code would be equivalent to navigating to \\Server1\C$\App\findwavs.exe in Explorer and double clicking on the exe. What I mean by this is that it will run the program on the machine that told it to run, not where the exe is stored.
The easiest way I can see to accomplish the task you want to perform would be to use PsExec.

Related

Launch an application on a remote PC using c# [duplicate]

I'm trying to execute notepad.exe on a remote machine, but it's not working now. What am I missing?
var ui = new ImpersonateUser();
//the process to restart
const string processName = "notepad.exe";
var serverName = "serverName";
try
{
//Use adbadmin for access
ui.Impersonate(_domain, _userName, _pass);
//Start the process
ProcessStartInfo info = new ProcessStartInfo("C:\\PsTools");
info.FileName = #"C:\PsTools\psexec.exe";
info.Arguments = #"""\\" + serverName + #"C:\WINDOWS\notepad.exe""";
info.RedirectStandardOutput = true;
info.UseShellExecute = false;
Process p = Process.Start(info);
lblStatusResponse.Text = "Service " + processName + " was restarted correctly.";
}
catch (Exception ex)
{
lblStatusResponse.Text = ex.ToString();
}
finally
{
ui.Undo();
}
This gives me no exception, but I'm surely missing something...
The answer was a combination from your replies. But the whole correct solution was:
ProcessStartInfo info = new ProcessStartInfo("C:\\PsTools");
info.FileName = #"C:\PsTools\psexec.exe";
info.Arguments = #"\\" + serverName + #" -i C:\WINDOWS\notepad.exe";
info.RedirectStandardOutput = true;
info.UseShellExecute = false;
Process p = Process.Start(info);
Running an interactive program such as notepad.exe doesn't always open a visible window on the target PC. Try opening Task Manager on the target PC while you run the code and see if notepad.exe appears in the list of running processes.
I'd suggest trying to run psexec from the command line first before attempting to call it via code.
psexec \\serverName "notepad.exe"
You may also want to use the "interactive" flag so it can interact with the desktop on the target.
psexec \\serverName "notepad.exe" -i
Your UNC path does not look good.
After the string concatenation, it will look something like
"\\serverNameC:\WINDOWS\notepad.exe"
Try to print it out. See documentation about UNC on MSDN, and some examples here (or google about UNC Path)
If you have only the default shares, it should be something like
"\\serverName\C$\WINDOWS\notepad.exe"
OR psexec let you use a different notation, but you have to be careful with double quotes there
psexec \\serverName"c:\WINDOWS\notepad.exe"
Also, ensure that the "Server" service is running on the target machine.
PsExec starts an executable on a remote system and controls the input and output streams of the executable's process so that you can interact with the executable from the local system. PsExec does so by extracting from its executable image an embedded Windows service named Psexesvc and copying it to the Admin$ share of the remote system. PsExec then uses the Windows Service Control Manager API, which has a remote interface, to start the Psexesvc service on the remote system.
The Admin$ share is created and managed by the "Server" service, so you need it running for psexec to work (http://windowsitpro.com/systems-management/psexec).
Just to extend current version of answer.
There could be a problem of execution psexec due to OS policy, to enable remote control you need to modify registry:
reg add HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\system /v LocalAccountTokenFilterPolicy /t REG_DWORD /d 1 /f
You can check this: Remote UAC LocalAccountTokenFilterPolicy

Process.Start(); as another user works but explorer.exe iexplore.exe throws exception

I'm having problems with a program and its buttons (I know, I'm awesome lol) the buttons can be "programmed" to run programs. They also can be set to run as admin (different credentials).
If I set up simply notepad or cmd or explorer it runs like charm. But if I start iexplore it has got no admin rights.
I had problems before with running explorer.exe the solution was that I had to run it by typing the full path C:\windows\explorer.exe to be able to run it but that I solved it by setting up the VB2015 compiler (?) to Platform target: x64.
My other problem is that if I try to run dsa.msc or generally anything ends with msc it throws the following exception, even if I set up the full path to the syswow64 (or the system32) folder like c:\windows\syswow64\dsa.msc
"The specified executable is not a valid application for this OS platform."
Running the C:\Windows\System32\mmc.exe "services.msc" (or syswow64, with or without the /computer= switch) throws
"The requested operation requires elevation." which I have since I'm able to run services.msc (and all other msc-s from command line with the same user rights)
Thank you.
A beginner.
Basically you don't need to run the host app as administrator! There is a variable (inside your Process instance) called StartInfo (which is an instance of the ProcessStartInfo Class), where Verbs could be used as followed:
Process p = new Process()
{
StartInfo = new ProcessStartInfo("E:\\Users\\Temp\\app.exe")
{
Verb = "runas"
}
};
p.Start();
This will prompt the user to run the app.exe as administrator.
Edit
Running a Process as a defined user:
Process p = new Process()
{
StartInfo = new ProcessStartInfo("E:\\Users\\Temp\\app.exe")
{
Verb = "runas",
Arguments = "/user:Vira"
}
};
For more information about those RUNAS Arguments, click me! :)

Process on ASP.Net server not running correctly over IIS

I am trying to run an antivirus scan on an uploaded file in an ASP.Net web app. We are using Sophos so have access to their command line API sav32cli. In the code I use:
Process proc = new Process();
proc.StartInfo.FileName = #"C:\Program Files (x86)\Sophos\Sophos Anti-Virus\sav32cli.exe";
proc.StartInfo.Arguments = #"-remove -nc " + SavedFile;
proc.StartInfo.Verb = "runas";
proc.Start();
proc.WaitForExit();
int exitCode = proc.ExitCode;
When stepping through the code, when attached to the w3wp process on dev server, the code just jumps from one line to the next seemingly doing nothing at all. When running from code on dev server, it performs as expected scanning file and deleting if it is seen as a virus.
The server is running IIS 8.0, and the app built in .Net Framework 4. I have changed the machine config to allow the process to run as SYSTEM account, in accordance to these instructions. https://support.microsoft.com/en-us/kb/317012#%2Fen-us%2Fkb%2F317012
<processModel userName="SYSTEM" password="AutoGenerate" />
Is there something I'm missing? What is the best practice for this kind of implementation?
EDIT: When called, the Process returns an ExitCode of 2 (Error stopped execution), rather than the expected 0 (Scan worked, no viruses), or 3 (Scan worked, viruses found).
EDIT 2: As per comment below I changed the code to:
Process proc = new Process();
proc.StartInfo.FileName = #"C:\Program Files (x86)\Sophos\Sophos Anti-Virus\sav32cli.exe";
proc.StartInfo.Arguments = #"-remove -nc " + SavedFile;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.UseShellExecute = false;
proc.Start();
StringBuilder output = new StringBuilder();
while (!proc.StandardOutput.EndOfStream)
{
string line = proc.StandardOutput.ReadLine();
output.AppendLine(line);
}
proc.WaitForExit();
int exitCode = proc.ExitCode;
ASPxMemo2.Text = exitCode.ToString() + Environment.NewLine + output.ToString();
output is always empty when run over IIS, but is populated correctly when running from code.
EDIT 3: Instead of looking at StandardOutput we looked at StandardError and it revealed this error:
Error initialising detection engine [0xa0040200]
(Possible insufficient user Admin rights.)
For the time being we are going to move to another method of virus checking, but would still like to know a possible solution if anyone has it.
You will need to make sure that the application pool that is running your .NET application inside IIS has execute permissions to your file
"C:\Program Files (x86)\Sophos\Sophos Anti-Virus\sav32cli.exe"
You may also need to add this permission to the folder location where the file to be scanned is uploaded (c:\temp) for example
You may also need to have administrator privileges to run the anti virus scan since IIS8 does not run as an administrator. When you are debugging visual studio uses your current logged in windows user(unless you use runas) so this will explain why it would work when debugging.
Have you tried running your web process in elevated trust?
Configuring .NET Trust Levels in IIS 7
<system.web>
<securityPolicy>
<trustLevel name="Full" policyFile="internal"/>
</securityPolicy>
</system.web>
ASP.NET Trust Levels and Policy Files
Most likely the permissions are not configured correctly on the content being scanned (the uploads folder) or the worker process user doesn't have the full permissions it needs to use Sophos. You know the executable itself is accessible by the worker process because you are getting exit codes and error messages that are specific to Sophos.
Because your process will delete files that are perceived as threats you need to grant the user running the process modify or full control permissions on the folders that store the uploaded files.
By default you could use the IIS_IUSRS group for ApplicationPoolIdentity processes, but you can verify (and modify) the user in IIS Manager > App Pools > Advanced.
This answer has more details
Here are some ideas:
Create the process using a different user with elevated privileges on the folder, see for reference start-a-net-process-as-a-different-user
If the previous suggestion fails, login one time on the server using the credentials used in point 1. It will configure registry entries connected to the user profile, some programs requires it.
Develop a simple .net service running on the server and monitoring the upload folder. The service has more probability running the Sophos scan succesfully. Here is a reference on service creation using .net.
The service may also talk to your web page using DB/file system/ etc.. so the operation may seem synchronous.
These are my 4 cents :-)

How to start an exe from a .NET Windows Service for updating the service

I have a windows service that I would like to be automatically and silently updated. I started using wyBuild to implement this, but have had some issues with it, and decided to try to build my own. I've written a standalone exe that can be called to do the update procedure: checks for a new zip file with the update, downloads it, unzips, stop the windows service, copy files from the zip, then restart the service. This exe works fine when I run it from the commandline and wasn't really difficult to write.
However, now I would like the service (the same one being updated) to shell out to the updater exe to update itself. I first tried Process.Start:
var proc = Process.Start(pathToUpdaterExe);
proc.WaitForExit(60000);
This called the updater, but when the updater stops the service, the process is killed and the update stops. I did some searching and it sounds like the solution is to use a separate AppDomain. This is what I have now:
Evidence baseEvidence = AppDomain.CurrentDomain.Evidence;
Evidence objEvidence = new System.Security.Policy.Evidence(baseEvidence);
AppDomainSetup setup = new AppDomainSetup();
var updateDomain = AppDomain.CreateDomain("updateDomain", objEvidence, setup);
updateDomain.ExecuteAssembly(updater);
AppDomain.Unload(updateDomain);
However, now I get the error System.IO.IOException: "The process cannot access the file 'C:\Program Files (x86)\Company\Service\Service.dll' because it is being used by another process" when attempting to copy over the new Service.dll
Again, I've stopped the service at this point. I've confirmed this with logging. I can't imagine what would have Service.dll still locked, so I added code to check to see what is locking it:
public static IEnumerable<Process> GetProcessesLocking(string filePath)
{
var result = new List<Process>();
result.Clear();
var processes = Process.GetProcesses();
foreach (Process proc in processes)
{
try
{
if (proc.HasExited) continue;
foreach (ProcessModule module in proc.Modules)
{
if ((module.FileName.ToLower().CompareTo(filePath.ToLower()) == 0))
{
result.Add(proc);
break;
}
}
}
catch (Exception ex)
{
Log(ex.ToString());
Log("There was an error checking " + proc.ProcessName );
}
}
return result;
}
However this code indicates that nothing has a lock on the dll (result is empty and nothing is logged indicating an error).
I suspect I'm running afoul of some UAC issue that is the real cause of the IOException. The windows service runs as LocalSystem. All that to ask: How should I be running the update exe from the windows service such that it has rights to copy files in c:\Program Files?
Update
As the comments and answer suggest, Process.Start can work, but there is some nuance. You have to start cmd.exe and use it to start the updater. I also found I could not use a full path for the updater exe and that I needed to set UseShellExecute=false. This is my final working code that launches the updater from the .NET service:
var cmd = "/c start updater.exe";
var startInfo = new ProcessStartInfo("cmd.exe");
startInfo.Arguments = cmd;
startInfo.WorkingDirectory = AssemblyDirectory;
startInfo.UseShellExecute = false;
var proc = Process.Start(startInfo);
I did this exact thing - using a simpler (some might say kludgy) approach. The service:
Produces a batch command,
Downloads the new executables to a staging location,
Starts a process: cmd.exe which, in turn, runs the batch script w/o waiting for it to complete, and then
Immediately terminates itself.
The batch command:
Pings 127.0.0.1 five times,
Copies the executables to the final location and,
Restarts the service.
Works like clockwork. The ping is a reliable 5 second delay - lets the service shutdown before copying the files.
Edit:
Just for completeness - I realized that by batch cmd is pinging 127.0.0.1 not 128.0.0.1 and so I edited this answer to reflect that. I suppose either works - but 128.0.0.1 pings timeout, where 127.0.0.1 resolves to "me". Since I'm only using it as a poor-man's delay, it serves the purpose either way.

run shell command (manage-bde) as administrator from C#

I need to run "manage-bde" shell command from C# code.
The main application process is already running as administrator and is Elevated.
I used code from : UAC self-elevation example on MS website for confirming the app process is elevated.
(http://code.msdn.microsoft.com/windowsdesktop/CSUACSelfElevation-644673d3)
However, when I try to run manage-bde from the C# code, I get "System can't find file specified".
Process p = new Process();
p.StartInfo.FileName = "C:\\Windows\\System32\\manage-bde.exe";
p.StartInfo.UseShellExecute = true;
p.Start();
As a workaround, I tried to create a batch file that runs the command.
string batchFileName = DateTime.Now.Ticks + ".bat";
StreamWriter writer = new StreamWriter(batchFileName);
writer.WriteLine("manage-bde");
writer.Flush();
writer.Close();
Process p = new Process();
p.StartInfo.FileName = batchFileName;
p.StartInfo.UseShellExecute = true;
p.Start();
The batch file is written , and executed successfully; However, the command "manage-bde" is not recognized.
I changed the code to use the verb "runas" and use admin password and that works, but I want the batch file to work without the need for providing the admin password. The current logged in user is already administrator on the computer but the batch file is not getting executed with the existing admin privileges . I need the batch file to execute and manage-bde to run successfully.
Your help or advice will be very highly appreciated :)
ps: some commands other than manage-bde work fine without need for admin runas.
The reason of the behavior I encountered was the Windows File System Redirector.
In most cases, whenever a 32-bit application attempts to access %windir%\System32, the access is redirected to %windir%\SysWOW64
https://msdn.microsoft.com/en-us/library/windows/desktop/aa384187%28v=vs.85%29.aspx
My application build was 32 bits. Whenever it tried to access System32 windows automatically redirected it to SysWow64 which does not contain "manage-bde.exe". I changed the build to 64 bits and then the application could access manage-bde.exe from System32
Even if you're running as the Administrator user, you're not fully elevated if UAC is running. Meaning that you'll have either the UAC prompt come up or you'll be prompted for a password.
The only real way you could get around that is to run your application elevated first, or to write a service that runs with elevated permissions to start your new process.
The alternative of course is to disable UAC, but that is undesirable in most situations.

Categories