2012-05-11 95 views

回答

3

当然,你需要调用:

OpenThreadToken() 

要获得用户的令牌。

GetTokenInformation(hToken, TokenGroups, NULL, 0, &dwNeeded) 

要获得的令牌组信息的大小(以及分配足够的空间)

GetTokenInformation(hToken, TokenGroups, pTokenGroups, dwSize, &dwNeeded) 

为了获得本地组

AllocateAndInitializeSid(&SIDAuthNT, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &psidAdministrators); 

要得到管理员的SID

EqualSid() 

To co与您本地组中的SID进行比较。

+0

谢谢,这帮助了我,但起初我没有意识到第一次调用GetTokenInformation可能会返回一个错误。我建议添加到答案中(确保GetLastError()== ERROR_INSUFFICIENT_MEMORY)。 – TBD

+1

这实际上是Win32编程中一个非常常见的模式。无论何时你看到一个API被调用两次,第一次使用NULL时,通常情况下,第一次调用获取大小,以便为第二次调用分配正确的内存。 – Benj

6

有几种根本不同的方法。不幸的是,最常见的 非常乏味。它包括确定当前 用户的SID,然后找到该组他所属的,然后 发现其中一个是否是管理员组:

#include <windows.h> 
#include <vector> 

bool is_administrator() { 
    HANDLE access_token; 
    DWORD buffer_size = 0; 
    PSID admin_SID; 
    TOKEN_GROUPS *group_token = NULL; 
    SID_IDENTIFIER_AUTHORITY NT_authority = SECURITY_NT_AUTHORITY; 

    if (!OpenProcessToken(GetCurrentProcess(),TOKEN_READ,&access_token)) 
     return false; 

    GetTokenInformation( 
     access_token, 
     TokenGroups, 
     group_token, 
     0, 
     &buffer_size 
     ); 

    std::vector<char> buffer(buffer_size); 

    group_token = 
     reinterpret_cast<TOKEN_GROUPS*>(&buffer[0]); 

    bool succeeded = GetTokenInformation( 
     access_token, 
     TokenGroups, 
     group_token, 
     buffer_size, 
     &buffer_size 
     ); 

    CloseHandle(access_token); 
    if (!succeeded) 
     return false; 

    if (!AllocateAndInitializeSid( 
     &NT_authority, 
     2, 
     SECURITY_BUILTIN_DOMAIN_RID, 
     DOMAIN_ALIAS_RID_ADMINS, 
     0,0,0,0,0,0, 
     &admin_SID 
     )) 
    { 
     return false; 
    } 

    bool found=false; 
    for(int i=0; !found && i < group_token->GroupCount; i++) 
     found = EqualSid(admin_SID,group_token->Groups[i].Sid); 
    FreeSid(admin_SID); 
    return found; 
} 

还有另外一种方式,这是一个有点简单不过:

bool is_administrator() 
{ 
     bool result; 
     DWORD rc; 
     wchar_t user_name[256]; 
     USER_INFO_1 *info; 
     DWORD size = sizeof(user_name); 
     GetUserNameW(user_name, &size); 
     rc = NetUserGetInfo(NULL, user_name, 1, (byte **) &info); 
     if (rc != NERR_Success) 
       return false; 
     result = info->usri1_priv == USER_PRIV_ADMIN; 
     NetApiBufferFree(info); 
     return result; 
} 

在任何一种情况下,如果您有域名,可能会有点毛病, ,因为特定用户可能是本地 机器上的管理员,但不在域上,反之亦然。查找信息 并不一定会有太大变化,但您可能需要考虑一下 找出您真正想要的内容。

编辑:正如@Benj指出的,第一种方法可以真正使用一点更新。虽然我已经修复了它的明显泄漏,但它仍然是一个巨大的,单一的函数,没有例外的安全性,而且通常是过时的编码风格。也许一个小的更新是为了:(?和更短更容易?)

#include <windows.h> 
#include <vector> 
#include <algorithm> 

class sid { 
    PSID s; 
public: 
    sid(SID_IDENTIFIER_AUTHORITY auth, std::vector<DWORD> sub_auths) { 
     DWORD count = sub_auths.size(); 
     sub_auths.resize(7, DWORD()); 

     if (!AllocateAndInitializeSid( 
      &auth, 
      count, 
      sub_auths[0], sub_auths[1], sub_auths[2], sub_auths[3], 
      sub_auths[4], sub_auths[5], sub_auths[6], sub_auths[7], 
      &s 
      )) 
     { 
      throw std::runtime_error("Unable to allocate Admin SID"); 
     } 
    } 

    sid(PSID const &p=NULL) : s(p) {} 
    bool operator==(sid const &r) const { return EqualSid(s, r.s); } 
}; 

class access_token { 
    HANDLE token; 
public: 
    access_token(HANDLE PID=GetCurrentProcess(), DWORD access=TOKEN_READ) { 
     if (!OpenProcessToken(PID, access, &token)) 
      throw std::runtime_error("Unable to open process token"); 
    } 
    operator HANDLE() { return token; } 
    ~access_token() { CloseHandle(token); } 
}; 

std::vector<sid> get_group_sids() { 
    DWORD buffer_size = 0; 
    TOKEN_GROUPS *group_token = NULL; 
    std::vector<sid> groups; 
    access_token token; 

    GetTokenInformation(token, TokenGroups, group_token, 0, &buffer_size); 

    std::vector<char> buffer(buffer_size); 

    group_token = reinterpret_cast<TOKEN_GROUPS*>(&buffer[0]); 

    if (GetTokenInformation(token, TokenGroups, group_token, buffer_size, &buffer_size)) 
     for (int i=0; i<group_token->GroupCount; i++) 
      groups.push_back(group_token->Groups[i].Sid); 
    return groups; 
} 

bool is_administrator() { 
    std::vector<sid> groups = get_group_sids(); 

    SID_IDENTIFIER_AUTHORITY NT_authority = SECURITY_NT_AUTHORITY; 
    std::vector<DWORD> sub_auths; 

    sub_auths.push_back(SECURITY_BUILTIN_DOMAIN_RID); 
    sub_auths.push_back(DOMAIN_ALIAS_RID_ADMINS); 

    sid admin_SID(NT_authority, sub_auths); 

    return std::find(groups.begin(), groups.end(), admin_SID) != groups.end(); 
} 

#ifdef TEST 
#include <iostream> 
#include <iomanip> 

int main() { 
    std::cout << std::boolalpha << is_administrator() << "\n"; 
} 

#endif 
+0

在第一个例子中,new char [buffer_size]被删除了吗? – Benj

+0

总是可以给它一个快速修改为'vector ' – Benj

+1

@Benj:完成。可能会更漂亮,但至少不会如此糟糕...... –

-3

您应该SHELL32

+0

在启用UAC的Win8/10上,IsUserAnAdmin无法正确识别属于管理员组的用户。 – jeffm

4

稍微不同的使用IsUserAnAdmin方法改编自一些提示MSDN:

PSID administrators_group = NULL; 
SID_IDENTIFIER_AUTHORITY nt_authority = SECURITY_NT_AUTHORITY; 
BOOL result = AllocateAndInitializeSid(&nt_authority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &administrators_group); 
BOOL is_user_admin = FALSE; 
if (result) 
{ 
    CheckTokenMembership(NULL, administrators_group, &is_user_admin); 
    FreeSid(administrators_group); 
} 

if (is_user_admin) 
{ 
    // do something here for admin users... 
} 
+0

我还没有测试,但想知道是否可以使用支持UAC的机器来确定UAC提升,但不一定是管理员组的成员资格? – TaterJuice

相关问题