【发布时间】:2015-02-23 11:02:53
【问题描述】:
我正在尝试从以本地管理员帐户运行的 Windows 服务执行进程。这台机器是 Windows 7,它使用Remote Desktop/Terminal Service APIs。
WTSQueryUserToken 上的代码失败,错误代码 = 5。
首先我尝试从当前线程获取令牌,然后调用SetPrivilege 来启用SE_DEBUG_NAME 和SE_TCB_NAME 权限。然后调用WTSQueryUserToken,但得到错误5。
只是为了澄清: 当服务在本地系统(localSystem)下时,即使不需要调用SetPrivilege,这段代码也能完美运行。现在的问题是我需要将服务移动到本地管理员用户下运行!!!
知道我错过了什么吗?
代码:
BOOL T_Ex_RunProgram (DWORD sessionId, LPCWSTR targetPath)
{
#ifdef DEBUG
if(g_pLog)
{
g_pLog->Format ("T_Ex_RunProgram: sessionId = %d, targetPath = \"%S\"\n", sessionId, targetPath);
}
#endif
HANDLE htoken;
if(!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, FALSE, &htoken))
{
if (GetLastError() == ERROR_NO_TOKEN)
{
if (!ImpersonateSelf(SecurityImpersonation))
{
DWORD dwErr = GetLastError();
#ifdef DEBUG
if(g_pLog)
{
g_pLog->Format ("ImpersonateSelf::RunProgram: dwErr = %d\n", dwErr);
}
#endif
SetLastError(dwErr);
return FALSE;
}
if(!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, FALSE, &htoken))
{
DWORD dwErr = GetLastError();
#ifdef DEBUG
if(g_pLog)
{
g_pLog->Format ("OpenThreadToken::RunProgram: dwErr = %d\n", dwErr);
}
#endif
SetLastError(dwErr);
return FALSE;
}
}
else{
DWORD dwErr = GetLastError();
#ifdef DEBUG
if(g_pLog)
{
g_pLog->Format ("OpenThreadToken::RunProgram: GetLastError return unexpected dwErr = %d\n", dwErr);
}
#endif
SetLastError(dwErr);
return FALSE;
}
}
#ifdef DEBUG
if(g_pLog)
{
g_pLog->Format ("Before SetPrivilege(SE_DEBUG_NAME)::RunProgram\n");
}
#endif
// enable SeDebugPrivilege
if(!SetPrivilege(htoken, SE_DEBUG_NAME, TRUE))
{
// close token handle
CloseHandle(htoken);
DWORD dwErr = GetLastError();
#ifdef DEBUG
if(g_pLog)
{
g_pLog->Format ("SetPrivilege::RunProgram((SE_DEBUG_NAME): dwErr = %d\n", dwErr);
}
#endif
SetLastError(dwErr);
return FALSE;
}
#ifdef DEBUG
if(g_pLog)
{
g_pLog->Format ("Before SetPrivilege(SE_TCB_NAME)::RunProgram\n");
}
#endif
// enable SeDebugPrivilege
if(!SetPrivilege(htoken, SE_TCB_NAME, TRUE))
{
// close token handle
CloseHandle(htoken);
DWORD dwErr = GetLastError();
#ifdef DEBUG
if(g_pLog)
{
g_pLog->Format ("SetPrivilege(SE_TCB_NAME)::RunProgram: dwErr = %d\n", dwErr);
}
#endif
SetLastError(dwErr);
return FALSE;
}
BOOL b = WTSQueryUserToken (sessionId, &htoken);
if (!b)
{
DWORD dwErr = GetLastError();
#ifdef DEBUG
if(g_pLog)
{
g_pLog->Format ("T_Ex_RunProgram: WTSQueryUserToken failed. dwErr = %d\n", dwErr);
}
#endif
SetLastError(dwErr);
return FALSE;
}
LPWSTR userName, userName1;
DWORD userNameLength;
b = WTSQuerySessionInformationW (WTS_CURRENT_SERVER_HANDLE, sessionId, WTSUserName, &userName, &userNameLength);
if (b)
{
userName1 = _wcsdup (userName);
WTSFreeMemory (userName);
}
else
{
DWORD dwErr = GetLastError();
#ifdef DEBUG
if(g_pLog)
{
g_pLog->Format ("T_Ex_RunProgram: WTSQuerySessionInformation failed: dwErr = %d\n", dwErr);
}
#endif
SetLastError(dwErr);
return FALSE;
}
b = RunProgramWithToken (htoken, userName1, targetPath,sessionId);
DWORD dwreturnErr = GetLastError();
#ifdef DEBUG
if(g_pLog)
{
g_pLog->Format ("Before SetPrivilege(SE_DEBUG_NAME,FALSE)::RunProgram\n");
}
#endif
// enable SeDebugPrivilege
if(!SetPrivilege(htoken, SE_DEBUG_NAME, FALSE))
{
// close token handle
CloseHandle(htoken);
DWORD dwErr = GetLastError();
#ifdef DEBUG
if(g_pLog)
{
g_pLog->Format ("SetPrivilege::RunProgram((SE_DEBUG_NAME,FALSE): dwErr = %d\n", dwErr);
}
#endif
SetLastError(dwErr);
}
#ifdef DEBUG
if(g_pLog)
{
g_pLog->Format ("Before SetPrivilege(SE_TCB_NAME,FALSE)::RunProgram\n");
}
#endif
// enable SeDebugPrivilege
if(!SetPrivilege(htoken, SE_TCB_NAME, FALSE))
{
// close token handle
CloseHandle(htoken);
DWORD dwErr = GetLastError();
#ifdef DEBUG
if(g_pLog)
{
g_pLog->Format ("SetPrivilege(SE_TCB_NAME,FALSE)::RunProgram: dwErr = %d\n", dwErr);
}
#endif
SetLastError(dwErr);
}
free (userName1);
CloseHandle (htoken);
if (!b)
SetLastError (dwreturnErr);
return b;
}
BOOL SetPrivilege( HANDLE hToken, // access token handle
LPCTSTR lpszPrivilege, // name of privilege to enable/disable
BOOL bEnablePrivilege // to enable or disable privilege
)
{
TOKEN_PRIVILEGES tp;
LUID luid;
if ( !LookupPrivilegeValue(
NULL, // lookup privilege on local system
lpszPrivilege, // privilege to lookup
&luid ) ) // receives LUID of privilege
{
printf("LookupPrivilegeValue error: %u\n", GetLastError() );
return FALSE;
}
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
if (bEnablePrivilege)
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
else
tp.Privileges[0].Attributes = 0;
// Enable the privilege or disable all privileges.
if ( !AdjustTokenPrivileges(
hToken,
FALSE,
&tp,
sizeof(TOKEN_PRIVILEGES),
(PTOKEN_PRIVILEGES) NULL,
(PDWORD) NULL) )
{
printf("AdjustTokenPrivileges error: %u\n", GetLastError() );
return FALSE;
}
if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
{
printf("The token does not have the specified privilege. \n");
return FALSE;
}
return TRUE;
}
【问题讨论】:
-
通常,您只需要
SE_DEBUG_NAME或SE_TCB_NAME之一。但您可能需要启用SE_ASSIGNPRIMARYTOKEN_NAME。那是让您替换主要进程令牌的那个,它是“用进程做任何你想做的事”这三位一体中的第三个。请参阅 MSDN 上的 Privilege Constants。 -
目标用户帐户可能需要一些登录权限。事件日志 (
eventvwr) 应该为您提供更多信息和更多详细信息。另外,对于初学者,请参阅知识库文章How to set logon user rights by using the NTRights utility。它应该让您能够找到更多信息。 -
另外,您使用的 Windows 版本是什么。这是服务器 2008 吗?终端服务在 XP 和 2003(以及可能的其他版本)中的行为略有不同,它可能有助于其他人为您提供帮助。
-
您说的是“本地管理员帐户”。该帐户是否已启用(我认为默认情况下已禁用)。您可以根据 MSDN 博客尝试使用
LocalSystem来尝试隔离问题吗?还有another blog 声明它需要SE_TCB_NAME。这对我来说似乎有点沉重,但在您可以测试减少权限之前使用它。 -
@Joseph:
WTSQueryUserToken()仅适用于LocalSystem帐户。 documentation 也这么说:“要成功调用此函数,调用应用程序必须在 LocalSystem 帐户的上下文中运行,并具有 SE_TCB_NAME 权限。”另一种获取会话用户令牌的方法是从会话中运行的进程中提取令牌,例如explorer.exe。
标签: c winapi windows-7 terminal-services