从服务的用户会话中创建进程
Posted
技术标签:
【中文标题】从服务的用户会话中创建进程【英文标题】:create process in user session from service 【发布时间】:2014-11-13 16:04:33 【问题描述】:我正在尝试让服务在 Windows 中打开的会话中创建一个进程。 我有这个代码:
sessionId =WTSGetActiveConsoleSessionId();
if (WTSQueryUserToken(sessionId,&dummy))
if (!DuplicateTokenEx(dummy, TOKEN_ALL_ACCESS, NULL, SecurityDelegation, TokenPrimary, &token))
CloseHandle(dummy);
return false;
CloseHandle(dummy);
// Create process for user with desktop
myfile = fopen("c:\\temp\\test123.txt", "a");
fprintf(myfile, "before create!!!!\n");
fclose(myfile);
if (!CreateProcessAsUser(token, NULL,NULL, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) // The "new console" is necessary. Otherwise the process can hang our main process
CloseHandle(token);
myfile = fopen("c:\\temp\\test123.txt", "a");
fprintf(myfile, " create failed!\n");
fclose(myfile);
return false;
CloseHandle(token);
else
myfile = fopen("c:\\temp\\test123.txt", "a");
fprintf(myfile, "Dummy fail\n");
fprintf(myfile, "last error is %d \n", GetLastError());
fclose(myfile);
//int ret = CreateProcess(FILE_EXEC, NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
如果我在安装服务时使用最后一行(注释的)一切正常,因为它在安装服务时运行,所以它发生在用户会话中但是当我希望服务执行它时它会失败, sessionId 没问题,失败开始于:
if (WTSQueryUserToken(sessionId,&dummy))
我知道 WTSQueryUserToken 是一个应该从服务运行的函数,sessionid 是 1(它是 cmd 检查的实数),并且假人假设在它之后持有用户令牌,但由于某种原因它失败了。 ...有什么想法吗?
【问题讨论】:
错误号是?请注意,您对GetLastError
的调用将返回废话,因为您在函数失败后很久才调用它。
【参考方案1】:
我在自己的服务中使用与您类似的代码,它运行良好。有一些事情需要考虑,你展示的代码没有做:
调用WTSQueryUserToken()
时,您必须确保您的服务进程启用了SE_TCB_NAME
权限。为此使用AdjustTokenPrivileges()
。
WTSGetActiveConsoleSessionId()
返回的会话 ID 可能不是是您运行衍生进程所需的正确会话!它返回附加到本地计算机的物理控制台(屏幕/键盘/鼠标)(如果有)的会话 ID。该会话可能正在显示安全的 WinLogon 桌面,这意味着实际上没有用户登录到物理机,因此在该会话 ID 上调用 WTSQueryUserToken()
将失败并出现 ERROR_NO_TOKEN
错误。例如,用户可以通过远程桌面连接登录,在这种情况下,该连接将在与控制台不同的会话中运行。如果您希望生成的进程在用户登录的会话中运行,您需要使用WTSEnumerateSessions()
来查找处于WTSActive
状态的会话。即使这样,WTSQueryUserToken()
也可能不会返回令牌,具体取决于用户的登录方式,因此您需要在找到的每个活动会话上调用 WTSQueryUserToken()
,直到找到一个成功为您提供令牌的会话。
调用DuplicateTokenEx()
时,使用SecurityIdentification
而不是SecurityDelegation
。
调用CreateProcessAsUser()
时,您可以先调用CreateEnvironmentBlock()
以创建适合该特定用户的环境,然后将该指针传递给CreateProcessAsUser()
。否则,生成的进程将使用您的服务环境。此步骤是可选的,具体取决于衍生应用的特定需求。
【讨论】:
是否有必要复制令牌?该文档似乎暗示 WTSQueryUserToken 返回一个主令牌,所以我希望能够直接在对 CreateProcessAsUser 的调用中使用它。 根据this的说法,没有必要,但我从未尝试过验证。在调用CreateProcessAsUser()
时,有大量使用DuplicateTokenEx()
和WTSQueryUserToken()
的示例。也许这是旧操作系统版本的要求,不再需要?我不知道。
首先谢谢,我正在使用 CreateProcessAsUser(token,NULL,NULL,NULL,NULL,FALSE,NORMAL_PRIORITY_CLASS,NULL,NULL,$si,&pi) 我得到错误 183,知道我还有什么可以吗?
错误 183 是 ERROR_ALREADY_EXISTS
,这不是 CreateProcessAsUser()
应该报告的错误代码。您可能没有正确管理错误处理,例如,如果您在错误的时间调用 GetLastError()
并看到来自另一个系统函数而不是 CreateProcessAsUser()
的错误。
你说得对,但知道为什么 CreateProcessAsUser 会失败吗??【参考方案2】:
代码正确....确保包含 "wtsapi.h" 头文件和此“#pragma comment( lib, "WtsApi32.lib")",因为加载 dll 很重要,否则会出现链接错误(error LNK2019: unresolved external symbol)。 并且不需要复制令牌......并且不需要权限设置,因为当从您的用户帐户中的服务调用进程时......默认会话ID将为0,即。 SYSTEM帐户..拥有用户可以拥有的所有权限...
当从服务调用进程时,我的代码可以正常工作:
LPCWSTR path=L"C:\\Windows\\System32\\notepad.exe";//change the path accordingly
PROCESS_INFORMATION pi;
STARTUPINFO si;
DWORD nsid=1;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof( si );
HANDLE htoken;
DWORD sessionId;
sessionId =WTSGetActiveConsoleSessionId();
WTSQueryUserToken(sessionId,&htoken);
si.wShowWindow=TRUE;
if (CreateProcessAsUser(htoken, path, NULL, NULL,
NULL, FALSE, 0, NULL, NULL, &si, &pi ))
/* Process has been created; work with the process and wait for it to
terminate. */
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
CloseHandle(htoken);
关于服务的清晰概念参考here。下载源代码,代码完美。 只需在“ServiceWorkerThread”函数中插入这段代码。这段代码用于从您的服务中打开一个 .exe(例如,文件资源管理器、记事本等)文件。启动您的 .exe 文件时将打开来自 services.msc(windows 应用程序)的服务。
【讨论】:
以上是关于从服务的用户会话中创建进程的主要内容,如果未能解决你的问题,请参考以下文章