Windows冒充的奇怪行为

时间:2013-12-09 20:45:09

标签: c++ windows winapi authentication impersonation

我的Windows应用程序可能需要某些部分的管理权限。 对于这些情况,我想要求用户提供管理员凭据,并使用以下代码来模拟管理员:

BOOL impersonate(LPTSTR lpszUsername, LPTSTR lpszDomain, LPTSTR lpszPassword) {

    BOOL ret = LogonUser(lpszUsername,
                         lpszDomain,
                         lpszPassword,
                         LOGON32_LOGON_INTERACTIVE,
                         LOGON32_PROVIDER_DEFAULT,
                         &hToken);
    if (ret != TRUE) return FALSE;

    OutputDebugString (L"step 1");

    ret = ImpersonateLoggedOnUser(hToken);
    if (ret != TRUE) return FALSE;

    OutputDebugString(L"step 2");

    return IsUserAdmin()
}

函数IsUserAdmin() has been taken from MSDN,如下所示:

BOOL IsUserAdmin(VOID) {
    BOOL b;
    SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
    PSID AdministratorsGroup; 
    b = AllocateAndInitializeSid( &NtAuthority,
                                  2,
                                  SECURITY_BUILTIN_DOMAIN_RID,
                                  DOMAIN_ALIAS_RID_ADMINS,
                                  0, 0, 0, 0, 0, 0,
                                  &AdministratorsGroup); 
    if (b)  {
        if (!CheckTokenMembership(NULL, AdministratorsGroup, &b)) {
            b = FALSE;
        }
        FreeSid(AdministratorsGroup); 
    }

    return(b);
}

情景1:
从管理员帐户运行应用程序。

  1. 调用IsUserAdmin()=>返回TRUE(好)

  2. 调用模仿("非管理员用户","域名","密码")=>返回FALSE(好!)

  3. 情景2:
    使用管理员帐户中的runas.exe从非管理员帐户运行应用程序。

    1. 调用IsUserAdmin()=>返回FALSE(好)

    2. 呼叫模仿("管理员","域","密码")=>返回FALSE(不好!)

    3. 情景3:
      直接从非管理员帐户运行 应用程序。

      1. 调用IsUserAdmin()=>返回FALSE(好)

      2. 呼叫模仿("管理员","域","密码")=>返回FALSE(不好!)

      3. 所有方案同时打印step 1step 2

        据我所知,鉴于合法的证书,上述内容应该保证冒充。我在这里错过了什么?

2 个答案:

答案 0 :(得分:3)

在执行需要管理员权限的任务之前,您真的不应该以可编程方式检查管理员权限。只是无条件地尝试任务,让API告诉您任务是否由于权限不足而失败,如果是,那么您可以决定是否只是因错误而导致任务失败,或者提示用户输入凭据并再次尝试该任务。

如果你想与UAC玩得很好,你应该做的是将你的管理代码作为一个单独的进程或COM对象实现,然后你可以在需要时以升高的状态运行该进程/ COM而不必提升你的主要过程。当操作系统需要时,让操作系统提示用户输入管理员凭据(并确定该提示应该如何显示),不要自己手动完成。

答案 1 :(得分:1)

Remy的回答是正确的,您正在尝试做的不是您的方案的正确解决方案。但是,根据文档,您的代码应该按预期工作。似乎有两个原因:

  1. 与文档相反,您似乎无法在没有LogonUser的情况下冒充SecurityIdentification以高于SeImpersonatePrivilege的模拟级别获得的令牌。 [在Windows 7 SP1 x64上测试。]

  2. 如果您为令牌传递CheckTokenMembership并且线程的模拟级别为NULL,则SecurityIdentification函数无法正常工作。 [同上。]

  3. 通过使用OpenThreadToken显式提取模拟令牌而不是将NULL作为令牌传递,您可以轻松解决问题2,如下所示。或者,您可以使用DuplicateToken复制主令牌,并将复制的令牌传递给CheckTokenMembership。如果你想要的只是检查令牌的内容,那将是一个更有效的解决方案。

    我不知道问题1的任何解决方法,除了在新上下文中启动子进程以代表您完成工作。当然,正如雷米指出的那样,无论如何,这都是你应该做的。

    以下是我在测试中使用的一些代码,供参考:

    #include <Windows.h>
    
    #include <stdio.h>
    #include <conio.h>
    
    void fail(wchar_t * err)
    {
        DWORD dw = GetLastError();
        printf("%ws: %u\n", err, dw);
        ExitProcess(1);
    }
    
    void impersonate(LPTSTR lpszUsername, LPTSTR lpszDomain, LPTSTR lpszPassword) 
    {
        HANDLE hToken, hImpToken2;
        BOOL b;
        SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
        PSID AdministratorsGroup; 
        DWORD dwLen;
        SECURITY_IMPERSONATION_LEVEL imp_level;
    
        if (!LogonUser(lpszUsername,
            lpszDomain,
            lpszPassword,
            LOGON32_LOGON_INTERACTIVE,
            LOGON32_PROVIDER_DEFAULT,
            &hToken)) fail(L"LogonUser");
    
        if (!ImpersonateLoggedOnUser(hToken)) 
            fail(L"ImpersonateLoggedOnUser");
    
        if (!OpenThreadToken(GetCurrentThread(), MAXIMUM_ALLOWED, TRUE, 
            &hImpToken2)) 
            fail(L"OpenThreadToken");
    
        if (!GetTokenInformation(hImpToken2, TokenImpersonationLevel, 
            &imp_level, sizeof(imp_level), &dwLen)) 
            fail(L"GetTokenInformation");
    
        printf("Impersonation level: %u\n", imp_level);
    
        if (!AllocateAndInitializeSid( &NtAuthority,
                                      2,
                                      SECURITY_BUILTIN_DOMAIN_RID,
                                      DOMAIN_ALIAS_RID_ADMINS,
                                      0, 0, 0, 0, 0, 0,
                                      &AdministratorsGroup)) 
            fail(L"AllocateAndInitializeSid");
    
        if (!CheckTokenMembership(hImpToken2, AdministratorsGroup, &b)) 
            fail(L"CheckTokenMembership");
    
        if (!b) fail(L"membership");
    }
    
    int main(int argc, char ** argv)
    {
        wchar_t password[1024];
        wchar_t * ptr;
    
        for (ptr = password;; ptr++)
        {
            *ptr = _getwch();
            if (*ptr == 13) break;
        }
    
        *ptr = '\0';
    
        impersonate(L"Administrator", NULL, password);
    
        return 0;
    }