¿Detectar si se está ejecutando como administrador con o sin privilegios elevados?

82

Tengo una aplicación que necesita detectar si se está ejecutando con privilegios elevados o no. Actualmente tengo un código configurado así:

static bool IsAdministrator()
{
    WindowsIdentity identity = WindowsIdentity.GetCurrent();
    WindowsPrincipal principal = new WindowsPrincipal(identity);
    return principal.IsInRole (WindowsBuiltInRole.Administrator);
}

Esto funciona para detectar si un usuario es administrador o no, pero no funciona si se ejecuta como administrador sin elevación. (Por ejemplo, en vshost.exe).

¿Cómo puedo determinar si la elevación [ya está en vigor o] es posible ?

MiffTheFox
fuente

Respuestas:

55

Probar esto:

using Microsoft.Win32;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;

public static class UacHelper
{
    private const string uacRegistryKey = "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System";
    private const string uacRegistryValue = "EnableLUA";

    private static uint STANDARD_RIGHTS_READ = 0x00020000;
    private static uint TOKEN_QUERY = 0x0008;
    private static uint TOKEN_READ = (STANDARD_RIGHTS_READ | TOKEN_QUERY);

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

    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern bool GetTokenInformation(IntPtr TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass, IntPtr TokenInformation, uint TokenInformationLength, out uint ReturnLength);

    public enum TOKEN_INFORMATION_CLASS
    {
        TokenUser = 1,
        TokenGroups,
        TokenPrivileges,
        TokenOwner,
        TokenPrimaryGroup,
        TokenDefaultDacl,
        TokenSource,
        TokenType,
        TokenImpersonationLevel,
        TokenStatistics,
        TokenRestrictedSids,
        TokenSessionId,
        TokenGroupsAndPrivileges,
        TokenSessionReference,
        TokenSandBoxInert,
        TokenAuditPolicy,
        TokenOrigin,
        TokenElevationType,
        TokenLinkedToken,
        TokenElevation,
        TokenHasRestrictions,
        TokenAccessInformation,
        TokenVirtualizationAllowed,
        TokenVirtualizationEnabled,
        TokenIntegrityLevel,
        TokenUIAccess,
        TokenMandatoryPolicy,
        TokenLogonSid,
        MaxTokenInfoClass
    }

    public enum TOKEN_ELEVATION_TYPE
    {
        TokenElevationTypeDefault = 1,
        TokenElevationTypeFull,
        TokenElevationTypeLimited
    }

    public static bool IsUacEnabled
    {
        get
        {
            RegistryKey uacKey = Registry.LocalMachine.OpenSubKey(uacRegistryKey, false);
            bool result = uacKey.GetValue(uacRegistryValue).Equals(1);
            return result;
        }
    }

    public static bool IsProcessElevated
    {
        get
        {
            if (IsUacEnabled)
            {
                IntPtr tokenHandle;
                if (!OpenProcessToken(Process.GetCurrentProcess().Handle, TOKEN_READ, out tokenHandle))
                {
                    throw new ApplicationException("Could not get process token.  Win32 Error Code: " + Marshal.GetLastWin32Error());
                }

                TOKEN_ELEVATION_TYPE elevationResult = TOKEN_ELEVATION_TYPE.TokenElevationTypeDefault;

                int elevationResultSize = Marshal.SizeOf((int)elevationResult);
                uint returnedSize = 0;
                IntPtr elevationTypePtr = Marshal.AllocHGlobal(elevationResultSize);

                bool success = GetTokenInformation(tokenHandle, TOKEN_INFORMATION_CLASS.TokenElevationType, elevationTypePtr, (uint)elevationResultSize, out returnedSize);
                if (success)
                {
                    elevationResult = (TOKEN_ELEVATION_TYPE)Marshal.ReadInt32(elevationTypePtr);
                    bool isProcessAdmin = elevationResult == TOKEN_ELEVATION_TYPE.TokenElevationTypeFull;
                    return isProcessAdmin;
                }
                else
                {
                    throw new ApplicationException("Unable to determine the current elevation.");
                }
            }
            else
            {
                WindowsIdentity identity = WindowsIdentity.GetCurrent();
                WindowsPrincipal principal = new WindowsPrincipal(identity);
                bool result = principal.IsInRole(WindowsBuiltInRole.Administrator);
                return result;
            }
        }
    }
}
Steven
fuente
8
Funciona si la cuenta para ejecutar es un administrador local, pero si usa el administrador de dominio, la variable isProcessAdmin devuelve falso. Pero UAC acepta al Administrador de Dominio como válido al elevar privilegios (crear carpeta en windows, ejecutar como administrador, etc) ... ¿Cómo puedo modificar tu función para que también tenga en cuenta ese caso?
VSP
1
También es posible que desee considerar que si la cuenta es el administrador integrado, entonces UAC está elevado de forma predeterminada, por lo que IsProcessElevated devolverá falso en este caso (porque IsUacEnabled es verdadero y ElevationResult es TokenElevationTypeDefault) aunque el proceso se ejecuta en modo elevado sin tener preguntó el usuario. O en otras palabras, la cuenta se eleva y el proceso se ejecuta en el tipo de elevación predeterminado.
Mister Cook
2
Este código requiere las siguientes instrucciones using: using System.Diagnostics; utilizando System.Runtime.InteropServices; utilizando System.Security.Principal; También parece reflejarse aquí.
Scott Solmer
Esto me consiguió una excepción en Windows 8, y Marshal.SizeOf((int)elevationResult)todavía no estoy seguro de por qué. El mensaje de excepción es: Método no encontrado. En:Int32 System.Runtime.InteropServices.Marshal.SizeOf(!!0).
CularBytes
¿Qué pasa con TokenElevationTypeLimited? ¿No debería considerarse establecer isProcessAdmin en verdadero?
Olivier MATROT
33

(nueva respuesta seis años después de que se hizo la pregunta)

Descargo de responsabilidad: esto es solo algo que sucedió que funcionó en mi sistema operativo particular con mi configuración particular con mi usuario particular:

using System.Security.Principal;

// ...

    static bool IsElevated
    {
      get
      {
        return WindowsIdentity.GetCurrent().Owner
          .IsWellKnown(WellKnownSidType.BuiltinAdministratorsSid);
      }
    }

Entonces, cuando ejecuto este "Ejecutar como administrador", el descriptor de getacceso a la propiedad vuelve true. Cuando se ejecuta normalmente (incluso si mi usuario "es" administrador, pero no ejecuta esta aplicación en particular "como administrador"), vuelve false.

Esto parece mucho más simple que muchas otras respuestas.

No tengo idea de si hay casos en los que esto falla.

¡PD! Esto también parece estar bien:

    static bool IsElevated
    {
      get
      {
        var id = WindowsIdentity.GetCurrent();
        return id.Owner != id.User;
      }
    }
Jeppe Stig Nielsen
fuente
1
¡Gracias por esto! - Usé esto en PowerShell [Security.Principal.WindowsIdentity] :: GetCurrent (). Owner.IsWellKnown ([System.Security.Principal.WellKnownSidType] :: BuiltinAdministratorsSid)
Lewis
Cuando haya configurado las notificaciones para que 'nunca muestre ninguna notificación', esto se volverá verdadero. Quizás en ciertos escenarios en los que realmente necesita ejecutar el software como administrador, puede dar una indicación falsa.
CularBytes
2
Esto no distinguirá entre el proceso "semi-no elevado" y un proceso correctamente no elevado: es posible que IsElevateddevuelva falso, pero el proceso puede seguir ejecutándose con un nivel de integridad alto. Un proceso genuinamente no elevado tiene un nivel de integridad medio. Esto probablemente sea irrelevante para el 99% de las aplicaciones, pero vale la pena mencionarlo porque herramientas como Process Hacker aún pueden declarar que dicho proceso es elevado. Un proceso "semi-no elevado" no es algo que vería normalmente; puede ocurrir cuando alguien no inicia correctamente un proceso secundario no elevado.
Roman Starkov
¿Qué es "ejecutar con alto nivel de integridad"?
StingyJack
@StingyJack es una pregunta demasiado grande para responder en los comentarios, pero mira aquí y aquí .
Roman Starkov
19

Aquí hay una versión modificada de esta respuesta para incluir cosas como la eliminación adecuada de recursos y el manejo de los administradores de dominio.

public static class UacHelper
{
    private const string uacRegistryKey = "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System";
    private const string uacRegistryValue = "EnableLUA";

    private static uint STANDARD_RIGHTS_READ = 0x00020000;
    private static uint TOKEN_QUERY = 0x0008;
    private static uint TOKEN_READ = (STANDARD_RIGHTS_READ | TOKEN_QUERY);

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

    [DllImport("kernel32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool CloseHandle(IntPtr hObject);

    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern bool GetTokenInformation(IntPtr TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass, IntPtr TokenInformation, uint TokenInformationLength, out uint ReturnLength);

    public enum TOKEN_INFORMATION_CLASS
    {
        TokenUser = 1,
        TokenGroups,
        TokenPrivileges,
        TokenOwner,
        TokenPrimaryGroup,
        TokenDefaultDacl,
        TokenSource,
        TokenType,
        TokenImpersonationLevel,
        TokenStatistics,
        TokenRestrictedSids,
        TokenSessionId,
        TokenGroupsAndPrivileges,
        TokenSessionReference,
        TokenSandBoxInert,
        TokenAuditPolicy,
        TokenOrigin,
        TokenElevationType,
        TokenLinkedToken,
        TokenElevation,
        TokenHasRestrictions,
        TokenAccessInformation,
        TokenVirtualizationAllowed,
        TokenVirtualizationEnabled,
        TokenIntegrityLevel,
        TokenUIAccess,
        TokenMandatoryPolicy,
        TokenLogonSid,
        MaxTokenInfoClass
    }

    public enum TOKEN_ELEVATION_TYPE
    {
        TokenElevationTypeDefault = 1,
        TokenElevationTypeFull,
        TokenElevationTypeLimited
    }

    public static bool IsUacEnabled
    {
        get
        {
            using (RegistryKey uacKey = Registry.LocalMachine.OpenSubKey(uacRegistryKey, false))
            {
                bool result = uacKey.GetValue(uacRegistryValue).Equals(1);
                return result;
            }
        }
    }

    public static bool IsProcessElevated
    {
        get
        {
            if (IsUacEnabled)
            {
                IntPtr tokenHandle = IntPtr.Zero;
                if (!OpenProcessToken(Process.GetCurrentProcess().Handle, TOKEN_READ, out tokenHandle))
                {
                    throw new ApplicationException("Could not get process token.  Win32 Error Code: " +
                                                   Marshal.GetLastWin32Error());
                }

                try
                {
                    TOKEN_ELEVATION_TYPE elevationResult = TOKEN_ELEVATION_TYPE.TokenElevationTypeDefault;

                    int elevationResultSize = Marshal.SizeOf(typeof(TOKEN_ELEVATION_TYPE));
                    uint returnedSize = 0;

                    IntPtr elevationTypePtr = Marshal.AllocHGlobal(elevationResultSize);
                    try
                    {
                        bool success = GetTokenInformation(tokenHandle, TOKEN_INFORMATION_CLASS.TokenElevationType,
                                                           elevationTypePtr, (uint) elevationResultSize,
                                                           out returnedSize);
                        if (success)
                        {
                            elevationResult = (TOKEN_ELEVATION_TYPE) Marshal.ReadInt32(elevationTypePtr);
                            bool isProcessAdmin = elevationResult == TOKEN_ELEVATION_TYPE.TokenElevationTypeFull;
                            return isProcessAdmin;
                        }
                        else
                        {
                            throw new ApplicationException("Unable to determine the current elevation.");
                        }
                    }
                    finally
                    {
                        if (elevationTypePtr != IntPtr.Zero)
                            Marshal.FreeHGlobal(elevationTypePtr);
                    }
                }
                finally
                {
                    if (tokenHandle != IntPtr.Zero)
                        CloseHandle(tokenHandle);
                }
            }
            else
            {
                WindowsIdentity identity = WindowsIdentity.GetCurrent();
                WindowsPrincipal principal = new WindowsPrincipal(identity);
                bool result = principal.IsInRole(WindowsBuiltInRole.Administrator) 
                           || principal.IsInRole(0x200); //Domain Administrator
                return result;
            }
        }
    }
}
Scott Chamberlain
fuente
Todo depende del usuario con el que esté ejecutando el servicio. ¿Está intentando detectar si el servicio se está ejecutando como sistema local, servicio local, servicio de red o un usuario de Windows? La detección del "estado administrativo" no funcionará para diferenciar entre el sistema local y el servicio local; debe probarlo comprobando directamente qué usuario está ejecutando el proceso.
Scott Chamberlain
Esto me consiguió una excepción en Windows 8, y Marshal.SizeOf((int)elevationResult)todavía no estoy seguro de por qué. El mensaje de excepción es: Método no encontrado. En:Int32 System.Runtime.InteropServices.Marshal.SizeOf(!!0).
CularBytes
@RageCompex, ¿está utilizando una plataforma restringida como una aplicación universal o Unity3d?
Scott Chamberlain
1
Ah, estás compilando con 4.5.1 debido a que está intentando usar esta sobrecarga pero el usuario no tiene 4.5.1 instalado. Intente reemplazarlo con Marshal.SizeOf(typeof(TOKEN_ELEVATION_TYPE)),
Scott Chamberlain
2
@ScottChamberlain int elevationResultSize = Marshal.SizeOf(typeof(TOKEN_ELEVATION_TYPE))lanza una ArgumentExceptionaplicación de 32 bits .NET 4.0 int elevationResultSize = Marshal.SizeOf((int)elevationResult), sin embargo funcionó.
Martin Braun
16

El proyecto CodePlex UAChelper tiene un código que verifica la elevación en UserAccountControl.cpp UserAccountControl::IsUserAdmin, que verifica si UAC está habilitado y luego verifica si el proceso está elevado.

bool UserAccountControl::IsCurrentProcessElevated::get()
{
    return GetProcessTokenElevationType() == TokenElevationTypeFull;    //elevated
}

de la función:

int UserAccountControl::GetProcessTokenElevationType()
{
    HANDLE hToken;
    try
    {
        if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
            throw gcnew Win32Exception(GetLastError());

        TOKEN_ELEVATION_TYPE elevationType;
        DWORD dwSize;
        if (!GetTokenInformation(hToken, TokenElevationType, &elevationType, sizeof(elevationType), &dwSize))
            throw gcnew Win32Exception(GetLastError());

        return elevationType;
    }
    finally
    {
        CloseHandle(hToken);
    }
}
Preet Sangha
fuente
10

En .net Framwork 4.5 encontré otro método que me funciona. En relación con el siguiente script que se puede encontrar aquí aquí (en alemán)

 rem --- Admintest.bat ---
 whoami /groups | find "S-1-5-32-544" > nul
 if errorlevel 1 goto ende
 echo Benutzer %username% ist lokaler Administrator.
 :ende

En C # se ve así:

    private bool IsAdmin
    {
        get
        {
            WindowsIdentity identity = WindowsIdentity.GetCurrent();
            if (identity != null)
            {
               WindowsPrincipal principal = new WindowsPrincipal(identity);
               List<Claim> list = new List<Claim>(principal.UserClaims);
               Claim c = list.Find(p => p.Value.Contains("S-1-5-32-544"));
               if (c != null)
                  return true;
            }
            return false;
        }
    }

Pero en .net <4.5 la WindowsPrincipalclase no contiene la UserClaimspropiedad y no encontré forma de obtener esta información.

Rafael
fuente
FYI: solo se determina si la cuenta es de administrador, no si la aplicación está elevada
CularBytes
Para verificar si un usuario es miembro de S-1-5-32-544 (grupo de administradores) en .Net <4.5, puede usar el código de la pregunta original. El principal solo será miembro del grupo de administradores si el proceso se está ejecutando en un nivel elevado y el usuario está en el grupo. Si el proceso no es elevado, el principal no estará en el grupo.
Adam
1
Buena respuesta, corta y eficiente, te di un +1 por ello. NB Hice esto una propiedad en mi código ( private bool IsAdmin{ get { ... } }), entonces no necesita los corchetes si invoca IsAdmin.
Matt
4

El uso TokenElevationTypefuncionaría, pero si PInvoke CheckTokenMembership()contra el SID del grupo de administración, su código también funcionaría cuando UAC está apagado y encendido 2000 / XP / 2003 y también manejará los SID denegados.

También hay una IsUserAnAdmin()función que realiza la CheckTokenMembershipverificación por usted, pero MSDN dice que podría no estar allí para siempre.

Anders
fuente
Encontré CheckTokenMembership insuficiente cuando está sujeto a UAC - github.com/chocolatey/choco/blob/… devuelve falso. Verifique el código (lo estoy reemplazando) y eche un vistazo a la salida de Win2012R2 - i.imgur.com/gX3JP0W.png
ferventcoder
@ferventcoder Depende de lo que realmente quieras saber; ¿Es el usuario un administrador elevado en este momento o puede elevarlo si es necesario? Por ejemplo, puede verificar TOKEN_ELEVATION_TYPE y terminar con algo como: bool is_or_can_elevate () {return process_is_elevated () || TokenElevationTypeLimited == get_current_token_elevation_type (); }. Otro problema es que la definición de elevado no es la misma en todas partes, puede tener una ventana de consola con el prefijo "Administrador:" y al mismo tiempo estar por debajo del nivel de integridad alto. TokenElevation no siempre coincide con TokenIntegrityLevel.
Anders
Tiempos divertidos. Quiero saber si tengo un proceso elevado aparte de si el usuario es un administrador. Aquí es donde terminé. Avísame a dónde debo ir si está mal: github.com/chocolatey/choco/issues/77#issuecomment-73523774 y github.com/chocolatey/choco/commit/…
ferventcoder
@ferventcoder is_processes_elevated () {return CheckTokenMembership / IsInRole || TokenElevation / TokenIntegrityLevel> = 0x3000; } CheckTokenMembership o IsInRole para <Vista y Vista + con UAC desactivado. TokenElevation o TokenIntegrityLevel> = 0x3000 dependiendo exactamente de cómo desee detectar la elevación. Creo que conhost.exe usa TokenElevation pero está roto en mi humilde opinión y deberías verificar el nivel real ... (aunque necesitas herramientas especiales para generar un token que engañe a TokenElevation) Ver también: windowssucks.wordpress.com/2011/02/07 / uac-are-you-high / #
Anders
... e incluso eso está algo mal, en teoría es posible tener un token elevado y no estar en el grupo de administradores. Entonces, si solo desea personas en el grupo de administradores y se asegura de que estén elevados, debe realizar la verificación CheckTokenMembership / IsInRole y luego la verificación Token * debe fallar (No UAC) o su valor debe indicar elevación ... Esto, por supuesto, depende sobre lo que realmente desea acceder. Es posible que deba ser administrador del sistema y estar elevado, o simplemente elevado, depende de la ACL.
Anders
4

Esta respuesta tiene algunos problemas. Primero, no obtiene ningún proceso del sistema que se ejecute como administrador (por ejemplo, bajo NT-Authority / SYSTEM). El siguiente ejemplo de código corrige todos los problemas (detecta, LocalAdmins, DomainAdmins y LocalSystemAdmins)

Si solo desea el proceso actual, reemplácelo pHandleconProcess.GetCurrentProcess().Handle

NOTA: Debe tener ciertos privilegios para ejecutarlo. (Cada AdminProcess los tiene pero necesita activarlos primero, los Servicios los tienen activados por defecto)

internal static bool IsProcessElevatedEx(this IntPtr pHandle) {

        var token = IntPtr.Zero;
        if (!OpenProcessToken(pHandle, MAXIMUM_ALLOWED, ref token))
                throw new Win32Exception(Marshal.GetLastWin32Error(), "OpenProcessToken failed");

        WindowsIdentity identity = new WindowsIdentity(token);
        WindowsPrincipal principal = new WindowsPrincipal(identity);
        bool result = principal.IsInRole(WindowsBuiltInRole.Administrator)
                   || principal.IsInRole(0x200); //Domain Administrator
        CloseHandle(token);
        return result;
}
John Smith
fuente
1

Creo que hay un problema más. Verifiqué las soluciones proporcionadas por usted y debo decir que en la instalación de Windows 7 e inicie sesión como administrador, la verificación no funciona. Windows nunca devuelve información de que el proceso se ejecuta en modo elevado. Entonces la secuencia:

if (IsUacEnabled)
    return IsProcessInElevatedMode();
return IsUserAdmin();

no devuelve verdadero cuando se registra como administrador, pero el proceso tiene todos los privilegios para realizar operaciones del sistema (por ejemplo, detener los servicios del sistema). La secuencia de trabajo es:

if (IsUserAdmin())
    return true;

if (IsUacEnabled)
    return IsProcessInElevatedMode();

return false;

Primero debe verificar si el proceso se ejecuta en el contexto del Administrador. Información adicional:

IsUacEnabled() - checks if the UAC has been enabled in the system (Windows)
IsProcessInElevatedMode() - checks if the process is run in an elevated mode
IsUserAdmin() - checks if the current user has an Administrtor role

Todos esos métodos se han descrito en publicaciones anteriores.

Grzegorz Dąbrowski
fuente
1
Esta no es una respuesta, sino un comentario de otra publicación
MickyD
1

Usando el paquete nuget de UACHelper: https://www.nuget.org/packages/UACHelper/

if (UACHelper.IsElevated)
    // something
else
    // something else

Hay muchas otras propiedades que pueden usarse para detectar si el usuario es, de hecho, un administrador, o si el proceso se está ejecutando bajo virtualización UAC, o si el propietario del escritorio es el propietario del proceso. (Ejecutar desde una cuenta limitada)

Consulte el léame para obtener más información.

Soroush Falahati
fuente