How can I map a network drive using C#. I don't want to use net use or any third party API.
Heard about UNC paths in C# code but not quite sure how to go about it.
Use the WnetAddConnection functions available in the native mpr.dll.
You will have to write the P/Invoke signatures and structures to call through to the unmanaged function. You can find resources on P/Invoke on pinvoke.net.
This is the signature for WNetAddConnection2 on pinvoke.net:
[DllImport("mpr.dll")]
public static extern int WNetAddConnection2(
ref NETRESOURCE netResource,
string password,
string username,
int flags);
Take a look # the NetShareAdd Windows' API. You'll need to use PInvoke to get your hands on it, of course.
There is no standard feature in .net to map networkdrives but you can find a good wrapper here if you dont want to execute the Native calls by yourself: http://www.codeguru.com/csharp/csharp/cs_network/windowsservices/article.php/c12357
More straight-forward solution is to use Process.Start()
internal static int RunProcess(string fileName, string args, string workingDir)
{
var startInfo = new ProcessStartInfo
{
FileName = fileName,
Arguments = args,
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardOutput = true,
CreateNoWindow = true,
WorkingDirectory = workingDir
};
using (var process = Process.Start(startInfo))
{
if (process == null)
{
throw new Exception($"Failed to start {startInfo.FileName}");
}
process.OutputDataReceived += (s, e) => e.Data.Log();
process.ErrorDataReceived += (s, e) =>
{
if (!string.IsNullOrWhiteSpace(e.Data)) { new Exception(e.Data).Log(); }
};
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
return process.ExitCode;
}
}
Once you have the above, use below create/delete mapped drive as necessary.
Converter.RunProcess("net.exe", #"use Q: \\server\share", null);
Converter.RunProcess("net.exe", "use Q: /delete", null);
Related
My question is different with the one identified. Obviously I have called "BeginErrorReadLine" method (I mark it in the code below).
I want to parse the result produced by Handle
Command line
When run in a command line environment, it will output something like:
> handle64 -p [PID]
Nthandle v4.11 - Handle viewer
Copyright (C) 1997-2017 Mark Russinovich
Sysinternals - www.sysinternals.com
10: File C:\Windows
1C: File C:\Windows\SysWOW64
[PID] is any running process ID
The output is seperated.
First 5 lines (include empty lines) go to the standard error, last 2 lines go to the standard output.
So I can strip the header by redirecting:
> handle64 -p [PID] 2>nul
10: File C:\Windows
1C: File C:\Windows\SysWOW64
Winform application
Then I try to implement this command in a C# winform application:
Stream streamOut, streamErr;
var p = Process.Start(new ProcessStartInfo
{
FileName = "handle64.exe",
Arguments = "-p [PID]",
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
});
p.OutputDataReceived += (sender, e) =>
{
streamOut.Write("Output => " + e.Data);
};
p.ErrorDataReceived += (sender, e) =>
{
streamErr.Write("Error => " + e.Data);
};
p.BeginOutputReadLine();
p.BeginErrorReadLine(); // !!!
p.WaitForExit();
Then I find everything go to the standard output.
Question
Ok, I can seperate the header and the body by code.
The question is why the program's output behaves different between the 2 environments?
Can I make the result in the winform application behaves like it in the command line?
Update
For Damien's comment, I try to run the program via 'cmd', unfortunately I get the same result:
var p = Process.Start(new ProcessStartInfo
{
FileName = "cmd",
Arguments = "/C handle64.exe -p [PID]",
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
});
...
In output window:
Output =>
Output => Nthandle v4.11 - Handle viewer
Output => Copyright (C) 1997-2017 Mark Russinovich
Output => Sysinternals - www.sysinternals.com
Output =>
Output => 10: File C:\Windows
Output => 1C: File C:\Windows\SysWOW64
Error =>
Not the answer of your question, but just a suggestion to achieve what you are trying do (i.e only get handle information in Winform application):
handle tool has -nobanner switch, you can use that to skip copyright message information.
handle64.exe -pid 11624 -nobanner
This is just a sample to illustrate the problem I alluded to in my comments. It's not a fix since I don't believe there is a trivial way to fix this. I've created Main in my scratch program (called PlayAreaCSCon). If it's called with no parameters, it's acting a way similar to what I suspect Handle64.exe is doing. When called with a parameter, it contains code similar to your own, but it then launches a copy of itself with no parameters:
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
namespace PlayAreaCSCon
{
class Program
{
[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow();
static void Main(string[] args)
{
if (args.Length == 0)
{
Console.Out.WriteLine("Hello");
if (GetConsoleWindow() == IntPtr.Zero)
{
Console.Out.WriteLine("No Console window");
}
else
{
Console.Error.WriteLine("We have a console window");
}
}
else
{
Process p = Process.Start(new ProcessStartInfo
{
FileName = "PlayAreaCSCon.exe",
Arguments = "",
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
});
TextWriter streamOut = Console.Out;
TextWriter streamErr = Console.Error;
p.OutputDataReceived += (sender, e) =>
{
streamOut.WriteLine("Output => " + e.Data);
};
p.ErrorDataReceived += (sender, e) =>
{
streamErr.WriteLine("Error => " + e.Data);
};
p.BeginOutputReadLine();
p.BeginErrorReadLine(); // !!!
p.WaitForExit();
}
}
}
}
In a command prompt, I have the following session:
C:\Dev\PlayAreaCSCon\PlayAreaCSCon\bin\Debug>PlayAreaCSCon.exe
Hello
We have a console window
C:\Dev\PlayAreaCSCon\PlayAreaCSCon\bin\Debug>PlayAreaCSCon.exe a
Error =>
Output => Hello
Output => No Console window
Output =>
So even here, if Handle64.exe is calling GetConsoleWindow or any morally equivalent function, it can detect that it's not connected to a console and exhibit different behaviour. The only way you might let it get a console window would be to set CreateNoWindow to false, which I gather you probably wouldn't want to do.
Since Handle64 is closed source it's difficult to confirm that this is the specific check it's performing either. There's no non-trivial fix to this from the calling side.
As stated by Damien:
CreateNoWindow = false,
Let it create the window the immediately hide it. We could create it offscreen, but it would still show on the taskbar.
Note: this code may be no better than letting the Window appear and disappear naturally.
At the top of the class add:
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
Then your code becomes:
var p = Process.Start(new ProcessStartInfo
{
FileName = "cmd",
Arguments = "/C handle64.exe -p [PID]",
CreateNoWindow = false,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
});
p.WaitForInputIdle();
IntPtr windowHandle = p.MainWindowHandle;
if(windowHandle == 0) throw new Exception("This did not work");
// use win32 API's to hide window (May still flicker)
ShowWindow(windowHandle,0);
// ...
I am unable to test this, since I am only running Linux at the moment.
If the exception doesn't fire, you may see a flicker of a window, but you should have the proper output.
The other way I know of doing this involves inserting handlers into the Win32 message pump and respond to the specific process telling it what it needs to know to think it has a proper window when it does not. I will not publicly post any code related to this technique. Any errors would cause Windows to become unstable.
I made some changes to your code:
Stream streamOut, streamErr;
var p = Process.Start(new ProcessStartInfo
{
FileName = "handle64.exe",
Arguments = "-p [PID]",
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardInput = true, // even if no writing to std::in, still need this
RedirectStandardError = true,
});
p.OutputDataReceived += (sender, e) =>
{
streamOut.Write("Output => " + e.Data);
};
p.BeginOutputReadLine();
p.ErrorDataReceived += (sender, e) =>
{
streamErr.Write("Error => " + e.Data);
};
p.BeginErrorReadLine();
p.WaitForExit();
p.StandardInput.Close(); // call this before WaitForExit
p.WaitForExit();
I've been trying to get the console output from running psexec as a process in a c# windows forms application. I've found I can redirect the standardoutput(and standard error) to a specified text file fine in a console application, and can redirect the output when the process used is something other than PsExec (ping for instance), but when I try to use psexec from a windows forms application I usually get an empty line in my logs, or at best I've been able to get the first line. I know psexec has had issues with synchronous redirected output, but even asychronous runs into this problem, but only when used within a Windows Forms Application.
My code for the method called that runs the process:
class Tester
{
static readonly StringBuilder outputText = new StringBuilder();
static readonly StringBuilder errorText = new StringBuilder();
public void Installer(string command, string arguments)
{
using (var psexec = Process.Start(new ProcessStartInfo(
command,
arguments)
{
CreateNoWindow = true,
ErrorDialog = false,
RedirectStandardError = true,
RedirectStandardOutput = true,
UseShellExecute = false
}))
{
psexec.OutputDataReceived += (sendingProcess, outLine) =>
outputText.AppendLine(outLine.Data);
psexec.ErrorDataReceived += (sendingProcess, errorLine) =>
errorText.AppendLine(errorLine.Data);
psexec.BeginOutputReadLine();
psexec.BeginErrorReadLine();
psexec.WaitForExit();
string text = outputText.ToString();
File.AppendAllText(#"C:\test\psexec-test.log", text);
}
}
}
The above works (gives me the output from psexec I expect in a designated file) when called within a console application like this:
class Program
{
static void Main(string[] args)
{
Tester test1 = new Tester();
test1.Installer("PsExec.exe", #"-h \\remoteserver ipconfig");
}
}
However, if I call it from an equivalent Windows Forms Application like so:
public partial class Form1 : Form
{
Tester test = new Tester();
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
string sendAddress = Address.Text;
test.Installer("psexec.exe", #"-h \\remoteserver ipconfig");
} }
I only get:
Windows IP Configuration
Done!
Without the rest of the results from ipconfig.
In the larger application I made everything else does work and I know real work is done (it runs an installer on the remote machine, or multiple remote machines), but I don't get the output from psexec. Is there something I'm missing with the Windows Forms Applications, and how to get the redirection to work with psexec?
Seems like you're jumping the gun on starting the process. It surprises me you're getting what you want in a console app. But I think you want to create your process, hook the events, and then start:
public void Installer( string command, string arguments, string logFile )
{
var outputText = new StringBuilder( );
var errorText = new StringBuilder( );
var startInfo = new ProcessStartInfo( command, arguments )
{
CreateNoWindow = true,
ErrorDialog = false,
RedirectStandardError = true,
RedirectStandardOutput = true,
UseShellExecute = false
};
using ( var psexec = new Process( ) )
{
psexec.StartInfo = startInfo;
psexec.OutputDataReceived += ( _, dat ) => outputText.AppendLine( dat.Data );
psexec.ErrorDataReceived += ( _, dat ) => errorText.AppendLine( dat.Data );
psexec.Start( );
psexec.BeginOutputReadLine( );
psexec.BeginErrorReadLine( );
psexec.WaitForExit( );
File.AppendAllText( logFile, outputText.ToString( ) );
}
}
I am teaching a class and am trying to show how you shouldn't trust applications on your system blindly.
The first demo is to run netstat and output the list of sockets connected.
I have created another application with the same name, but it omits the IP passed into arg[0] from the display. The console output is the same. The goal being that you can have a file named the correct name, but not necessarily legitimate. (Obviously hashes won't match)
The next demo is something like tasklist, though I have having trouble getting the "Session Name" from processes.GetProcesses. If I run tasklist on the demo machine (XP) I can't find the value associated with it. Also, is there a easy way to sort the list, as tasklist sorts on PID. I am new to C# so bear with me.
Thanks!
Process[] procs = Process.GetProcesses();
foreach (Process proc in procs)
{
Console.WriteLine(image.PadRight(17) + pid.PadLeft(5) + sname.PadRight(16) + mem.PadLeft(12));
}
So the goal is to replicate tasklist (basic functionality) or another windows command line app to show that a real malware author could replicate all of it.
Update:
All the students will be running in on their own XP VM so I can't really have them connect somewhere else. Being on XP also eliminates the UAC issue.
Here's a way to just use any command-line app and inspect the text (and modify it if you wish):
var exeName = #"tasklist.exe";
var arguments = "";
var process = new Process
{
EnableRaisingEvents = true,
StartInfo = new ProcessStartInfo(exeName)
{
Arguments = arguments,
CreateNoWindow = true,
UseShellExecute = false,
WindowStyle = ProcessWindowStyle.Hidden,
RedirectStandardError = true,
RedirectStandardOutput = true,
},
};
process.OutputDataReceived +=
(sender, e) => Console.WriteLine("received output: {0}", e.Data);
process.Start();
process.BeginOutputReadLine();
process.WaitForExit();
You can get the image name from the Process.Modules list (first element), the pid from the Process.Id property, and the memory from Process.Working set; but, you'll have to pinvoke WTSQuerySessionInformation to get the session name. For example:
foreach(Process p in Process.GetProcesses())
{
IntPtr buffer;
uint bytesReturned;
WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, (uint) p.SessionId, WTS_INFO_CLASS.WTSWinStationName, out buffer, out bytesReturned);
var sessionName = Marshal.PtrToStringAnsi(buffer);
WTSFreeMemory(buffer);
string moduleName = p.ProcessName;
try
{
moduleName = p.Modules[0].ModuleName;
}
catch(Exception ex)
{
ex = ex;
}
Console.WriteLine(String.Format("{0,-17} {1,5} {2,-16} {3,12} {4,12} K", moduleName, p.Id, sessionName, p.SessionId, (p.WorkingSet64 / 1024).ToString("n0")));
}
Which assumes the following is declared in the class:
enum WTS_INFO_CLASS
{
WTSInitialProgram,
WTSApplicationName,
WTSWorkingDirectory,
WTSOEMId,
WTSSessionId,
WTSUserName,
WTSWinStationName,
WTSDomainName,
WTSConnectState,
WTSClientBuildNumber,
WTSClientName,
WTSClientDirectory,
WTSClientProductId,
WTSClientHardwareId,
WTSClientAddress,
WTSClientDisplay,
WTSClientProtocolType,
WTSIdleTime,
WTSLogonTime,
WTSIncomingBytes,
WTSOutgoingBytes,
WTSIncomingFrames,
WTSOutgoingFrames,
WTSClientInfo,
WTSSessionInfo
};
[DllImport("wtsapi32.dll", ExactSpelling = true, SetLastError = false)]
public static extern void WTSFreeMemory(IntPtr memory);
[DllImport("Wtsapi32.dll", SetLastError = true)]
static extern bool WTSQuerySessionInformation(
IntPtr hServer,
uint sessionId,
WTS_INFO_CLASS wtsInfoClass,
out IntPtr ppBuffer,
out uint pBytesReturned
);
How can I map a network drive using C#. I don't want to use net use or any third party API.
Heard about UNC paths in C# code but not quite sure how to go about it.
Use the WnetAddConnection functions available in the native mpr.dll.
You will have to write the P/Invoke signatures and structures to call through to the unmanaged function. You can find resources on P/Invoke on pinvoke.net.
This is the signature for WNetAddConnection2 on pinvoke.net:
[DllImport("mpr.dll")]
public static extern int WNetAddConnection2(
ref NETRESOURCE netResource,
string password,
string username,
int flags);
Take a look # the NetShareAdd Windows' API. You'll need to use PInvoke to get your hands on it, of course.
There is no standard feature in .net to map networkdrives but you can find a good wrapper here if you dont want to execute the Native calls by yourself: http://www.codeguru.com/csharp/csharp/cs_network/windowsservices/article.php/c12357
More straight-forward solution is to use Process.Start()
internal static int RunProcess(string fileName, string args, string workingDir)
{
var startInfo = new ProcessStartInfo
{
FileName = fileName,
Arguments = args,
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardOutput = true,
CreateNoWindow = true,
WorkingDirectory = workingDir
};
using (var process = Process.Start(startInfo))
{
if (process == null)
{
throw new Exception($"Failed to start {startInfo.FileName}");
}
process.OutputDataReceived += (s, e) => e.Data.Log();
process.ErrorDataReceived += (s, e) =>
{
if (!string.IsNullOrWhiteSpace(e.Data)) { new Exception(e.Data).Log(); }
};
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
return process.ExitCode;
}
}
Once you have the above, use below create/delete mapped drive as necessary.
Converter.RunProcess("net.exe", #"use Q: \\server\share", null);
Converter.RunProcess("net.exe", "use Q: /delete", null);
i am using the ghostscriptsharp wrapper for c# and ghostscript. I want to generate thumbnails out of pdf-files.
There are different Methods imported form the ghostscript-c-dll "gsdll32.dll".
[DllImport("gsdll32.dll", EntryPoint = "gsapi_new_instance")]
private static extern int CreateAPIInstance(out IntPtr pinstance,
IntPtr caller_handle);
[DllImport("gsdll32.dll", EntryPoint = "gsapi_init_with_args")]
private static extern int InitAPI(IntPtr instance, int argc, IntPtr argv);
//...and so on
I am using the GhostscriptWrapper for generating the thumbnails in a webapplication (.net 2.0). This class uses the methods imported above.
protected void Page_Load(object sender, EventArgs e){
GhostscriptWrapper.GeneratePageThumb("c:\\sample.pdf", "c:\\sample.jpg", 1, 100, 100);
}
When i debug the Web-Application in Visual Studio 2008 by hitting key "F5" it works fine (a new instance of webserver is generated). When i create a WindowsForm Application it works too. The thumbnails get generated.
When i access the application with the webbrowser directly (http://localhoast/mywebappliation/..) it doesn't work. No thumbnails are generated. But there is also no exception thrown.
I placed the gsdll32.dll in the system32-folder of windows xp. The Ghostscript Runtime is installed too. I have given full access in the IIS-Webproject (.Net 2.0).
Does anybody know why i can't access Ghostscript from my webapplication? Are there any security-issues for accessing dll-files on the IIS-Server?
Greetings
Klaus
Try changing the current directory
string workingDirectory = #"C:\tmp";
Directory.SetCurrentDirectory(workingDirectory);
GhostscriptWrapper.GeneratePageThumb("c:\\sample.pdf", "c:\\sample.jpg", 1, 100, 100);
I now have a workaround. I am not accessing Ghostscript with the GhostscriptWrapper-Class. Instead i access the cmd.exe on the server directly. The following method takes a command (ghostscript syntax) and runs it in cmd.exe. I used the following method for doing this:
public static string runCommand(string workingDirectory, string command)
{
// Create the ProcessInfo object
System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo("cmd.exe");
psi.UseShellExecute = false;
psi.RedirectStandardOutput = true;
psi.RedirectStandardInput = true;
psi.RedirectStandardError = true;
psi.WorkingDirectory = workingDirectory;
// Start the process
System.Diagnostics.Process proc = System.Diagnostics.Process.Start(psi);
// Attach the output for reading
System.IO.StreamReader sOut = proc.StandardOutput;
// Attach the in for writing
System.IO.StreamWriter sIn = proc.StandardInput;
sIn.WriteLine(command);
// strm.Close();
// Exit CMD.EXE
string stEchoFmt = "# {0} run successfully. Exiting";
// sIn.WriteLine(String.Format(stEchoFmt, targetBat));
sIn.WriteLine("EXIT");
// Close the process
proc.Close();
// Read the sOut to a string.
string results = sOut.ReadToEnd().Trim();
// Close the io Streams;
sIn.Close();
sOut.Close();
// Write out the results.
string fmtStdOut = "<font face=courier size=0>{0}</font>";
return String.Format(fmtStdOut, results.Replace(System.Environment.NewLine, "<br>"));
}
Its possible that the identity you are running the web site under does not have write permissions for c:\