我的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:
从管理员帐户运行应用程序。
调用IsUserAdmin()=>返回TRUE
(好)
调用模仿("非管理员用户","域名","密码")=>返回FALSE
(好!)
情景2:
使用管理员帐户中的runas.exe
从非管理员帐户运行应用程序。
调用IsUserAdmin()=>返回FALSE
(好)
呼叫模仿("管理员","域","密码")=>返回FALSE
(不好!)
情景3:
直接从非管理员帐户运行 应用程序。
调用IsUserAdmin()=>返回FALSE
(好)
呼叫模仿("管理员","域","密码")=>返回FALSE
(不好!)
所有方案同时打印step 1
和step 2
。
据我所知,鉴于合法的证书,上述内容应该保证冒充。我在这里错过了什么?
答案 0 :(得分:3)
在执行需要管理员权限的任务之前,您真的不应该以可编程方式检查管理员权限。只是无条件地尝试任务,让API告诉您任务是否由于权限不足而失败,如果是,那么您可以决定是否只是因错误而导致任务失败,或者提示用户输入凭据并再次尝试该任务。
如果你想与UAC玩得很好,你应该做的是将你的管理代码作为一个单独的进程或COM对象实现,然后你可以在需要时以升高的状态运行该进程/ COM而不必提升你的主要过程。当操作系统需要时,让操作系统提示用户输入管理员凭据(并确定该提示应该如何显示),不要自己手动完成。
答案 1 :(得分:1)
Remy的回答是正确的,您正在尝试做的不是您的方案的正确解决方案。但是,根据文档,您的代码应该按预期工作。似乎有两个原因:
与文档相反,您似乎无法在没有LogonUser
的情况下冒充SecurityIdentification
以高于SeImpersonatePrivilege
的模拟级别获得的令牌。 [在Windows 7 SP1 x64上测试。]
如果您为令牌传递CheckTokenMembership
并且线程的模拟级别为NULL
,则SecurityIdentification
函数无法正常工作。 [同上。]
通过使用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;
}