Виявити, чи працює адміністратором із підвищеними привілеями чи без них?


82

У мене є програма, яка повинна виявити, працює вона чи ні з підвищеними привілеями чи ні. На даний момент у мене такий код налаштований так:

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

Це працює для виявлення того, є користувач адміністратором чи ні, але не працює, якщо працює як адміністратор без підвищення. (Наприклад, у vshost.exe).

Як я можу визначити, чи можливий підйом [вже в силі чи] можливий ?

Відповіді:


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 приймає Адміністратора домену як дійсний під час підвищення прав (створювати папку у вікнах, запускати як адміністратор тощо) ... Як я можу змінити вашу функцію, щоб вона враховувала і цей випадок?
VSP

1
Можливо, ви також захочете врахувати, що якщо обліковий запис є вбудованим адміністратором, то UAC за замовчуванням підвищений, тому IsProcessElevated поверне false у цьому випадку (оскільки IsUacEnabled має значення true, а elevationResult - TokenElevationTypeDefault), хоча процес працює в підвищеному режимі, не маючи підказав користувач. Або іншими словами, обліковий запис підвищений, і процес виконується за типом підвищення за замовчуванням.
Містер Кук,

2
Цей код вимагає таких операторів: using System.Diagnostics; використання System.Runtime.InteropServices; використання System.Security.Principal; Здається, це також дзеркально відображається тут.
Скотт Солмер,

Це дало мені виняток у Windows 8, оскільки Marshal.SizeOf((int)elevationResult)я ще не впевнений, чому. Повідомлення про виняток: Метод не знайдено. На:Int32 System.Runtime.InteropServices.Marshal.SizeOf(!!0).
CularBytes

А як щодо 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)
Льюїс

Якщо для сповіщень встановлено значення "ніколи не показувати жодне сповіщення", це повернеться істиною. Можливо, у певних сценаріях, коли вам дійсно потрібно запустити програмне забезпечення як адміністратор, воно може дати помилкові вказівки.
CularBytes

2
Це не дозволить розрізнити "напіввисокий" процес від правильно піднятого процесу: можливо, IsElevatedповернеться false, але процес все ще може працювати з високим рівнем цілісності. Справді не підвищений процес має середній рівень цілісності. Це, мабуть, не має значення для 99% програм, але варто згадати, оскільки такі інструменти, як Process Hacker, все одно можуть оголосити такий процес підвищеним. "Напівневисокий" процес - це не те, що ви бачите зазвичай; це може статися, коли комусь не вдається правильно запустити непіднятий дочірній процес.
Роман Старков

Що таке "робота з високим рівнем цілісності"?
StingyJack

@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

@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))кидає ArgumentException32- бітну програму .NET 4.0 int elevationResultSize = Marshal.SizeOf((int)elevationResult), проте працював.
Мартін Браун

16

У проекті CodePlex UAChelper є код, який перевіряє висоту в UserAccountControl.cppUserAccountControl::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);
    }
}

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властивості, і я не знайшов способу отримати цю інформацію.


FYI: Тільки визначити, чи є адміністратор облікового запису, а не якщо додаток підвищений
CularBytes

Щоб перевірити, чи є користувач членом S-1-5-32-544 (група адміністраторів) у .Net <4.5, ви можете просто використати код у вихідному запитанні. Принципал буде членом групи адміністраторів, лише якщо процес запущений підвищено, а користувач знаходиться в групі. Якщо процес не підвищений, головний не буде в групі.
Адам

1
Приємна відповідь, коротка та ефективна, я дав вам +1 за це. Примітка. Я зробив це властивістю у своєму коді ( 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

Використання пакета nuche UACHelper: https://www.nuget.org/packages/UACHelper/

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

Існує безліч інших властивостей, за допомогою яких можна виявити, чи користувач насправді є адміністратором, чи процес виконується в рамках віртуалізації UAC, чи власник робочого столу є власником процесу. (Запустити як з обмеженого рахунку)

Перевірте прочитане для отримання додаткової інформації.


1

Я використовую цей код, і він працює добре:


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

* Адміністратор входить до групи вбудованих адміністраторів.

"Обліковий запис користувача для системного адміністратора. Цей обліковий запис є першим обліковим записом, створеним під час встановлення операційної системи. Обліковий запис не можна видалити або заблокувати. Він є членом групи адміністраторів і не може бути видалений з цієї групи." - https://ss64.com/nt/syntax-security_groups.html

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.