检测是否以具有或没有提升特权的管理员身份运行?


82

我有一个应用程序,需要检测它是否正在以提升的特权运行。我目前有这样的代码设置:

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

这可以检测用户是否是管理员,但是如果以没有提升权限的管理员身份运行则无法使用。(例如,在vshost.exe中)。

如何确定高程是否已经生效

Answers:


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,而levationResult为TokenElevationTypeDefault),即使该进程以提升模式运行而无需提示用户。或者换句话说,帐户已提升,并且流程以默认的提升类型运行。
库克先生

2
此代码需要以下using语句: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吗?
奥利维尔·马特

33

(提出问题六年后的新答案)

免责声明:这只是在特定用户的特定设置下,在我的特定OS上发生的事情:

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)中使用了它
Lewis

将通知设置为“从不显示任何通知”时,它将返回true。也许在某些情况下,您确实需要以管理员身份运行该软件,这可能会给出错误的指示。
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用户身份运行?检测“管理状态”将无法分辨本地系统和本地服务之间的区别,您需要通过直接检查哪个用户正在运行该进程来进行测试。
Scott Chamberlain

在Windows 8上,这使我成为一个例外,Marshal.SizeOf((int)elevationResult)目前还不确定为什么。异常消息是:找不到方法。在:Int32 System.Runtime.InteropServices.Marshal.SizeOf(!!0).
CularBytes

@RageCompex您使用的是通用应用程序还是Unity3d等受限平台?
Scott Chamberlain

1
嗯,您正在使用4.5.1进行编译,因为它正在尝试使用此重载,但用户未安装4.5.1。试着用替换它Marshal.SizeOf(typeof(TOKEN_ELEVATION_TYPE))
斯科特张伯伦

2
@ScottChamberlain在32位应用程序.NET 4.0上int elevationResultSize = Marshal.SizeOf(typeof(TOKEN_ELEVATION_TYPE))抛出了一个ArgumentException问题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);
    }
}

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

要检查用户是否是.Net <4.5中S-1-5-32-544(管理员组)的成员,您可以只使用原始问题中的代码。仅当进程运行提升且用户在组中时,主体才是Administrators组的成员。如果没有提高流程,则委托人将不在组中。
亚当

1
好的答案,简短有效,我给了你+1。注意:我在代码(private bool IsAdmin{ get { ... } })中将此属性设置为属性,因此如果调用,则不需要括号IsAdmin
马特

4

使用TokenElevationType可以使用,但是如果您CheckTokenMembership()针对管理组SID调用,则在UAC处于关闭状态且在2000 / XP / 2003上时,您的代码也可以使用,并且可以处理拒绝SID。

还有一个IsUserAnAdmin()功能可以CheckTokenMembership为您进行检查,但是MSDN说它可能永远不会存在


当受到UAC约束时,我发现CheckTokenMembership不足-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(); }。另一个问题是,提升的定义在每个地方都不相同,您可以在控制台窗口中添加前缀“ Administrator:”,同时使其低于“高完整性”级别!TokenElevation并不总是与TokenIntegrityLevel匹配。
Anders

娱乐时间。我想知道我是否有一个提升的流程,而不是该用户是否是管理员。这是我结束的地方。让我知道我应该去,如果它是错的- github.com/chocolatey/choco/issues/77#issuecomment-73523774github.com/chocolatey/choco/commit/...
ferventcoder

@ferventcoder is_processes_elevated(){返回CheckTokenMembership / IsInRole || TokenElevation / TokenIntegrityLevel> = 0x3000; } <Vista和Vista +的CheckTokenMembershipIsInRole,且UAC已关闭。TokenElevationTokenIntegrityLevel> = 0x3000,具体取决于您要如何检测高程。我相信conhost.exe使用TokenElevation,但是它已损坏,恕我直言,您应该检查实际的级别...(您需要特殊的工具来生成使TokenElevation愚蠢的令牌。)另请参阅:windowssucks.wordpress.com/2011/02/07 / uac-you-you-high /#
安德斯(Anders)2015年

...甚至那是错误的,从理论上讲,也有可能拥有一个提升的令牌,并且不在管理员组中。因此,如果您只希望管理员组中的人员并确保他们得到提升,则应执行CheckTokenMembership / IsInRole检查,然后Token *检查应该失败(否UAC),或者其值应指示海拔高度...这当然取决于您实际想要访问的内容。您可能需要由系统/管理员来提升权限,或者只是提升权限,这取决于ACL。
Anders

4

这个答案有一些问题。首先,它没有任何以Admin身份运行的系统进程(例如,在NT-Authority / SYSTEM下)。下面的代码示例解决了所有问题(检测,LocalAdmins,DomainAdmins和LocalSystemAdmins)

如果只需要当前流程,请替换pHandleProcess.GetCurrentProcess().Handle

注意:您必须具有某些特权才能运行它。(每个AdminProcess都有它们,但需要先激活它们,而Services会默认将它们激活)

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();

以管理员身份登录时不会返回true,但是该进程具有执行系统操作的所有特权(例如,停止系统服务)。工作顺序为:

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

使用UACHelper nuget包:https ://www.nuget.org/packages/UACHelper/

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

还有许多其他属性可用于检测用户实际上是否是管理员,或者进程是否在UAC虚拟化下运行,或者桌面所有者是否是进程所有者。(从受限帐户运行)

检查自述以获取更多信息。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.