Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

create process in user session from service

i am trying to make a service create a process in opened session in windows. i have this code:

    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);

if i use the last lin e(that commented) in the install of the service all works fine because it runs when the service is been installed so it happens inside the User session but when i want the service to do it it fails, sessionId is ok, the failure starts at :

    if (WTSQueryUserToken(sessionId,&dummy)) {

i know that WTSQueryUserToken is a function that should be run from service, sessionid is 1 (and it is the real number from cmd check) and the dummy is suppose to hold the user token after it but for some reason it fails.... any ideas?

like image 321
Omega Doe Avatar asked Nov 13 '14 16:11

Omega Doe


2 Answers

I use code similar to yours in my own service and it works fine. There are some things that need to be taken into account that the code you showed is not doing:

  1. When calling WTSQueryUserToken(), you have to make sure your service process has the SE_TCB_NAME privilege enabled. Use AdjustTokenPrivileges() for that.

  2. The session ID returned by WTSGetActiveConsoleSessionId() may not be the correct session that you need to run your spawned process on! It returns the session ID that is attached to the physical console (screen/keyboard/mouse) of the local machine, if any. That session may be displaying the secure WinLogon desktop, meaning no user is actually logged in to the physical machine, so calling WTSQueryUserToken() on that session ID would fail with an ERROR_NO_TOKEN error. A user can log in over a Remote Desktop Connection, for instance, in which case that connection would be running in a different session than the console. If you want your spawned process to run in a session that has a user logged in, you need to use WTSEnumerateSessions() to find a session that is in the WTSActive state. And even then, WTSQueryUserToken() may not return a token depending on how the user is logged in, so you need to call WTSQueryUserToken() on each active session you find until you find one that successfully gives you a token.

  3. When calling DuplicateTokenEx(), use SecurityIdentification instead of SecurityDelegation.

  4. When calling CreateProcessAsUser(), you can call CreateEnvironmentBlock() first to create an environment that is appropriate for that specific user and then pass that pointer to CreateProcessAsUser(). Otherwise, the spawned process will use your service's environment instead. This step is optional, depending on the particular needs of the spawned app.

like image 105
Remy Lebeau Avatar answered Oct 01 '22 23:10

Remy Lebeau


The code is correct....make sure that you include "wtsapi.h" header file and this "#pragma comment(lib, "WtsApi32.lib")",because it is important to load the dll,else you will get linking error(error LNK2019: unresolved external symbol). And also duplicating token is not necessary...and privileges setting is not required because when the process is called from the service in your user account..the default session id will be 0 i.e,. SYSTEM account..which has all the privileges which a user can have...

my code works properly when process is called from a service:

   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);

for clear idea about service refer here .Download the source code and the code is perfect. Just insert this piece of code in the 'ServiceWorkerThread' function.this piece of code is for opening an .exe (Eg.File Explorer,Notepad,etc.,) file from your service.The .exe file will open when you start your service from services.msc( windows application).

like image 39
anjlairaj Avatar answered Oct 01 '22 23:10

anjlairaj