9

I am seeking example code:

For a service calls CreateProcessAsUser() I want the process to run in the user's session, not session 0

thus far the created process is only running like a service in session 0

sylvanaar
  • 8,096
  • 37
  • 59
kevinwaite
  • 613
  • 1
  • 8
  • 20

2 Answers2

7

This was stripped from some old code that launched a console app from a service. It worked under NT4 but I haven't tested it with a modern version of Windows so can't guarantee it will work as it did on NT4.

EDIT: No, that's not going to work as-is. You need to add the code found here to create a desktop, set the SID, etc.

    if (!LogonUser(userId,
                   domain,
                   password,
                   LOGON32_LOGON_INTERACTIVE,
                   LOGON32_PROVIDER_DEFAULT,
                   &hUserToken))
    {
        return GetLastError();
    }

    if (!ImpersonateLoggedOnUser(hUserToken))
    {
        DWORD rc = GetLastError();
        CloseHandle(hUserToken);
        return rc;
    }

    STARTUPINFO             si;
    PROCESS_INFORMATION pi;

    memset(&si, 0, sizeof(si));
    memset(&pi, 0, sizeof(pi));

    si.cb = sizeof(si);

    rc = CreateProcessAsUser(hUserToken,                // user token
                           0,                           // app name
                           "foo.exe",                   // command line
                           0,                           // process attributes
                           0,                           // thread attributes
                           FALSE,                       // don't inherit handles
                           DETACHED_PROCESS,            // flags
                           0,                           // environment block
                           0,                           // current dir
                           &si,                         // startup info
                           &pi);                        // process info gets put here


    if (!rc)
    {
        DWORD rc = GetLastError();
        RevertToSelf();
        CloseHandle(hUserToken);
        return rc;
    }

    RevertToSelf();
    CloseHandle(hUserToken);

    return 0;
Carey Gregory
  • 6,836
  • 2
  • 26
  • 47
  • 2
    You do not need to create a new desktop. You specify an existing desktop in the `STARTUPINFO` structure, such as the user's interactive `WinSta0\Default` desktop (as shown in Microsoft's example). And you do not really need to mess around with SIDs, either. `CreateProcessAsUser()` in a service can work fine without them. I have been using it in services for years without any problem, and I do not manipulate SIDs for it. On the other hand, I do not use `LogonUser()` or `ImpersonateLoggedOnUser()`, I use `WTSQueryUserToken()` and `DuplicateTokenEx()` instead. – Remy Lebeau Mar 05 '14 at 22:49
  • 1
    Also, you should call `CreateEnvironmentBlock()` before calling `CreateProcessAsUser()` as well. – Remy Lebeau Mar 05 '14 at 22:51
  • @RemyLebeau I agree (somewhat). Perhaps you should write a new answer. It's a three-year old answer with no acceptance and no votes, up or down, so I'm reluctant to spend more time on it right now. – Carey Gregory Mar 06 '14 at 04:36
  • 1
    There's no need to duplicate the user token obtained via `WTSQueryUserToken`. It already returns you a primary token – klaus triendl Feb 15 '17 at 09:08
4

I know this is an ancient post but I happen to be working on this so here's some code that works for me.

Determine the session ID of the currently logged-on user

DWORD GetCurrentSessionId ()
{
    WTS_SESSION_INFO *pSessionInfo;
    DWORD n_sessions = 0;
    BOOL ok = WTSEnumerateSessions (WTS_CURRENT_SERVER, 0, 1, &pSessionInfo, &n_sessions);
    if (!ok)
        return 0;

    DWORD SessionId = 0;

    for (DWORD i = 0; i < n_sessions; ++i)
    {
        if (pSessionInfo [i].State == WTSActive)
        {
            SessionId = pSessionInfo [i].SessionId;
            break;
        }
    }

    WTSFreeMemory (pSessionInfo);
    return SessionId;
}

Launch process as the currently logged-on user

bool LaunchProcess (const char *process_path)
{
    DWORD SessionId = GetCurrentSessioId ();
    if (SessionId == 0)    // no-one logged in
        return false;

    HANDLE hToken;
    BOOL ok = WTSQueryUserToken (SessionId, &hToken);
    if (!ok)
        return false;

    void *environment = NULL;
    ok = CreateEnvironmentBlock (&environment, hToken, TRUE);

    if (!ok)
    {
        CloseHandle (hToken);
        return false;
    }

    STARTUPINFO si = { sizeof (si) } ;
    PROCESS_INFORMATION pi = { } ;
    si.lpDesktop = "winsta0\\default";

    // Do NOT want to inherit handles here
    DWORD dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT;
    ok = CreateProcessAsUser (hToken, process_path, NULL, NULL, NULL, FALSE,
        dwCreationFlags, environment, NULL, &si, &pi);

    DestroyEnvironmentBlock (environment);
    CloseHandle (hToken);

    if (!ok)
        return false;

    CloseHandle (pi.hThread);
    CloseHandle (pi.hProcess);
    return true;
}
Paul Sanders
  • 24,133
  • 4
  • 26
  • 48
  • I am facing an undefinde error for `WTSActive` and `WTS_CURRENT_SERVER` – Narendra Prasath Jul 03 '18 at 06:16
  • The documentation at [MSDN](https://learn.microsoft.com/en-gb/windows/desktop/api/wtsapi32/nf-wtsapi32-wtsenumeratesessionsa) always tells you what header file(s) you need to #include and what .libs you need to link against. Sorry that isn't in my post. – Paul Sanders Jul 03 '18 at 06:21
  • using Can I impersonate logged on user using `WTSQueryUserToken` token ? – Narendra Prasath Jul 03 '18 at 12:47
  • I believe that `ImpersonateLoggedOnUser` will accept such a token, yes. I suggest you give it a go. – Paul Sanders Jul 03 '18 at 13:59
  • Is it mandatory to set `si.lpDesktop` to `winsta0\\default`? – Louis Bernard May 14 '22 at 12:37
  • @LouisBernard IDK sorry. You'd have to experiment. – Paul Sanders May 14 '22 at 14:46
  • @PaulSanders Could you please let me know how to get the hToken from the security context or windowsIdentity object. Please help me to solve this issue/blocker. https://stackoverflow.com/questions/73070791/impersonating-user-in-remote-machine-and-creating-a-process-as-logged-in-user – Chandru Jul 22 '22 at 05:59
  • @ChandruT Sorry, I don't understand what you're asking me. That question is all about Java and I don't use that. – Paul Sanders Jul 22 '22 at 09:10