Определить, работает ли он от имени администратора с повышенными привилегиями или без них?

82

У меня есть приложение, которое должно определять, работает ли оно с повышенными привилегиями или нет. В настоящее время у меня есть такой код:

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

Это работает, чтобы определить, является ли пользователь администратором или нет, но не работает, если он работает от имени администратора без повышения прав. (Например в vshost.exe).

Как я могу определить, [уже действует или] возможно ли повышение уровня ?

MiffTheFox
источник

Ответы:

55

Попробуйте это:

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;
            }
        }
    }
}
Стивен
источник
8
Работает, если учетная запись для запуска от имени локального администратора, но если вы используете администратора домена, переменная isProcessAdmin возвращает false. Но UAC принимает администратора домена как действующего при повышении привилегий (создание папки в Windows, запуск от имени администратора и т. Д.) ... Как я могу изменить вашу функцию, чтобы она учитывала и этот случай?
VSP
1
Вы также можете принять во внимание, что если учетная запись является встроенным администратором, тогда UAC повышается по умолчанию, поэтому IsProcessElevated вернет false в этом случае (потому что IsUacEnabled - true, а elevationResult - TokenElevationTypeDefault), даже если процесс выполняется в повышенном режиме без подсказал пользователю. Или, другими словами, учетная запись повышена, и процесс выполняется с типом повышения по умолчанию.
Mister Cook
2
Для этого кода требуются следующие операторы using: using System.Diagnostics; using System.Runtime.InteropServices; с использованием System.Security.Principal; Кажется, это тоже здесь
Скотт Солмер
Это сделало меня исключением в Windows 8, Marshal.SizeOf((int)elevationResult)но я пока не знаю почему. Сообщение об исключении: Метод не найден. В:Int32 System.Runtime.InteropServices.Marshal.SizeOf(!!0).
CularBytes 07
А как насчет TokenElevationTypeLimited? Не следует ли рассматривать установку isProcessAdmin в значение true?
Оливье МАТРО
34

(новый ответ через шесть лет после того, как вопрос был задан)

Отказ от ответственности: это просто то, что случилось с моей конкретной ОС с моими конкретными настройками с моим конкретным пользователем:

using System.Security.Principal;

// ...

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

Поэтому, когда я запускаю этот «Запуск от имени администратора», getвозвращается свойство доступа true. При нормальной работе (даже если мой пользователь «является» администратором, но не запускает это конкретное приложение «от имени администратора»), он возвращается false.

Это кажется намного проще, чем многие другие ответы.

Я понятия не имею, есть ли случаи, когда это не удается.

PS! Это тоже кажется нормальным:

    static bool IsElevated
    {
      get
      {
        var id = WindowsIdentity.GetCurrent();
        return id.Owner != id.User;
      }
    }
Йеппе Стиг Нильсен
источник
1
Спасибо за это! - Я использовал это в PowerShell [Security.Principal.WindowsIdentity] :: GetCurrent (). Owner.IsWellKnown ([System.Security.Principal.WellKnownSidType] :: BuiltinAdministratorsSid)
Льюис
Если для уведомлений установлено значение «никогда не показывать никаких уведомлений», будет возвращено значение true. Возможно, в определенных сценариях, когда вам действительно нужно запускать программное обеспечение от имени администратора, это может дать ложную информацию.
CularBytes 08
2
При этом не будет проводиться различие между процессом с «частично не повышенным» и процессом, не повышенным должным образом: возможно, IsElevatedон вернет false, но процесс все еще может работать с высоким уровнем целостности. Подлинно неподготовленный процесс имеет средний уровень целостности. Вероятно, это не имеет отношения к 99% приложений, но стоит упомянуть, потому что такие инструменты, как Process Hacker, могут по-прежнему объявлять такой процесс повышенным. «Полувысокий» процесс - это не то, что вы обычно видели бы; это может произойти, когда кто-то не может правильно запустить дочерний процесс, не имеющий прав.
Роман Старков
Что «работает с высоким уровнем целостности»?
StingyJack 01
@StingyJack - это слишком сложный вопрос, чтобы отвечать в комментариях, но посмотрите здесь и здесь .
Роман Старков
19

Вот модифицированная версия этого ответа, которая включает такие вещи, как правильное использование ресурсов и управление администраторами домена.

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;
            }
        }
    }
}
Скотта Чемберлена
источник
Все зависит от того, от имени какого пользователя вы запускаете службу. Вы пытаетесь определить, работает ли служба как локальная система, локальная служба, сетевая служба или как пользователь Windows? Обнаружение «административного статуса» не поможет определить разницу между локальной системой и локальной службой, вам необходимо проверить это, напрямую проверив, какой пользователь запускает процесс.
Скотт Чемберлен
Это сделало меня исключением в Windows 8, Marshal.SizeOf((int)elevationResult)но я пока не знаю почему. Сообщение об исключении: Метод не найден. В:Int32 System.Runtime.InteropServices.Marshal.SizeOf(!!0).
CularBytes 07
@RageCompex вы используете ограниченную платформу, такую ​​как универсальное приложение или Unity3d?
Скотт Чемберлен
1
Ах, вы компилируете с 4.5.1, потому что он пытается использовать эту перегрузку, но у пользователя не установлена ​​4.5.1. Попробуйте заменить его на Marshal.SizeOf(typeof(TOKEN_ELEVATION_TYPE)),
Скотт Чемберлен
2
@ScottChamberlain int elevationResultSize = Marshal.SizeOf(typeof(TOKEN_ELEVATION_TYPE))бросает ArgumentExceptionна 32 бит приложения .NET 4.0, int elevationResultSize = Marshal.SizeOf((int)elevationResult)работал, однако.
Мартин Браун
16

В проекте CodePlex UAChelper есть код, который проверяет повышение прав в UserAccountControl.cpp UserAccountControl::IsUserAdmin, который проверяет, включен ли UAC, а затем проверяет, повышен ли процесс.

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

из функции:

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
источник
10

В .net Framwork 4.5 я нашел другой метод, который мне подходит. В отношении следующего сценария, который можно найти здесь (на немецком языке)

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

В C # это выглядит так:

    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;
        }
    }

Но в .net <4.5 WindowsPrincipalкласс не содержит UserClaimsсвойства, и я не нашел способа получить эту информацию.

Рафаэль
источник
К вашему сведению: определение только того, является ли учетная запись администратором, а не повышением уровня приложения
CularBytes,
Чтобы проверить, является ли пользователь членом S-1-5-32-544 (группа администраторов) в .Net <4.5, вы можете просто использовать код в исходном вопросе. Принципал будет членом группы администраторов только в том случае, если процесс выполняется с повышенными правами и пользователь входит в группу. Если процесс не повышен, участник не будет в группе.
Adam
1
Хороший ответ, короткий и эффективный, я поставил вам за это +1. NB Я сделал это свойством в моем коде ( private bool IsAdmin{ get { ... } }), тогда вам не нужны скобки, если вы вызываете IsAdmin.
Мэтт
4

Использование TokenElevationTypeбудет работать, но если вы введете PInvoke CheckTokenMembership()против SID группы администратора, ваш код также будет работать, когда UAC выключен и на 2000 / XP / 2003, а также будет обрабатывать запрещенные SID.

Существует также IsUserAnAdmin()функция, которая выполняет CheckTokenMembershipпроверку за вас, но MSDN говорит, что она может быть не навсегда

Андерс
источник
Я обнаружил, что CheckTokenMembership недостаточен для UAC - github.com/chocolatey/choco/blob/… возвращает false. Проверьте код (я его заменяю) и взгляните на вывод Win2012R2 - i.imgur.com/gX3JP0W.png
ferventcoder
@ferventcoder Это зависит от того, что вы действительно хотите знать; является ли пользователь в данный момент администратором с повышенными правами или он может повысить его, если потребуется. Вы можете, например, проверить TOKEN_ELEVATION_TYPE и получить что-то вроде: bool is_or_can_elevate () {return process_is_elevated () || TokenElevationTypeLimited == get_current_token_elevation_type (); }. Другая проблема заключается в том, что определение повышенного уровня не везде одинаково, вы можете иметь окно консоли с префиксом «Администратор:» и в то же время быть ниже высокого уровня целостности! TokenElevation не всегда соответствует TokenIntegrityLevel.
Андерс
Веселые времена. Я хочу знать, есть ли у меня повышенный процесс отдельно от того, является ли пользователь администратором. Вот где я оказался. Дайте мне знать, куда мне идти, если это неправильно - github.com/chocolatey/choco/issues/77#issuecomment-73523774 и github.com/chocolatey/choco/commit/…
ferventcoder
@ferventcoder is_processes_elevated () {return CheckTokenMembership / IsInRole || TokenElevation / TokenIntegrityLevel> = 0x3000; } CheckTokenMembership или IsInRole для Vista и Vista + с отключенным UAC. TokenElevation или TokenIntegrityLevel> = 0x3000 в зависимости от того, как именно вы хотите определять высоту. Я считаю, что conhost.exe использует TokenElevation, но он сломан IMHO, и вы должны проверить фактический уровень ... (вам нужны специальные инструменты для создания токена, который обманывает TokenElevation) См. Также: windowssucks.wordpress.com/2011/02/07 / uac-are-you-high / #
Андерс
... и даже это в некотором роде неверно, теоретически возможно иметь повышенный токен и не входить в группу администраторов. Поэтому, если вам нужны только люди в группе администраторов и убедитесь, что они имеют повышенные права, вы должны выполнить проверку CheckTokenMembership / IsInRole, а затем проверка Token * должна завершиться неудачно (без UAC), или ее значение должно указывать на повышение ... Это, конечно, зависит о том, к чему вы действительно хотите получить доступ. Вам может потребоваться быть системным / администратором и иметь повышенный уровень или просто повышенный, это зависит от ACL.
Андерс
4

В этом ответе есть несколько проблем. Во-первых, он не получает никаких системных процессов, которые запускаются от имени администратора (например, NT-Authority / SYSTEM). Приведенный ниже пример кода устраняет все проблемы (обнаруживает, LocalAdmins, DomainAdmins и LocalSystemAdmins)

Если вам нужен только текущий процесс, замените его pHandleнаProcess.GetCurrentProcess().Handle

ПРИМЕЧАНИЕ. У вас должны быть определенные права для его запуска. (Они есть у каждого AdminProcess, но сначала их нужно активировать, у Сервисов они активированы по умолчанию)

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;
}
Джон Смит
источник
1

Думаю, есть еще одна проблема. Я проверил предоставленные вами решения и должен сказать, что при установке Windows 7 и входе в систему как администратор проверка не работает. Windows никогда не возвращает информацию о том, что процесс работает в повышенном режиме. Итак, последовательность:

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

не возвращает истину, если вы вошли в систему как администратор, но процесс имеет все привилегии для выполнения системных операций (например, остановки системных служб). Последовательность работы такова:

if (IsUserAdmin())
    return true;

if (IsUacEnabled)
    return IsProcessInElevatedMode();

return false;

Сначала вы должны проверить, запущен ли процесс в контексте администратора. Дополнительная информация:

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

Все эти методы были описаны в предыдущих постах.

Гжегож Домбровски
источник
1
Это не ответ, а, возможно, комментарий к другому сообщению
MickyD
1

Использование пакета nuget UACHelper : https://www.nuget.org/packages/UACHelper/

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

Существует множество других свойств, которые можно использовать для определения того, является ли пользователь на самом деле администратором, или процесс выполняется под виртуализацией UAC, или владелец рабочего стола является владельцем процесса. (Запуск от имени ограниченной учетной записи)

Прочтите меня для получения дополнительной информации.

Соруш Фалахати
источник
1

Я использую этот код, и он хорошо работает:


bool runningAsAdmin = WindowsIdentity.GetCurrent().Owner.IsWellKnown(WellKnownSidType.BuiltinAdministratorsSid);

* Admin является частью группы встроенных администраторов.

«Учетная запись пользователя для системного администратора. Эта учетная запись - первая учетная запись, созданная во время установки операционной системы. Учетная запись не может быть удалена или заблокирована. Она является членом группы администраторов и не может быть удалена из этой группы». - https://ss64.com/nt/syntax-security_groups.html

Blee
источник