从服务的用户会话中创建进程

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 应用程序)的服务。

【讨论】:

以上是关于从服务的用户会话中创建进程的主要内容,如果未能解决你的问题,请参考以下文章

如何从 Windows 服务启动进程到当前登录的用户会话

如何从Windows服务启动进程到当前登录的用户会话

从服务启动用户会话中的进程

从会话 0(服务)启动提升的用户进程 [重复]

如何在Informatica Cloud中创建进程实时暴露为Restful服务,可以从浏览器或其他地方调用?

如何从 List 中创建多个 GenServer 进程并映射存储在其中的数据?