Iterating over running processes works fine just once - c#

I would like to retrieve information about all running processes that match a certain name pattern. I do that by using the following code I found online, which apparently is supposed to help with some privilige issues on Windows Vista and above. Sadly, that does not work for me. I am executing the following code as administrator.
The Natives.OpenProcess works fine the first time it is being called, but fails for every after call that by returning IntPtr.Zero and GetLastWin32Error() returns "Access Denied".
public static string GetExecutablePathAboveVista(int ProcessId)
{
var buffer = new StringBuilder(1024);
IntPtr hprocess = Natives.OpenProcess(ProcessAccessFlags.PROCESS_QUERY_LIMITED_INFORMATION, false, ProcessId);
if (hprocess != IntPtr.Zero)
{
try
{
int size = buffer.Capacity;
if (Natives.QueryFullProcessImageName(hprocess, 0, buffer, out size))
{
return buffer.ToString();
}
}
finally
{
Natives.CloseHandle(hprocess);
}
}
throw new Win32Exception(Marshal.GetLastWin32Error());
}

OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, ..) give you ERROR_ACCESS_DENIED when and only when you have no SE_DEBUG_PRIVILEGE enabled. so can be only 2 case: or before first call GetExecutablePathAboveVista you have enabled SE_DEBUG_PRIVILEGE in thread (if it have token) or process token. and before second/next calls you direct or faster of all indirect disable this privilege or impersonate thread with another token. or possible you say confuse processes - first time you open one process (id) and second time you try open another process (id) - not clear from your code.
I am executing the following code as administrator.
this is not enough. this mean only that in your process token exist SE_DEBUG_PRIVILEGE. (with default windows settings, however this can be changed) but you need that this privilege will be enabled in token, not just exist.
also
that match a certain name pattern
if you need only process name without full path - you already have it when you enumerate all running processes.
also exist undocumented SystemProcessIdInformation information class for ZwQuerySystemInformation - with it you can got full path of process without open it and have any privileges.

Related

How to sandbox code running another app (started using Process.Start)?

I've heard of sandboxing and how to make a simple example using AppDomain in .NET as in this article https://learn.microsoft.com/en-us/dotnet/framework/misc/how-to-run-partially-trusted-code-in-a-sandbox
However the unsafe (or untrusted) code I execute here is run another process using Process.Start (or if you know another way to help limit access of the started process, please suggest). My purpose is to constrain resource access of the started process (may not be a .NET app). So for example, the started process should not be able to access any file in the current environment.
The issue here is we need a security context (provided by the current AppDomain) having full-trust (unrestricted) for Process.Start to work.
I really hope that the current partially-trusted context (before calling Process.Start) would be cascaded down to the started process and can help constrain the resource access as expected. But if we need a full-trust context to run Process.Start, then it fails right at that step.
I've run out of ideas for how to make this possible because the only way I know to run a process in .NET is using Process.Start but it requires full-trust context … :(
Here is the code I've tried and there is always an error being thrown right before calling Process.Start:
class Program
{
static void Main(string[] args)
{
var ads = new AppDomainSetup();
ads.ApplicationBase = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var ps = new PermissionSet(System.Security.Permissions.PermissionState.None);
ps.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));
var ad = AppDomain.CreateDomain("SB", null, ads, ps);
var sb = ad.CreateInstanceAndUnwrap(typeof(Sandbox).Assembly.FullName, typeof(Sandbox).FullName) as Sandbox;
//the code throws exception and be highlighted at this line
sb.ExecuteUnsafeCode();
Console.WriteLine("End!");
Console.ReadLine();
}
}
//the sandbox class
public class Sandbox : MarshalByRefObject
{
//this simple method stub is just for testing
public void ExecuteUnsafeCode()
{
try
{
var si = new ProcessStartInfo("someSimpleApp.exe");
Process.Start(si);
Console.WriteLine("Run OK!");
} catch(Exception ex)
{
Console.WriteLine("SecurityException caught:\n{0}", ex.ToString());
}
}
}
The exception thrown is a SecurityException with a very short message of Request fail. The stack-trace is also too short (only 3 lines) and actually contains nothing helpful.
The bigger picture of my purpose here is to run submitted code (from user) in a sandbox so that no malicious code can harm the server. If the submitted code is some .NET lang, it would be easier because I may not have to use Process.Start here. But the submitted code is Java or unmanaged C++, really we have to compile it into some executable file and run it using Process.Start.
I hope to get some suggestions to try out, of course it's better if I have a right solution for this, thanks!

Get the window handle of the Windows setup process

I am trying to automate Windows 10 installation by first mounting the .iso file on the drive. And then using c# to start windows 10 installation by using this below code which passes the keys to the installation application
[DllImport("user32.dll")]
static extern int SetForegroundWindow(IntPtr point);
public static void Main(String[] args){
Process p1 = Process.Start("h:\\setup.exe");
IntPtr h = p1.MainWindowHandle;
SetForegroundWindow(h);
Thread.Sleep(30000);
SendKeys.SendWait("{ENTER}");
Thread.Sleep(1000);
SendKeys.SendWait("{ENTER}");
Thread.Sleep(1000);
SendKeys.SendWait("{ENTER}");
}
But the problem is that the setup window is not taking the signal of the ENTER key in the code. The setup window is starting with this code. After that, nothing is happening.
Process p1 = Process.Start("h:\\setup.exe");
I tried using notepad instead of setup.exe in the code which is taking all the ENTER keys. Please tell me if anyone has a solution. Thank you
Disclaimer:
I would advise against automating a Windows setup using something like
SendKeys as you can't guarantee a consistent behavior and could
easily mess things up. You may consider looking for different
approaches as suggested by lan Kemp in the comments. This answer
only shows you how to get the handle of the setup window correctly.
You may use it at your own risk.
Update:
Apparently, the Windows 10 setup executable ("Setup.exe") starts another process called "SetupPrep.exe" which starts a third process called "SetupHost.exe" (the one you're after). So, what you can do is start the main process, wait for the target process to start and obtain a MainWindowHandle before executing the remaining code:
Process p1 = Process.Start("H:\\setup.exe");
Process targetProcess;
do
{
Thread.Sleep(500);
targetProcess = Process.GetProcessesByName("SetupHost").FirstOrDefault();
} while (targetProcess == null || targetProcess.MainWindowHandle == IntPtr.Zero);
IntPtr h = targetProcess.MainWindowHandle;
// ...
This should solve your problem, however, it's not a wise idea to use SendKeys for this purpose. Please refer to the disclaimer above.
Original answer:
Did you make sure that h does actually have a value (other than IntPtr.Zero)? Because it probably doesn't have the actual window handle since you don't give the process enough time to start and obtain a window handle.
Try something like this:
Process p1 = Process.Start("h:\\setup.exe");
while (p1.MainWindowHandle == IntPtr.Zero)
{
Thread.Sleep(500);
}
IntPtr h = p1.MainWindowHandle;
// ...

CreateProcessAsUser not working with LogonUser token when printing from a Windows Service

I am building a windows services under LocalSystem account that will print pdf in some time interval.
To do this I am creating user token by using LogonUser
IntPtr currentToken = IntPtr.Zero;
const int LOGON32_LOGON_INTERACTIVE = 2;
const int LOGON32_PROVIDER_DEFAULT = 0;
bool loggedOn = LogonUser(user, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref currentToken);
and passing this currentToken in CreateProcessAsUser
CreateProcessAsUser(primaryToken, null, command, ref Security1, ref Security2, false, CREATE_NO_WINDOW | NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT, lpEnvironment, null, ref StartupInfo, out processInfo_);
But it's not doing it's job
If I get the current user token by using following code.
public static IntPtr GetCurrentUserToken()
{
IntPtr currentToken = IntPtr.Zero;
IntPtr primaryToken = IntPtr.Zero;
IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;
int dwSessionId = 0;
IntPtr hUserToken = IntPtr.Zero;
IntPtr hTokenDup = IntPtr.Zero;
IntPtr pSessionInfo = IntPtr.Zero;
int dwCount = 0;
WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, ref pSessionInfo, ref dwCount);
Int32 dataSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO));
Int32 current = (int)pSessionInfo;
for (int i = 0; i < dwCount; i++)
{
WTS_SESSION_INFO si = (WTS_SESSION_INFO)Marshal.PtrToStructure((System.IntPtr)current, typeof(WTS_SESSION_INFO));
if (WTS_CONNECTSTATE_CLASS.WTSActive == si.State)
{
dwSessionId = si.SessionID;
break;
}
current += dataSize;
}
WTSFreeMemory(pSessionInfo);
bool bRet = WTSQueryUserToken(dwSessionId, out currentToken);
if (bRet == false)
{
return IntPtr.Zero;
}
bRet = DuplicateTokenEx(currentToken, TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS, IntPtr.Zero, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, TOKEN_TYPE.TokenPrimary, out primaryToken);
if (bRet == false)
{
return IntPtr.Zero;
}
return primaryToken;
}
Then CreateProcessAsUser works fine. But I need to create token byLogonUserbecause after user logoffGetCurrentUserToken` method is not returning user token and I want user token also after logoff.
Update
I am checking last error after calling CreateProcessAsUser like this
uint exitCode;
if (!GetExitCodeProcess(processInfo_.hProcess, out exitCode))
{
int lastError = Marshal.GetLastWin32Error();
Logger.LogService(" GetExitCodeProcess Error " + lastError);
}
But GetExitCodeProcess returns true. I didn't found any error
Firstly
As with the last time you posted this question (Print Pdf from windows service and keep working after logoff), you need understand and workout what particular API call is failing and then call GetLastError which will give you more information about why the call is failing
GetLastError : Retrieves the calling thread's last-error code value
Secondly
It could be the case that one of the calls in GetCurrentUserToken e.g WTSQueryUserToken might just have a permission problem or something else that can be fixed (though i doubt it)
Reading the documentation for WTSQueryUserToken it seems to state the following
WTSQueryUserToken : Obtains the primary access token of the logged-on user specified by
the session ID.
Further more, WTSQueryUserToken is probably returning
ERROR_NO_TOKEN
1008
ERROR_NO_TOKEN : The token query is for a session in which no user is logged-on. This
occurs, for example, when the session is in the idle state or
SessionId is zero.
Thirdly
In my opinion, I don't think this approach is ever going to work in your situation and printing from a service is actually not recommended officially.
Please see the blow similar questions
Printing from a Windows Service
Printing from a .NET Service
Lastly
The only thing i can think of, is running you service under a User Account with access to the printers or impersonating a user by calling LogonUser, LoadUserProfile, and ImpersonateLoggedOnUser then printing via Gdi+ (which is going have its own problems)
Please note
While it is possible to send GDI+ output to a printer by obtaining a
device context handle for the printer and then passing that handle to
a GDI+ Graphics constructor, this is not recommended. The GDI+
functions and classes are not supported for use within a Windows
service. Attempting to use these functions and classes from a Windows
service may produce unexpected problems, such as diminished service
performance and run-time exceptions or errors:
In short, you solution as it stands and the source code you currently have is unlikely to work, and in my opinion you are most likely going to spend a lot of time on this problem trying to do something services are really not designed to do (which is also out of the scope of the current question).
I really think you need to seriously reconsider what you are trying to do and why and wish you the best of luck
Additional information on GDI Printing and printing from a service
How to install printer which should be accessible from windows service?
Printing from a Windows Service
Blatantly stolen from the linked pages.
Typically, the process that calls the CreateProcessAsUser function must have the SE_INCREASE_QUOTA_NAME.
The LocalSystem account has the following privileges:
...
SE_INCREASE_QUOTA_NAME (disabled)
So I am surprised that CreateProcessAsUser ever works in LocalSystem context.
CreateProcessAsUser does not load the specified user's profile into the HKEY_USERS registry key. Therefore, to access the information in the HKEY_CURRENT_USER registry key, you must load the user's profile information into HKEY_USERS with the LoadUserProfile function before calling CreateProcessAsUser. Be sure to call UnloadUserProfile after the new process exits.
CreateProcessAsUser seems a bit more picky than CreateProcessWithLogonW. I would try CreateProcessWithLogonW with the LOGON_WITH_PROFILE parameter.
You call DuplicateTokenEx on the token in the code that seems to work. You might want to try that with the LogonUser token.
You didn't give any information about your printing process, but from what you provided, I guess it must need to be run under interactive session. Your first way didn't work because the token provede by LogonUser has same session id as the calling process which is 0. And your second way didn't work after log out is because WTSQueryUserToken will fail as it suppose to. So I suggest you try winlogon solution which could be summarized as find out winlogon process of interactive session, duplicate its token and use the token to create you printing process. This acticle has all the details.

WaitForExit() throws exception c#

Below is my code to start the process, have put link for demo only.I want this process to run in background without opening browser. Also 2nd line throws exception
Object reference not set to an instance of an object.
var process=Process.Start("http://www.google.com");
process.WaitForExit();
Because when you start a process indirectly you won't get Process object (then in your case process is always null and second line throws an exception).
Let me explain what I mean with indirectly: if you don't specify an executable but you give a document (or a resource) then it'll be executed through a shell verb. In this case an existing process may be (re)used. In this cases Process.Start() will return null.
Try this:
Create an empty Word document 'c:\test.docx'.
Close all Word instances.
Execute Process.Start(#"c:\test.docx"); // Returns a Process instance
Execute Process.Start(#"c:\test.docx"); // Returns null
Can you simply solve this? AFAIK you can't because Process uses ShellExecuteEx with a SHELLEXECUTEINFO structure to start the process. Reading SHELLEXECUTEINFO documentation for hProcess field you'll see that:
A handle to the newly started application. This member is set on return and is always NULL unless fMask is set to SEE_MASK_NOCLOSEPROCESS. Even if fMask is set to SEE_MASK_NOCLOSEPROCESS, hProcess will be NULL if no process was launched. For example, if a document to be launched is a URL and an instance of Internet Explorer is already running, it will display the document. No new process is launched, and hProcess will be NULL.
Note ShellExecuteEx does not always return an hProcess, even if a process is launched as the result of the call. For example, an hProcess does not return when you use SEE_MASK_INVOKEIDLIST to invoke IContextMenu.
Note if you're running a new process just to open an URL and get a server side generated file then you should follow Damien's suggestion and use a WebClient.DownloadFile().
Process.Start() can return a null reference:
Return Value
Type: System.Diagnostics.Process
A new Process component that is associated with the process resource, or null, if no process resource is started (for example, if an existing process is reused).
(Emphasis mine)
When that happens, you'll get a NullReferenceException when trying to call WaitForExit()

How to determine if a previous instance of my application is running?

I have a console application in C# in which I run various arcane automation tasks. I am well aware that this should really be a Windows Service since it needs to run continuously, but I don't want to do that at this time. (So, don't suggest that as an answer).
In the meantime, I need some sample C# code that will allow me to determine if there's already an instance of the Application running.
In the old VB6.0 days, I would have used App.PrevInstance()
I want to be able to do this in my Main method:
static void Main()
{
if(!MyApp.IsAlreadyRunning())
{
while(true)
{
RockAndRollAllNightAndPartyEveryDay();
}
}
}
The proper way to use a mutex for this purpose:
private static Mutex mutex;
static void Main()
{
// STEP 1: Create and/or check mutex existence in a race-free way
bool created;
mutex = new Mutex(false, "YourAppName-{add-your-random-chars}", out created);
if (!created)
{
MessageBox.Show("Another instance of this application is already running");
return;
}
// STEP 2: Run whatever the app needs to do
Application.Run(new Form1());
// No need to release the mutex because it was never acquired
}
The above won't work for detecting if several users on the same machine are running the app under different user accounts. A similar case is where a process can run both under the service host and standalone. To make these work, create the mutex as follows:
var sid = new SecurityIdentifier(WellKnownSidType.WorldSid, null);
var mutexsecurity = new MutexSecurity();
mutexsecurity.AddAccessRule(new MutexAccessRule(sid, MutexRights.FullControl, AccessControlType.Allow));
mutexsecurity.AddAccessRule(new MutexAccessRule(sid, MutexRights.ChangePermissions, AccessControlType.Deny));
mutexsecurity.AddAccessRule(new MutexAccessRule(sid, MutexRights.Delete, AccessControlType.Deny));
_mutex = new Mutex(false, "Global\\YourAppName-{add-your-random-chars}", out created, mutexsecurity);
Two differences here - firstly, the mutex needs to be created with security rights that allow other user accounts to open/acquire it. Second, the name must be prefixed with "Global" in the case of services running under the service host (not sure about other users running locally on the same machine).
Jeroen already answered this, but the best way by far is to use a Mutex... not by Process. Here's a fuller answer with code.
I've updated this answer after seeing some comments about a race condition to address that by instead using the Mutex Constructor
Boolean createdNew;
Mutex mutex;
try
{
mutex = new Mutex(false, "SINGLEINSTANCE" out createdNew);
if (createdNew == false)
{
Console.WriteLine("Error : Only 1 instance of this application can run at a time");
Application.Exit();
}
// Run your application
}
catch (Exception e)
{
// Unable to open the mutex for various reasons
}
finally
{
// If this instance created the mutex, ensure that
// it's cleaned up, otherwise we can't restart the
// application
if (mutex && createdNew)
{
mutex.ReleaseMutex();
mutex.Dispose();
}
}
Notice the try{} finally{} block. If you're application crashes or exits cleanly but you don't release the Mutex then you may not be able to restart it again later.
The most simple (and reliable) way to do this, is using a Mutex. Use the WaitOne method of the Mutex class to wait until the mutex becomes available. An added advantage, this will not require any infinite loops
You can search process names of existing system process. For example code, see this blog post.
You can also used a named system Mutex to see if your process is already running.
Here is some sample code. This tends to be more reliable in my experience, and is much simpler, more understandable code.
This article talks about it: Prevent a second process instance from running. It's in VB.net but you can convert it.
The problem in writing a generic function that checks whether the current application is already running comes from the fact that the ProcessName property of the Process object seems to be limited to 15 characters, so longer process names are truncated.
A safer way to retrieve a process name is to get the filename of its main module and dropping the extension. The following reusable routine uses this approach:
Function AppIsAlreadyRunning() As Boolean
' get the filename of the main module
Dim moduleName As String = Process.GetCurrentProcess.MainModule.ModuleName
' discard the extension to get the process name
Dim procName As String = System.IO.Path.GetFileNameWithoutExtension(moduleName)
' return true if there are 2 or more processes with that name
If Process.GetProcessesByName(procName).Length > 1 Then
Return True
End If
End Function
// Allow running single instance
string processName = Process.GetCurrentProcess().ProcessName;
Process[] instances = Process.GetProcessesByName(processName);
if (instances.Length > 1)
{
MessageBox.Show("Application already Running", "Error 1001 - Application Running");
return;
}
Gracefully exit application with messagebox as shown above if application is already running
You can use Process.GetProcessesByName("MyProcessName"); in the System.Diagnostics namespace to check if there is an instance of your process running.
EDIT: Very good observations in the comments! This is a (very) simplistic way of doing it, and certainly doesn't cover all the bases.
Using a kernal object is the only correct way to implement single instance protection in Windows.
This statement:
mutex = Mutex.OpenExisting("SINGLEINSTANCE");
won't work if someone else copies this line from Stackoverflow and runs their program before your program, since that other guy grabbed "SINGLEINSTANCE" before you did. You want to include a GUID in your mutex name:
mutex = Mutex.OpenExisting("MyApp{AD52DAF0-C3CF-4cc7-9EDD-03812F82557E}");
This technique will prevent the current user from running more than one instance of your program, but will not prevent another user from doing so.
To ensure that only one instance of your application can run on the local computer, you need to do this:
mutex = Mutex.OpenExisting("Global\MyApp{AD52DAF0-C3CF-4cc7-9EDD-03812F82557E}");
See the help for the CreateMutex api.
In one of my projects I used SingleInstance Component
Another way to do it is to bind to an address on the local machine (as a TCP listener would). Only one process at a time can bind to a port/address combination. So pick a port on the loopback adapter and have at it.
This has the nice side-effects of:
Working even if someone renames the executable
Resetting itself when the application crashes
The technique is portable across other operating systems
On the down-side, it can fail if there's another application that binds to that particular port.
As requested, some code to bind to a address/port is below. This is ripped out of something else. It is incomplete, but the necessary bits are here.
using System.Net;
using System.Net.Sockets;
[...]
// Make up a number that's currently unused by you, or any
// well-known service. i.e. 80 for http, 22 for ssh, etc..
int portNum = 2001;
// This binds to any (meaning all) adapters on this system
IPAddress ipAddress = IPAddress.Any;
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, portNum);
Socket listener = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp );
// The next statement will throw an exception if anyone has done this Bind!
listener.Bind(localEndPoint);
As long as listener is not garbage collected (falls out of scope) or the program doesn't terminate: that port on that adapter is yours and only yours. If anything should happen to listener then it becomes available for someone else to use. For purposes of a lock, you should probably have listener be static somewhere.

Categories