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
);
Related
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 need to communicate with a command line Fortran app using a c# wrapper. The Fortran process is started using the following c# code.
var process = new Process();
process.StartInfo = new ProcessStartInfo(pathToFortranExe)
{
WorkingDirectory = directory,
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardInput = true,
RedirectStandardOutput = true
};
...
//listen for prompts from the Fortran program
//and send replies using standardInput as follows
process.StandardInput.WriteLine(data);
When the Fortran program is waiting for user input on the command line I can successfully send messages using the above code.
Now here is the problem. The Fortran program uses long running analysis loops which can be interrupted by sending keys such as Esq or Q. I've been told this interrupt feature is implemented in the Fortran code using the Intel Fortran command PEEKCHARQQ. When I try and trigger these keys from c# using StandardInput they are ignored by the Fortran program. To send these interrupt signals I use:
char key = 'q'
process.StandardInput.Write(key);
//Note that StandardInput.AutoFlush==true
I've also tried SendMessage via pinvoke, but again no luck so far:
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
private const UInt32 WM_KEYDOWN = 0x0100;
private const UInt32 WM_KEYUP = 0x0101;
public static void SendKey(Process process, char key)
{
var keyCode = (IntPtr)key;
var hWnd = process.Handle;
SendMessage(hWnd, WM_KEYDOWN, keyCode, IntPtr.Zero);
SendMessage(hWnd, WM_KEYUP, keyCode, IntPtr.Zero);
}
So the question is: Are there any other ways to place keys into the keyboard buffer such that they might be picked up PEEKCHARQQ in the Fortran process? Or anything else that I might be missing here?
Update 1:
I've also tried WriteConsoleInput, but I don't think I have the right handle:
var keyCode = (short)key;
var hWnd = process.Handle;
INPUT_RECORD[] lpBuffer = new INPUT_RECORD[1];
lpBuffer[0].KeyEvent.wVirtualKeyCode = keyCode;
int nLength = lpBuffer.Length;
int lpNumberOfEventsWritten;
if (!WriteConsoleInput(
hWnd,
lpBuffer,
nLength,
out lpNumberOfEventsWritten))
{
//this results error code 6: Invalid handle
Console.WriteLine("Error: {0}", GetLastError());
}
You can write to the console input buffer for the child process using a handle to the CONIN$ device with the WriteConsoleInput API.
Two Fortran programs below to demonstrate. The program for the child process:
PROGRAM peek_a_boo
USE IFCORE
IMPLICIT NONE
CHARACTER(*), PARAMETER :: fmt = "('Press any key!')"
PRINT fmt
DO WHILE (.NOT. PEEKCHARQQ())
CALL SLEEPQQ(2000)
PRINT fmt
END DO
END PROGRAM peek_a_boo
The program that shows the Windows API calls. You can translate this to whichever language floats your boat.
PROGRAM peek_parent
USE IFWIN
IMPLICIT NONE
INTEGER(BOOL) :: api_result
INTEGER(HANDLE) :: console_input
TYPE(T_STARTUPINFO) :: startup_info
TYPE(T_PROCESS_INFORMATION) :: process_info
TYPE(T_INPUT_RECORD) :: input_record
INTEGER(DWORD) :: events_written
INTEGER(DWORD) :: wait_event
! Create the child process.
CALL ZeroMemory(LOC(startup_info), SIZEOF(startup_info))
startup_info%cb = SIZEOF(startup_info)
api_result = CreateProcess( &
'peek_a_boo.exe', &
NULL, & ! command line.
NULL, & ! process security attributes.
NULL, & ! thread security attributes.
.FALSE., & ! inherit handles.
0_DWORD, &
NULL, & ! environment.
NULL, & ! current directory.
startup_info, &
process_info )
IF (api_result == 0) THEN
ERROR STOP 'Couldn''t start it :('
END IF
api_result = CloseHandle(process_info%hThread)
! Let the child run for a bit.
CALL SLEEPQQ(5000)
! Get a handle to our console input buffer.
console_input = CreateFile( &
'CONIN$', &
GENERIC_WRITE, &
IANY([FILE_SHARE_READ, FILE_SHARE_WRITE]), &
NULL, & ! security attrs
OPEN_EXISTING, &
0_DWORD, & ! attributes
NULL ) ! template file
IF (console_input == 0) THEN
ERROR STOP 'Couldn''t open it :('
END IF
! Poke something into the buffer.
input_record%EventType = KEY_EVENT
input_record%KeyEvent%bKeyDown = .TRUE.
input_record%KeyEvent%wRepeatCount = 1
input_record%KeyEvent%wVirtualKeyCode = INT(Z'51', WORD)
input_record%KeyEvent%wVirtualScanCode = INT(Z'51', WORD)
input_record%KeyEvent%AsciiChar = 'Q'
input_record%KeyEvent%dwControlKeyState = 0
api_result = WriteConsoleInput( &
console_input, &
input_record, &
1, &
LOC(events_written) )
! Wait for the child to terminate.
wait_event = WaitForSingleObject(process_info%hProcess, INFINITE)
api_result = CloseHandle(console_input)
api_result = CloseHandle(process_info%hProcess)
END PROGRAM peek_parent
I need to write some code that would prompt a message "printer is connected" when the printer is plugged into the computer and also prompt a message "printer not connected" when I plug out the printer from computer. I also want to list the printers available through a combobox. How can I do this in C# using Visual Studio?
You should use Winspool.lib
C# Signature :
[DllImport("winspool.Drv", EntryPoint = "OpenPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter, out IntPtr hPrinter, IntPtr pd);
EDIT:
You can also use this
foreach (string printer in System.Drawing.Printing.PrinterSettings.InstalledPrinters)
{
//Add in combo box
}
to immediately get the pop-up that new Printer Found/Disconnected... you must have to run some code in background continuously Windows Service is the best for that.. and using the below code you can get the installed printer so first store the currently installed printer in list and after each 10(or any you want) second get the installed printer again if difference found propmt the message accordingly..
this is the snippet to get the installed printer..
private string[] GetAvailablePrinters()
{
var installedPrinters = new string[PrinterSettings.InstalledPrinters.Count];
PrinterSettings.InstalledPrinters.CopyTo(installedPrinters, 0);
var printers = new List<string>();
var printServers = new List<string>();
var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_Printer");
foreach (var printer in searcher.Get())
{
var serverName = #"\\" + printer["SystemName"].ToString().TrimStart('\\');
if (!printServers.Contains(serverName))
printServers.Add(serverName);
}
foreach (var printServer in printServers)
{
var server = new PrintServer(printServer);
try
{
var queues = server.GetPrintQueues();
printers.AddRange(queues.Select(q => q.Name));
}
catch (Exception)
{
// Handle exception correctly
}
}
return printers.ToArray();
}
You might need to add the System.Management, System.Drawing, System.Printing references in you project..
I'm launching a process from a windows service in XP, I'm just launching the process not trying to interact with it. The process starts but the UI does not show. I believe I need to set some flags in STARTUPINFO to make process visible, and hoping someone could show how and what flags to set.
sPath = #"C:\Windows\notepad.exe";
string Message = string.Empty;
// Variables
PROCESS_INFORMATION processInfo = new PROCESS_INFORMATION();
STARTUPINFO startInfo = new STARTUPINFO();
Boolean bResult = false;
IntPtr hToken = IntPtr.Zero;
try
{
// Logon user
bResult = LogonUser(
"Test",
"VirtualXP-23639",
"test",
LogonType.LOGON32_LOGON_INTERACTIVE,
LogonProvider.LOGON32_PROVIDER_DEFAULT,
out hToken
);
if (!bResult) { throw new Exception("Logon error #" + Marshal.GetLastWin32Error()); }
// Create process
startInfo.cb = Marshal.SizeOf(startInfo);
startInfo.lpDesktop = "winsta0\\default";
bResult = CreateProcessAsUser(
hToken,
null,
sPath,
IntPtr.Zero,
IntPtr.Zero,
false,
0,
IntPtr.Zero,
null,
ref startInfo,
out processInfo
);
if (!bResult)
{
Message = "Failed to Create Process on Desktop/Console. Code=" + Marshal.GetLastWin32Error().ToString();
Logging.LogError(Ascension.CM.Common.Enums.ApplicationModuleEnums.Service, Message, "Ascension.CM.ServiceWorker.ProcessLauncher.XpLaunchDesktopProcess", null);
}
}
finally
{
// Close all handles
CloseHandle(hToken);
CloseHandle(processInfo.hProcess);
CloseHandle(processInfo.hThread);
}
}
You'll at least need to allow the service to interact with the desktop, so in services.msc, click on your serivce an go to properties, then logon and select allow to interact with desktop..
I would suggest that you use the Process class in the .net framework.
Process.Start("notepad.exe")
This should have your desired effect.
Thanks guys, but I've found a solution.
I ended up using WTSQueryUserToken to get the current logged in user and then used DuplicateTokenEx to get a token that I used with CreateProcessAsUser to start the process.
For XP use session id 0 and for win7 use WTSGetActiveConsoleSessionId to get the current session Id.
This works fine with out having to use the "Allow to interact with Desktop" property.
Thanks
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);