在.NET/C#中测试进程是否具有管理员权限

51

有没有一种规范的方法可以测试进程是否在机器上拥有管理员权限?

我将启动一个长时间运行的进程,该进程的生命周期后期将尝试一些需要管理员权限的操作。

我想事先测试进程是否具有这些权限,而不是后来再测试。

10个回答

79

这将检查用户是否在本地管理员组中(假设您不是检查域管理员权限)

using System.Security.Principal;

public bool IsUserAdministrator()
{
    //bool value to hold our return value
    bool isAdmin;
    WindowsIdentity user = null;
    try
    {
        //get the currently logged in user
        user = WindowsIdentity.GetCurrent();
        WindowsPrincipal principal = new WindowsPrincipal(user);
        isAdmin = principal.IsInRole(WindowsBuiltInRole.Administrator);
    }
    catch (UnauthorizedAccessException ex)
    {
        isAdmin = false;
    }
    catch (Exception ex)
    {
        isAdmin = false;
    }
    finally
    {
        if (user != null)
            user.Dispose();
    }
    return isAdmin;
}

3
这将确定用户是否在BUILTIN\Administrators组中,但它能显示用户在Vista上是否已提升权限吗? - John Saunders
5
@Jared:它还隐藏了该块可能出现的任何严重错误。应该将其删除。 - John Saunders
3
如果启用了用户账户控制(UAC),这个方法在Vista系统中将行不通。因为UAC会为拥有管理员权限的用户创建一个“分裂令牌”,而该“分裂令牌”明确排除所有管理员角色(包括像“域管理员”这样的角色)。 - Jacob Proffitt
11
我在启用UAC的Windows Server 2008上测试了这个功能。它的工作原理是:提升的管理员 -> true,非提升的管理员 -> false,标准用户 -> false。 - Joe Daley
3
如果您是非高级管理员,则 principal.IsInRole(WindowsBuiltInRole.Administrator) 将返回 false,它仅在您是提升的管理员时返回 true。 - Scott Chamberlain
显示剩余7条评论

30

从Wadih M的代码开始,我有一些额外的P/Invoke代码来尝试处理UAC打开的情况。

http://www.davidmoore.info/blog/2011/06/20/how-to-check-if-the-current-user-is-an-administrator-even-if-uac-is-on/

首先,我们需要一些支持GetTokenInformation API调用的代码:

[DllImport("advapi32.dll", SetLastError = true)]
static extern bool GetTokenInformation(IntPtr tokenHandle, TokenInformationClass tokenInformationClass, IntPtr tokenInformation, int tokenInformationLength, out int returnLength);

/// <summary>
/// Passed to <see cref="GetTokenInformation"/> to specify what
/// information about the token to return.
/// </summary>
enum TokenInformationClass
{
     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
}

/// <summary>
/// The elevation type for a user token.
/// </summary>
enum TokenElevationType
{
    TokenElevationTypeDefault = 1,
    TokenElevationTypeFull,
    TokenElevationTypeLimited
}

然后,需要实际的代码来检测用户是否为管理员(如果是则返回true,否则返回false)。

var identity = WindowsIdentity.GetCurrent();
if (identity == null) throw new InvalidOperationException("Couldn't get the current user identity");
var principal = new WindowsPrincipal(identity);

// Check if this user has the Administrator role. If they do, return immediately.
// If UAC is on, and the process is not elevated, then this will actually return false.
if (principal.IsInRole(WindowsBuiltInRole.Administrator)) return true;

// If we're not running in Vista onwards, we don't have to worry about checking for UAC.
if (Environment.OSVersion.Platform != PlatformID.Win32NT || Environment.OSVersion.Version.Major < 6)
{
     // Operating system does not support UAC; skipping elevation check.
     return false;
}

int tokenInfLength = Marshal.SizeOf(typeof(int));
IntPtr tokenInformation = Marshal.AllocHGlobal(tokenInfLength);

try
{
    var token = identity.Token;
    var result = GetTokenInformation(token, TokenInformationClass.TokenElevationType, tokenInformation, tokenInfLength, out tokenInfLength);

    if (!result)
    {
        var exception = Marshal.GetExceptionForHR( Marshal.GetHRForLastWin32Error() );
        throw new InvalidOperationException("Couldn't get token information", exception);
    }

    var elevationType = (TokenElevationType)Marshal.ReadInt32(tokenInformation);

    switch (elevationType)
    {
        case TokenElevationType.TokenElevationTypeDefault:
            // TokenElevationTypeDefault - User is not using a split token, so they cannot elevate.
            return false;
        case TokenElevationType.TokenElevationTypeFull:
            // TokenElevationTypeFull - User has a split token, and the process is running elevated. Assuming they're an administrator.
            return true;
        case TokenElevationType.TokenElevationTypeLimited:
            // TokenElevationTypeLimited - User has a split token, but the process is not running elevated. Assuming they're an administrator.
            return true;
        default:
            // Unknown token elevation type.
            return false;
     }
}
finally
{    
    if (tokenInformation != IntPtr.Zero) Marshal.FreeHGlobal(tokenInformation);
}

1
这是最完整的答案,解决了因UAC阻止而导致调用IsInRole(WindowsBuiltInRole.Administrator)返回false的问题。已确认在Windows 8上正常工作。 - David Chen
这是一个很好的答案,但不是针对这个问题的! - Harry Johnston

17

如果你想确保你的解决方案在Vista UAC中有效,并且拥有.Net Framework 3.5或更高版本,你可能想要使用System.DirectoryServices.AccountManagement命名空间。你的代码将类似于:

bool isAllowed = false;
using (PrincipalContext pc = new PrincipalContext(ContextType.Machine, null))
{
    UserPrincipal up = UserPrincipal.Current;
    GroupPrincipal gp = GroupPrincipal.FindByIdentity(pc, "Administrators");
    if (up.IsMemberOf(gp))
        isAllowed = true;
}

应该 的。我还没有针对内置管理员帐户进行测试。我的应用程序是针对域进行测试的。http://theruntime.com/blogs/jacob/archive/2009/02/13/so-you-think-youre-an-admin.aspx - Jacob Proffitt
7
请勿硬编码管理员(我相信这是可本地化的字符串,而不是已知的BUILTIN\Administrators字符串)。 - Ruben Bartelink
那么,你会使用什么,Ruben? - Jacob Proffitt
4
根据 MSDN 页面上的说明,您可以使用 SID,我相信它是 "S-1-5-32-544"。http://msdn.microsoft.com/en-us/library/bb359434.aspx - Matt
Jacob提到的代码在Windows XP上能否运行?只要安装了所需的.NET Framework 3.5+,就可以运行。 - Rob K.
仅供参考:此代码未正确指示我的进程正在以提升的权限运行。尽管如此,我有一个稍微奇怪的设置,我的帐户不在管理员组中,但可以提升以包括进程所需的管理员令牌。Wadih的代码为我的情况提供了正确的答案。 - Goyuix

4

尝试了Erwin的代码,但它无法编译。

我按照以下方式使其工作:

[DllImport("shell32.dll")] public static extern bool IsUserAnAdmin();

这对我来说像个冠军一样有效 - 有人知道IsUserAnAdmin函数内部的代码实际上是什么样子吗? - Goyuix
6
MSDN页面上的IsUserAnAdmin链接(http://msdn.microsoft.com/en-us/library/windows/desktop/bb776463(v=vs.85).aspx)指出:“客户端支持结束:Windows Vista”。这个函数可能在未来的Windows版本中不可用,所以它肯定不是“规范”的方法。 - jeyk
看起来文章中已经删除了“客户端支持结束:Windows Vista”部分,尽管它仍然暗示可能会[弃用]。 - daserge

3

使用.NET Framework 4.5,似乎更容易检查用户是否属于管理员组:

WindowsPrincipal principal = WindowsPrincipal.Current;
bool canBeAdmin = principal.Claims.Any((c) => c.Value == "S-1-5-32-544");

注意 - WindowsPrincipal.Current.Claims 和 (new WindowsPrincipal(WindowsIdentity.GetCurrent())).Claims 返回的声明列表之间存在很大的差异,请参见我的下面的答案。 - OzBob

3
以下内容已在Windows 10和Ubuntu Linux上测试通过,适用于.NET Core 3:
[DllImport("libc")]
public static extern uint getuid(); // Only used on Linux but causes no issues on Windows

static bool RunningAsAdmin()
{
    if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
    {
        using var identity = WindowsIdentity.GetCurrent();
        WindowsPrincipal principal = new WindowsPrincipal(identity);
        return principal.IsInRole(WindowsBuiltInRole.Administrator);
    }
    else return getuid() == 0;
}

当UAC生效时(Windows)或应用程序在Linux上以超级用户身份运行(例如sudo myapp)时,它返回true

如果有人有机会在MacOS上测试,请分享您的发现。


2
其他利用IsInRole方法的答案只有在用户使用提升令牌运行时才返回true,正如其他评论所述。这里是一个潜在替代方案,在标准和提升上下文中检查本地管理员组成员资格:
bool isAdmin = false;
using (var user = WindowsIdentity.GetCurrent())
{
    var principal = new WindowsPrincipal(user);
    // Check for token claim with well-known Administrators group SID
    const string LOCAL_ADMININSTRATORS_GROUP_SID = "S-1-5-32-544";
    if (principal.Claims.SingleOrDefault(x => x.Value == LOCAL_ADMININSTRATORS_GROUP_SID) != null)
    {
        isAdmin = true;
    }
}

return isAdmin;

1

你可以使用类似以下的WMI代码来查找账户是否为管理员,以及关于该账户的任何其他信息

using System;
using System.Management;
using System.Windows.Forms;

namespace WMISample
{
    public class MyWMIQuery
    {
        public static void Main()
        {
            try
            {
                ManagementObjectSearcher searcher = 
                    new ManagementObjectSearcher("root\\CIMV2", 
                    "SELECT * FROM Win32_UserAccount"); 

                foreach (ManagementObject queryObj in searcher.Get())
                {
                    Console.WriteLine("-----------------------------------");
                    Console.WriteLine("Win32_UserAccount instance");
                    Console.WriteLine("-----------------------------------");
                    Console.WriteLine("AccountType: {0}", queryObj["AccountType"]);
                    Console.WriteLine("FullName: {0}", queryObj["FullName"]);
                    Console.WriteLine("Name: {0}", queryObj["Name"]);
                }
            }
            catch (ManagementException e)
            {
                MessageBox.Show("An error occurred while querying for WMI data: " + e.Message);
            }
        }
    }
}

为了更容易上手,请下载WMI Creator

您还可以使用它访问活动目录(LDAP)或计算机/网络上的其他任何内容。


在我的四核i7上,这个调用导致机器挂起了7秒钟,直到调用返回。 - TripleAntigen

0

有4种可能的方法 - 我更喜欢:

(new WindowsPrincipal(WindowsIdentity.GetCurrent())).IsInRole(WindowsBuiltInRole.Administrator);

这里是代码,可以为您提供当前用户身份的所有相关索赔数据列表。
注意:在返回的索赔列表中,WindowsPrincipal.Current.Claims 和 (new WindowsPrincipal(WindowsIdentity.GetCurrent())).Claims 之间有很大的区别。
Console.WriteLine("press the ENTER key to start listing user claims:");
Console.ReadLine();

Console.WriteLine("---------------------");
Console.WriteLine("---------------------");
bool canBeAdmin = (new WindowsPrincipal(WindowsIdentity.GetCurrent())).IsInRole(WindowsBuiltInRole.Administrator);
Console.WriteLine("GetCurrent IsInRole: canBeAdmin:{0}", canBeAdmin);

Console.WriteLine("---------------------");
Console.WriteLine("---------------------");
canBeAdmin = (new WindowsPrincipal(WindowsIdentity.GetCurrent())).Claims.Any((c) => c.Value == "S-1-5-32-544");
Console.WriteLine("GetCurrent Claim: canBeAdmin?:{0}", canBeAdmin);

Console.WriteLine("---------------------");
Console.WriteLine("---------------------");
canBeAdmin = (new WindowsPrincipal(WindowsIdentity.GetCurrent())).IsInRole("Administrator");
Console.WriteLine("GetCurrent IsInRole \"Administrator\": canBeAdmin?:{0}", canBeAdmin);

Console.WriteLine("---------------------");
Console.WriteLine("---------------------");
canBeAdmin = (new WindowsPrincipal(WindowsIdentity.GetCurrent())).IsInRole("Admin");
Console.WriteLine("GetCurrent IsInRole \"Admin\": canBeAdmin?:{0}", canBeAdmin);

Console.WriteLine("---------------------");
Console.WriteLine("---------------------");
canBeAdmin = WindowsPrincipal.Current.IsInRole("Admin");
Console.WriteLine("Current IsInRole \"Admin\": canBeAdmin:{0}", canBeAdmin);


Console.WriteLine("---------------------");
Console.WriteLine("---------------------");
canBeAdmin = WindowsPrincipal.Current.IsInRole("Administrator");
Console.WriteLine("Current IsInRole \"Administrator\": canBeAdmin:{0}", canBeAdmin);

Console.WriteLine("---------------------");
Console.WriteLine("---------------------");
canBeAdmin = WindowsPrincipal.Current.Claims.Any((c) => c.Value == "S-1-5-32-544");
Console.WriteLine("Current Claim: canBeAdmin?:{0}", canBeAdmin);

Console.WriteLine("---------------------");
Console.WriteLine("---------------------");
Console.WriteLine("WindowsPrincipal Claims:");
Console.WriteLine("---------------------");

var propertyCount = 0;
foreach (var claim in WindowsPrincipal.Current.Claims)
{
    Console.WriteLine("{0}", propertyCount++);
    Console.WriteLine("{0}", claim.ToString());
    Console.WriteLine("Issuer:{0}", claim.Issuer);
    Console.WriteLine("Subject:{0}", claim.Subject);
    Console.WriteLine("Type:{0}", claim.Type);
    Console.WriteLine("Value:{0}", claim.Value);
    Console.WriteLine("ValueType:{0}", claim.ValueType);
}

Console.WriteLine("---------------------");
Console.WriteLine("---------------------");
Console.WriteLine("WindowsPrincipal Identities Claims");
Console.WriteLine("---------------------");

propertyCount = 0;
foreach (var identity in WindowsPrincipal.Current.Identities)
{
    int subPropertyCount = 0;
    foreach (var claim in identity.Claims)
    {
        Console.WriteLine("{0} {1}", propertyCount, subPropertyCount++);
        Console.WriteLine("{0}", claim.ToString());
        Console.WriteLine("Issuer:{0}", claim.Issuer);
        Console.WriteLine("Subject:{0}", claim.Subject);
        Console.WriteLine("Type:{0}", claim.Type);
        Console.WriteLine("Value:{0}", claim.Value);
        Console.WriteLine("ValueType:{0}", claim.ValueType);
    }
    Console.WriteLine();
    propertyCount++;
}

Console.WriteLine("---------------------");
Console.WriteLine("---------------------");
Console.WriteLine("Principal Id Claims");
Console.WriteLine("---------------------");

var p = new WindowsPrincipal(WindowsIdentity.GetCurrent());
foreach (var claim in (new WindowsPrincipal(WindowsIdentity.GetCurrent())).Claims)
{
    Console.WriteLine("{0}", propertyCount++);
    Console.WriteLine("{0}", claim.ToString());
    Console.WriteLine("Issuer:{0}", claim.Issuer);
    Console.WriteLine("Subject:{0}", claim.Subject);
    Console.WriteLine("Type:{0}", claim.Type);
    Console.WriteLine("Value:{0}", claim.Value);
    Console.WriteLine("ValueType:{0}", claim.ValueType);
}

Console.WriteLine("press the ENTER key to end");
Console.ReadLine();

如果您能指出每个结果实际上意味着什么,以及WindowsPrincipalnew WindowsPrincipal之间的区别以及原因,则这可能会更有用。 - Harry Johnston
1
@HarryJohnston 感谢您的反馈。实际上我并不知道 '为什么'(就像您一样,我也无法访问微软内部),我只是发现了 '什么',这是通过运行实验显而易见的。祝你好运。 - OzBob

0

这个怎么样:

using System.Runtime.InteropServices;

internal static class Useful {
    [DllImport("shell32.dll", EntryPoint = "IsUserAnAdmin")]
    public static extern bool IsUserAnAdministrator();
}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接