1

I have wrote a code to check is a process running on the same machine has administrator privileges or not. But it always returns false.

Can you tell me what's wrong with it.

private static bool HasAdminPrivileges(int processId)
{
    var hProcess = WinApi.OpenProcess(ProcessAccessFlags.QueryInformation, false, processId);
    var opened = WinApi.OpenProcessToken(hProcess, WinApi.TOKEN_QUERY, out IntPtr hToken);
    if (opened)
    {
        var token = new IntPtr(hProcess.ToInt64() + hToken.ToInt64()); // 64 bit machine only

        WinApi.CreateWellKnownSid(WELL_KNOWN_SID_TYPE.WinBuiltinAdministratorsSid, IntPtr.Zero, IntPtr.Zero, out uint cbSidUint);
        var cbSid = new IntPtr(cbSidUint);

        var succeed = WinApi.CheckTokenMembership(token, cbSid, out bool isMember);

        return succeed && isMember;
    }
    return false;
}

public class WinApi
{
    public const int TOKEN_QUERY = 0X00000008;

    [DllImport("advapi32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool OpenProcessToken(IntPtr ProcessHandle, UInt32 DesiredAccess, out IntPtr TokenHandle);

    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern IntPtr OpenProcess(ProcessAccessFlags processAccess, bool bInheritHandle, int processId);    

    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern bool CreateWellKnownSid(WELL_KNOWN_SID_TYPE WellKnownSidType, IntPtr DomainSid, IntPtr pSid, out uint cbSid);    

    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern bool CheckTokenMembership(IntPtr TokenHandle, IntPtr SidToCheck, out bool IsMember);
}

public enum ProcessAccessFlags : uint
{
    QueryInformation = 0x00000400
}

public enum WELL_KNOWN_SID_TYPE
{
    WinBuiltinAdministratorsSid = 26
}
Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
  • 2
    errors: **1** you must open process with access `PROCESS_QUERY_LIMITED_INFORMATION` but not `PROCESS_QUERY_INFORMATION` (you get access denied here if you not run as admin) **2** `CheckTokenMembership` work only with impersonation token.but you try pass primary token to it. **3** you not create admin sid - in single call to `CreateWellKnownSid` you not pass pointer to memory where this sid can be stored. **4** you not close opened handles. **5** you not check any errors – RbMm Nov 24 '18 at 15:03
  • **6** you not debug self code at all – RbMm Nov 24 '18 at 15:11

2 Answers2

4

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));
}
Anders
  • 97,548
  • 12
  • 110
  • 164
  • must be not `ProcessAccessFlags.QueryInformation` but ask only for `PROCESS_QUERY_LIMITED_INFORMATION` (not admin process can not open admin with `ProcessAccessFlags.QueryInformation` (i guess that this is equal to `PROCESS_QUERY_INFORMATION`. also we can call `DuplicateToken` without `Ex` here and if want be correct check that first call to `CreateWellKnownSid` fail with `ERROR_INSUFFICIENT_BUFFER` – RbMm Nov 24 '18 at 17:35
  • @RbMM: It tries limited information first, then it tries the old one for XP. Technically, the documentation does not say that CreateWellKnownSid fails with a specific error. Either way, if cbSid is somehow incorrect then the memory allocation will fail or the next call to CreateWellKnownSid will fail, either way, the code is safe. – Anders Nov 24 '18 at 17:46
  • *It tries limited information first, then it tries the old one for XP* - sorry - not note this in your code. `CreateWellKnownSid` - yes, which error returned in case we pass 0 pointer and 0 size not documented, but from another side not check error of api call and just try allocate `cbSid` also wrong. may be sid type unknown ? more correct anyway check for this error, even if not explicit documented – RbMm Nov 24 '18 at 17:54
  • about access - i be use `dwAccess = ver < 6.0 ? QueryInformation : QueryLimitedInformation` and call OpenProcess only once – RbMm Nov 24 '18 at 18:02
  • 1
    Microsoft says you are not suppose to do version checks. Anyway, only on < Vista will it call OpenProcess twice, not a big deal. Just allocating cbSid is fine, if it is a very large size then memory allocation might fail but that is fine if the SID was not well known. In this case we hardcode the well known sid so we know it will work. – Anders Nov 24 '18 at 18:43
  • this is question of style of course. check here for pre vista (where no QueryLimitedInformation) absolute safe too. of course your code with twice call OpenProcess and not check first result of `CreateWellKnownSid` also correct. simply we have different style ) – RbMm Nov 24 '18 at 18:49
1

I have created Process.Extensions.dll extension using solution offered by Anders

using System;
using ProcessExtensions;
using System.Diagnostics;
static void Main(string[] args)
{
    bool isAdminGroupMember = Process.GetCurrentProcess().IsAdminGroupMember();
    Console.WriteLine(string.Format("IsAdminGroupMember={0}", isAdminGroupMember));
}

https://github.com/IamhereTeam/Process.Extensions.git

Process.Extensions.dll