繁体   English   中英

Windows冒充的奇怪行为

[英]Odd behavior of windows 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() 取自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. 调用impersonate(“非管理员用户”,“域名”,“密码”)=>返回FALSE (好!)

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

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

  2. 调用impersonate(“administrator”,“domain”,“password”)=>返回FALSE (不好!)

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

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

  2. 调用impersonate(“administrator”,“domain”,“password”)=>返回FALSE (不好!)

所有方案都打印step 1step 2

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

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

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

Remy的回答很明显,你要做的不是你的场景的正确解决方案。 但是,根据文档,您的代码应该按预期工作。 似乎有两个原因:

  1. 与文档相反,您似乎无法在没有保留SeImpersonatePrivilege情况下模拟比SecurityIdentification更高的模拟级别通过LogonUser获取的令牌。 [在Windows 7 SP1 x64上测试。]

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

通过使用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;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM