My app runs as requestedExecutionLevel
set to highestAvailable
.
How do I run a process unelevated?
I tried the following but it didn't work:
Process.Start(new ProcessStartInfo {FileName = "foo.exe", Verb = "open"})
I have tried the following trust levels to start my process using Win32 API but none of them work correctly:
0
1260: This program is blocked by group policy. For more information, contact your system administrator.
0x1000
The application was unable to start correctly (0xc0000142). Click OK to close the application.
0x10000
Process starts then hangs
0x20000
All options are not available
0x40000
Runs as admin
If I run tskill foo
from my elevated app, it restarts foo with correct privileges.
What I need is a solution in which I don't have to specify the trust level. The process should start with the correct trust level automatically just like the tskill
tool restarts foo.exe
in the correct trust level. The user selects and runs foo.exe
and so it can be anything.
If I can get the trust level of a process somehow, I can do this easily since foo.exe
runs when my app can capture its trust level.
The Win32 Security Management functions provide the capability to create a restricted token with normal user rights; with the token, you can call
CreateProcessAsUser
to run the process with that token. Below is a proof of concept that runs cmd.exe as a normal user, regardless of whether the process is run in an elevated context.This approach makes use the following Win32 functions:
SaferIdentifyLevel
to indicate the identity level (limited, normal, or elevated). Setting thelevelId
toSAFER_LEVELID_NORMALUSER
(0x20000) provides the normal user level.SaferComputeTokenFromLevel
creates a token for the provided level. PassingNULL
to the InAccessToken parameter uses the identity of the current thread.CreateProcessAsUser
creates the process with the provided token. Since the session is already interactive, most of the parameters can be kept at default values. (The third parameter,lpCommandLine
can be provided as a string to specify the command line.)CloseHandle
(Kernel32) andSaferCloseLevel
to free allocated memory.Finally, the P/Invoke code is below (copied mostly from pinvoke.net):
I had best results by cloning Explorer's token as follows:
Alternative approach
Originally I went with drf's excellent answer, but expanded it somewhat. If the above (clone Explorer's token) is not to your liking, keep reading but see a gotcha at the very end.
When using drf's method as described, the process is started without administrative access, but it still has a high integrity level. A typical un-elevated process has a medium integrity level.
Try this: use Process Hacker to see the properties of the process started this way; you will see that PH considers the process to be elevated even though it doesn't have administrative access. Add an Integrity column and you'll see it's "High".
The fix is reasonably simple: after using
SaferComputeTokenFromLevel
, we need to change the token integrity level to Medium. The code to do this might look something like this (converted from MSDN sample):Alas, this still doesn't really solve the problem completely. The process won't have administrative access; it won't have a high integrity, but it will still have a token that's marked as "elevated".
Whether this is a problem for you or not I don't know, but it may have been why I ended up cloning Explorer's token in the end, as described at the start of this answer.
Here is my full source code (modified drf's answer), in all its P/Invoke glory:
And here are the P/Invoke definitions you'll need in addition to those listed in drf's answer: