从 Vista 上的服务中使用 CreateProcessAsUser 的桌面问题
Posted
技术标签:
【中文标题】从 Vista 上的服务中使用 CreateProcessAsUser 的桌面问题【英文标题】:Desktop problem with using CreateProcessAsUser from a service on Vista 【发布时间】:2010-02-06 06:30:01 【问题描述】:我正在使用 VC++ 从 Vista 上使用 CreateProcessAsUser 的服务创建进程。当我通过本地登录机器执行此代码时,进程创建成功。当我使用远程桌面 (mstsc) 从另一台机器登录到我的机器并启动我的应用程序时,相同的代码失败。
我可以理解,当我进行远程登录时,活动桌面被视为远程桌面。谁能帮我获取当前登录用户桌面的桌面名称而不是远程桌面的桌面名称。
这是我的代码
ACTIVECONSOLESESSIONIDFUNC lpfnProc; // WTSGetActiveConsoleSessionId function pointer
HMODULE hModule = NULL; // Instance for kernel32.dll library
DWORD dwSessionId = 0; // Session ID
HANDLE hToken = NULL; // Active session token
HANDLE hDupToken = NULL; // Duplicate session token
WCHAR szErr[1024] = 0;
LPVOID lpEnvironment = NULL; // Environtment block
// Get the active session ID
hModule = LoadLibrary(KERNEL32LIB);
if(!hModule)
//wsprintf(szErr, L"LoadLibrary Error: %d", GetLastError());
return;
lpfnProc = (ACTIVECONSOLESESSIONIDFUNC)GetProcAddress(hModule,"WTSGetActiveConsoleSessionId");
dwSessionId = lpfnProc();
// Get token of the logged in user by the active session ID
BOOL bRet = WTSQueryUserToken(dwSessionId, &hToken);
if (!bRet)
//wsprintf(szErr, L"WTSQueryUserToken Error: %d", GetLastError());
return;
// Get duplicate token from the active logged in user's token
bRet = DuplicateTokenEx(hToken, // Active session token
MAXIMUM_ALLOWED, // Desired access
NULL, // Token attributes
SecurityIdentification, // Impersonation level
TokenPrimary, // Token type
&hDupToken); // New/Duplicate token
if (!bRet)
//wsprintf(szErr, L"DuplicateTokenEx Error: %d", GetLastError());
return;
// Get all necessary environment variables of logged in user
// to pass them to the process
bRet = CreateEnvironmentBlock(&lpEnvironment, // Environment block
hDupToken, // New token
FALSE); // Inheritence
if(!bRet)
//wsprintf(szErr, L"CreateEnvironmentBlock Error: %d", GetLastError());
return;
HDESK hdeskInput=OpenInputDesktop(0, FALSE, 0); // does not set GetLastError(), so GetLastError() is arbitrary if NULL is returned
if( hdeskInput==NULL )
TRACE( "hdeskInput==NULL" );
return false;
// Initialize Startup and Process info
startupInfo->cb = sizeof(STARTUPINFO);
startupInfo->lpDesktop = TEXT("winsta0\\default");
// Start the process on behalf of the current user
BOOL returnCode = CreateProcessAsUser(hDupToken,
applicationName,
commandLine,
NULL,
NULL,
FALSE,
NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT,
lpEnvironment,
NULL,
startupInfo,
&processInformation);
谢谢, F
【问题讨论】:
【参考方案1】:这里有完整的工作代码来做你想做的事
//Function to run a process as active user from windows service
void ImpersonateActiveUserAndRun(WCHAR* path, WCHAR* args)
DWORD session_id = -1;
DWORD session_count = 0;
WTS_SESSION_INFOA *pSession = NULL;
if (WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSession, &session_count))
//log success
else
//log error
return;
for (int i = 0; i < session_count; i++)
session_id = pSession[i].SessionId;
WTS_CONNECTSTATE_CLASS wts_connect_state = WTSDisconnected;
WTS_CONNECTSTATE_CLASS* ptr_wts_connect_state = NULL;
DWORD bytes_returned = 0;
if (::WTSQuerySessionInformation(
WTS_CURRENT_SERVER_HANDLE,
session_id,
WTSConnectState,
reinterpret_cast<LPTSTR*>(&ptr_wts_connect_state),
&bytes_returned))
wts_connect_state = *ptr_wts_connect_state;
::WTSFreeMemory(ptr_wts_connect_state);
if (wts_connect_state != WTSActive) continue;
else
//log error
continue;
HANDLE hImpersonationToken;
if (!WTSQueryUserToken(session_id, &hImpersonationToken))
//log error
continue;
//Get real token from impersonation token
DWORD neededSize1 = 0;
HANDLE *realToken = new HANDLE;
if (GetTokenInformation(hImpersonationToken, (::TOKEN_INFORMATION_CLASS) TokenLinkedToken, realToken, sizeof(HANDLE), &neededSize1))
CloseHandle(hImpersonationToken);
hImpersonationToken = *realToken;
else
//log error
continue;
HANDLE hUserToken;
if (!DuplicateTokenEx(hImpersonationToken,
//0,
//MAXIMUM_ALLOWED,
TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS | MAXIMUM_ALLOWED,
NULL,
SecurityImpersonation,
TokenPrimary,
&hUserToken))
//log error
continue;
// Get user name of this process
//LPTSTR pUserName = NULL;
WCHAR* pUserName;
DWORD user_name_len = 0;
if (WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, session_id, WTSUserName, &pUserName, &user_name_len))
//log username contained in pUserName WCHAR string
//Free memory
if (pUserName) WTSFreeMemory(pUserName);
ImpersonateLoggedOnUser(hUserToken);
STARTUPINFOW StartupInfo;
GetStartupInfoW(&StartupInfo);
StartupInfo.cb = sizeof(STARTUPINFOW);
//StartupInfo.lpDesktop = "winsta0\\default";
PROCESS_INFORMATION processInfo;
SECURITY_ATTRIBUTES Security1;
Security1.nLength = sizeof SECURITY_ATTRIBUTES;
SECURITY_ATTRIBUTES Security2;
Security2.nLength = sizeof SECURITY_ATTRIBUTES;
void* lpEnvironment = NULL;
// Get all necessary environment variables of logged in user
// to pass them to the new process
BOOL resultEnv = CreateEnvironmentBlock(&lpEnvironment, hUserToken, FALSE);
if (!resultEnv)
//log error
continue;
WCHAR PP[1024]; //path and parameters
ZeroMemory(PP, 1024 * sizeof WCHAR);
wcscpy(PP, path);
wcscat(PP, L" ");
wcscat(PP, args);
// Start the process on behalf of the current user
BOOL result = CreateProcessAsUserW(hUserToken,
NULL,
PP,
//&Security1,
//&Security2,
NULL,
NULL,
FALSE,
NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE,
//lpEnvironment,
NULL,
//"C:\\ProgramData\\some_dir",
NULL,
&StartupInfo,
&processInfo);
if (!result)
//log error
else
//log success
DestroyEnvironmentBlock(lpEnvironment);
CloseHandle(hImpersonationToken);
CloseHandle(hUserToken);
CloseHandle(realToken);
RevertToSelf();
WTSFreeMemory(pSession);
【讨论】:
说得好,赞!【参考方案2】:什么是启动服务以创建新流程,因为这与合适的答案有关? WTSGetActiveConsoleSessionId 获取当前物理控制台会话的会话 ID,而不是您意义上的“活动”会话 ID。对于终端服务,可能有许多不同的用户都连接到不同的会话,因此您不能只选择一个并希望,但实际上可能有也可能没有实际登录的用户。
一种方法是使用WTSEnumerateSessions 列出所有活动会话并尝试找到您真正想要的会话,您可以使用WTSQuerySessionInformation 之类的名称来获取该会话的用户名。
如果您的服务这样做是为了响应您的用户帐户的某些请求(而不是对某些自动事件采取行动),那么如果您使用诸如 RPC/Named Pipes/DCOM 等之类的东西,您总是可以模拟用户(假设安全性设置为允许非匿名/识别模拟)并将线程的令牌复制到主令牌并使用它。
【讨论】:
谢谢泰伦。我上面粘贴的代码是我通过浏览网络得到的。我对桌面或会话一无所知。我希望你明白我的问题。我的问题是我应该从我的机器登录到我的桌面,然后应用程序运行正常,即。我能够从服务创建进程,但是当我从另一台机器(mstsc)登录到我的桌面时,我的服务无法创建进程。因为它在调用 WtsQueryUserToken 时失败,错误为 1008 ERROR_NO_TOKEN;我得到的会话 id 是 3,而我的实际是 1。请帮忙。 如前所述,实际上创建进程的是什么,如上面的代码是如何被调用的以及在什么上下文中。 我的应用程序有一个在系统帐户下运行的服务创建进程。我想创建一个单独的进程来处理服务的 GUI 相关任务。当我的服务从应用程序接收到管道消息时,尝试创建进程。【参考方案3】:经过大量谷歌搜索后,我能够找到解决问题的方法。
这是我找到解决方案的链接。 http://www.codeproject.com/KB/vista-security/interaction-in-vista.aspx
感谢每一位试图帮助我的人。
【讨论】:
当然我希望你是那台机器上唯一的人,否则你仍然可以在另一个桌面上随机创建一个进程:) 所以即使在这种情况下我也需要解决问题。我需要做什么?以上是关于从 Vista 上的服务中使用 CreateProcessAsUser 的桌面问题的主要内容,如果未能解决你的问题,请参考以下文章
Visual Studio 2008 + Windows Vista 上的嵌入式 Web 服务器
Windows XP 与 Vista/7 上的 MS Crypto API 行为