2013-12-09 62 views
3

我的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(好!)

方案2:
運行從非管理員帳戶的應用程序,使用從runas.exe管理員帳戶。

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

  2. 電話冒充( 「管理員」, 「域名」, 「密碼」)=>返回FALSE(不太好!)

方案3:
運行從非管理員accou應用直接 NT。

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

  2. 電話冒充( 「管理員」, 「域名」, 「密碼」)=>返回FALSE(不太好!)

所有情況都打印step 1step 2

據我所知,上面應該有保證模擬,鑑於合法憑證。我在這裏錯過了什麼?

+1

你說的'假冒()'返回FALSE,但你沒有指明究竟是失敗的。如果'LogonUser()'失敗,'ImpersonateLoggedOnUser()'失敗,'AllocateAndInitializeSid()'失敗,或者'CheckTokenMembership()'失敗,''impersonate()'有4個失敗點可以返回FALSE。您需要調試您的代碼並準確找出實際失敗的內容。 –

回答

3

在執行需要管理員權限的任務之前,您確實不應該可編程地檢查管理員權限。只需無條件地嘗試任務,並讓API告訴您任務是否由於權限不足而失敗,如果是,那麼您可以決定是否僅以失敗的方式使任務失敗,或者提示用戶輸入憑據並再次嘗試任務。

如果您嘗試使用UAC進行遊戲,您應該做的是將您的管理代碼作爲單獨的進程或COM對象實現,然後可以在需要時以高級狀態運行該進程/ COM,而無需提升你的主流程。讓操作系統在操作系統需要時提示用戶輸入管理員憑據(並決定該提示的外觀),不要自己手動執行。

1

雷米的答案是現貨,你試圖做的不是你的方案的正確解決方案。但是,根據文檔,您的代碼應該按照預期工作。似乎有兩個原因,爲什麼它不:

  1. 看來,違背了文件,你不能模擬在更高模擬級別比SecurityIdentification通過LogonUser獲得令牌未持有SeImpersonatePrivilege。 [測試在Windows 7 SP1 64位。]

  2. 如果傳遞NULL的令牌和線程的模擬級別SecurityIdentificationCheckTokenMembership功能不能正常工作。 [同上]

您可以解決問題2很輕鬆地通過使用OpenThreadToken明確提取模擬令牌,而不是通過NULL作爲標記,如下圖所示。或者,您可以使用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; 
}