簡體   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