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

时间:2014-11-13 16:04:34

标签: windows winapi service

我正在尝试使服务在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检查的实数)并且假设在它之后保存用户令牌但由于某种原因它失败了。 ......有什么想法吗?

2 个答案:

答案 0 :(得分:11)

我在自己的服务中使用类似于你的代码,它工作正常。有些事情需要考虑到你所展示的代码没有做到:

  1. 调用WTSQueryUserToken()时,您必须确保您的服务进程已启用SE_TCB_NAME权限。请使用AdjustTokenPrivileges()

  2. WTSGetActiveConsoleSessionId() 返回的会话ID可能不是是您运行生成进程所需的正确会话!它返回附加到本地计算机的物理控制台(屏幕/键盘/鼠标)的会话ID(如果有)。该会话可能正在显示安全的WinLogon桌面,这意味着没有用户实际登录到物理计算机,因此对该会话ID调用WTSQueryUserToken()将失败,并显示ERROR_NO_TOKEN错误。例如,用户可以通过远程桌面连接登录,在这种情况下,该连接将在与控制台不同的会话中运行。如果您希望生成的进程在用户登录的会话中运行,则需要使用WTSEnumerateSessions()来查找处于WTSActive状态的会话。即使这样,WTSQueryUserToken()也可能无法返回令牌,具体取决于用户的登录方式,因此您需要在找到的每个活动会话上调用WTSQueryUserToken(),直到找到成功为您提供令牌的会话为止。

  3. 致电DuplicateTokenEx()时,请使用SecurityIdentification代替SecurityDelegation

  4. 调用CreateProcessAsUser()时,您可以先调用CreateEnvironmentBlock()创建适合该特定用户的环境,然后将该指针传递给CreateProcessAsUser()。否则,生成的进程将使用您的服务环境。此步骤是可选的,具体取决于衍生应用程序的特定需求。

答案 1 :(得分:1)

代码正确无误。...确保包含 “ wtsapi.h” 头文件和此“ #pragma注释( lib,“ WtsApi32.lib”) “,因为加载dll很重要,否则您将获得链接错误(错误LNK2019:无法解析的外部符号 ) 。 并且也不需要复制令牌...并且不需要特权设置,因为从用户帐户中的服务调用该过程时,默认会话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文件(例如,文件资源管理器,记事本等)。服务(来自Windows应用程序)。