Check if another process has admin privileges
You cannot add handles together (new IntPtr(hProcess.ToInt64() + hToken.ToInt64());
), that makes no sense.
You need the process handle to get the process token handle, then pass the token handle to CheckTokenMembership
.
You also need to close these handles with CloseHandle
.
using System;
using System.Runtime.InteropServices;
...
public class WinApi
{
public const int TOKEN_DUPLICATE = 0x0002;
public const int TOKEN_QUERY = 0x00000008;
public const int SecurityImpersonation = 2;
public const int TokenImpersonation = 2;
[DllImport("advapi32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)]
public static extern bool OpenProcessToken(IntPtr ProcessHandle, UInt32 DesiredAccess, out IntPtr TokenHandle);
[DllImport("advapi32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DuplicateTokenEx(IntPtr hTok, UInt32 DesiredAccess, IntPtr SecAttPtr, int ImpLvl, int TokType, out IntPtr TokenHandle);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr OpenProcess(ProcessAccessFlags processAccess, bool bInheritHandle, int processId);
[DllImport("advapi32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CreateWellKnownSid(WELL_KNOWN_SID_TYPE WellKnownSidType, IntPtr DomainSid, IntPtr pSid, ref uint cbSid);
[DllImport("advapi32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CheckTokenMembership(IntPtr TokenHandle, IntPtr SidToCheck, out bool IsMember);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern int GetCurrentProcessId();
[DllImport("kernel32.dll", SetLastError = true)]
public static extern int CloseHandle(IntPtr h);
}
public enum ProcessAccessFlags : uint
{
QueryInformation = 0x00000400,
QueryLimitedInformation = 0x1000
}
public enum WELL_KNOWN_SID_TYPE
{
WinBuiltinAdministratorsSid = 26
}
private static bool IsAdminGroupMember(int processId)
{
IntPtr hPriToken = IntPtr.Zero, hImpToken = IntPtr.Zero;
var hProcess = WinApi.OpenProcess(ProcessAccessFlags.QueryLimitedInformation, false, processId);
if (hProcess == IntPtr.Zero) hProcess = WinApi.OpenProcess(ProcessAccessFlags.QueryInformation, false, processId); // < Vista
var haveToken = WinApi.OpenProcessToken(hProcess, WinApi.TOKEN_DUPLICATE, out hPriToken);
if (haveToken)
{
haveToken = WinApi.DuplicateTokenEx(hPriToken, WinApi.TOKEN_QUERY, IntPtr.Zero, WinApi.SecurityImpersonation, WinApi.TokenImpersonation, out hImpToken);
WinApi.CloseHandle(hPriToken);
}
if (hProcess != IntPtr.Zero) WinApi.CloseHandle(hProcess);
if (haveToken)
{
uint cbSid = 0;
bool isMember = false;
WinApi.CreateWellKnownSid(WELL_KNOWN_SID_TYPE.WinBuiltinAdministratorsSid, IntPtr.Zero, IntPtr.Zero, ref cbSid);
IntPtr pSid = Marshal.AllocCoTaskMem(Convert.ToInt32(cbSid));
var succeed = pSid != IntPtr.Zero && WinApi.CreateWellKnownSid(WELL_KNOWN_SID_TYPE.WinBuiltinAdministratorsSid, IntPtr.Zero, pSid, ref cbSid);
succeed = succeed && WinApi.CheckTokenMembership(hImpToken, pSid, out isMember);
Marshal.FreeCoTaskMem(pSid);
WinApi.CloseHandle(hImpToken);
return succeed && isMember;
}
return false;
}
[STAThread]static void Main(/*string[] args*/)
{
bool admin = IsAdminGroupMember(WinApi.GetCurrentProcessId());
Console.WriteLine(string.Format("IsAdminGroupMember={0}", admin));
}
Check if a different process is running with elevated privileges
Thanks to RbMm (and Hantalyte indirectly) I've been made aware that the Microsoft documentation for OpenProcessToken is incorrect in its assertion that the provided handle must have the PROCESS_QUERY_INFORMATION
access permission, as it actually only requires that the handle have PROCESS_QUERY_LIMITED_INFORMATION
(I have confirmed this with my own testing).
It is possible to get a handle to an elevated process from within a non-elevated process with the permission PROCESS_QUERY_LIMITED_INFORMATION
, as long as both were started from the same account, meaning that one can check other processes for elevation using the procedure in my question that I original though didn't work due to the errant documentation.
As Hantalyte/RbMm point out, if the process being checked is owned by a different account the checking process needs the SE_DEBUG_NAME
privilege to be enabled.
Hopefully the MS docs should be corrected soon.
UPDATE:
The correction PR has been merged.
UPDATE 2:
The correction is now live: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocesstoken
How can I tell if my process is running as Administrator?
Technically, if you want to see if the member is the local administrator account, then you can get the security identifier (SID) of the current user through the User
property on the WindowsIdentity
class, like so (the static GetCurrent
method gets the current Windows user):
WindowsIdentity windowsIdentity = WindowsIdentity.GetCurrent();
string sid = windowsIdentity.User.ToString();
The User
property returns the SID of the user which has a number of predefined values for various groups and users.
Then you would check to see if the SID has the following pattern, indicating it is the local administrator account (which is a well-known SID):
S-1-5-{other SID parts}-500
Or, if you don't want to parse strings, you can use the SecurityIdentifier
class:
// Get the built-in administrator account.
var sid = new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid,
null);
// Compare to the current user.
bool isBuiltInAdmin = (windowsIdentity.User == sid);
However, I suspect that what you really want to know is if the current user is a member of the administrators group for the local machine. You can get this SID using the WellKnownSidType
of BuiltinAdministratorsSid
:
// Get the SID of the admin group on the local machine.
var localAdminGroupSid = new SecurityIdentifier(
WellKnownSidType.BuiltinAdministratorsSid, null);
Then you can check the Groups
property on the WindowsIdentity
of the user to see if that user is a member of the local admin group, like so:
bool isLocalAdmin = windowsIdentity.Groups.
Select(g => (SecurityIdentifier) g.Translate(typeof(SecurityIdentifier))).
Any(s => s == localAdminGroupSid);
Check if another process has admin privileges in .NET
OpenProcess(PROCESS_QUERY_[LIMITED_]INFORMATION)
+OpenProcessToken(TOKEN_DUPLICATE)
to get the token, then DuplicateTokenEx(TOKEN_QUERY,SecurityImpersonation,TokenImpersonation)
to get the impersonation token, then pass that token and the SID from CreateWellKnownSid(WinBuiltinAdministratorsSid)
to CheckTokenMembership
.
To be able to open (almost) every process for PROCESS_QUERY_INFORMATION
access you need to be running as administrator and with debug privileges. On Vista and later you can use PROCESS_QUERY_LIMITED_INFORMATION
.
Example code available in this answer.
Related Topics
How to Pack a Visual Studio C++ Project for Release
What's the Usual Way of Controlling Frame Rate
How to Use Qt Without Qmake or Qt Creator
When and Why Would You Use Static with Constexpr
Differences Between Running an Executable with Visual Studio Debugger VS Without Debugger
Forward Declaration VS Include
Dynamically Register Constructor Methods in an Abstractfactory at Compile Time Using C++ Templates
Why Does Enable_If_T in Template Arguments Complains About Redefinitions
Using Condition Variable in a Producer-Consumer Situation
Same Function with Const and Without - When and Why
Openmp Nested Parallel for Loops VS Inner Parallel For
How Does =Delete on Destructor Prevent Stack Allocation
Which Is Faster/Preferred: Memset or for Loop to Zero Out an Array of Doubles
Checking If Two Cubic BéZier Curves Intersect
How to Initialize All Elements in an Array to the Same Number in C++