Published Tuesday, December 02, 2008 5:43 AM by martin

Creating a process in another user session

In my last post, I talked about how a process' access token determines which user session it belongs to.  With that information, and if I have sufficient privilege, I can programmatically start a process in any running session.

In my scenario I have a highly-privileged service that wants to communicate with a lowly-privileged app running in each established session.  The machine I have in mind is shared, and there might be multiple sessions that are all logged-in.  How do I get the low-privilege process to run?  I could make it a startup app for all users, but I can't really trust that people won't change their startup apps, or that they might not simply kill the process.  I would prefer that my service is able to create the other process and just have it run inside whichever user session it chooses.

Here's some code that does what I need...

HANDLE hThisProcess = ::GetCurrentProcess();

HANDLE hProcessToken = INVALID_HANDLE_VALUE;

::OpenProcessToken( hThisProcess, TOKEN_ALL_ACCESS, &hProcessToken );

HANDLE hDupToken = INVALID_HANDLE_VALUE;

::DuplicateTokenEx( hProcessToken, MAXIMUM_ALLOWED, NULL, SecurityImpersonation, TokenPrimary, &hDupToken );

::SetTokenInformation(hDupToken, TokenSessionId, &sessionId, sizeof(DWORD));

 

STARTUPINFO si;

::ZeroMemory(&si, sizeof(STARTUPINFO));

si.cb = sizeof(STARTUPINFO);

PROCESS_INFORMATION pi;

::CreateProcessAsUser(hDupToken, executablePath, NULL, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);

 

::CloseHandle(hDupToken);

...and here's some explanation...

  • Use OpenProcessToken to get the access token for the running process (this in my highly-privileged service).
  • Duplicate that token with DuplicateTokenEx, because I want to modify the copy.
  • Use SetTokenInformation to modify the Session ID in the copied token.
  • Call CreateProcessAsUser to start my chosen app in the specified session.

Now, where am I going to get a session ID from?  There are a number of ways actually, but one option is to use the WTSEnumerateSessions function.

When I create a process in this way, the new process is a child of my highly-privileged service.  My service code has full control over that new process, and communicate with it in a variety of ways.  If the user in the target session doesn't have admin rights they won't be able to stop this process because I'm using my highly-privileged token (slightly modified) to launch it.

I'm sure you'll agree this is exactly the kind of operation that should require high privilege.  In my sample code above, the actual line that requires high privilege is that call to SetTokenInformation.  If you use that API to set the session ID, as I have, the calling process needs to have SeTcbPrivilege, which is otherwise known as "Act as part of the operating system".  So my service runs as LocalSystem.