Monday, March 1, 2010

Process Session

NTSTATUS: STATUS_JAM_SESSION

I think there are a fair number of people who are not aware of the concept of 'sessions' in Windows, and some are aware of it, but only that there is such a thing. We're going to go through the basics of it, and see how it might be relevant.

A session in Windows NT is basically a user login session. It is managed by the Session Manager Subsystem process (SMSS.EXE). This includes Terminal Services/Remote Desktop sessions. Windows NT servers allow multiple concurrent Terminal Services login sessions whereas the client versions Windows NT does not.

If I recall correctly, in the initial version of Windows XP, you could be logged in to Windows, and have a Remote Desktop session active with another user being logged in. So, you could actually have two simultaneous user sessions. Then it came to pass, in, I think it was SP2 that Terminal Services was changed such that you could not run a Remote Desktop session without having the current user logged off. I think the story was that the change was made because the way Terminal Services was working was in violation of the EULA which allowed
only one user of that copy of Windows XP at any one time.

Now, moving along, sessions weren't terribly relevant prior to Windows Vista with the exception of the case of Windows XP prior to the Service Pack with changed Terminal Services. This is because prior to Vista, you could not actually have more than one user logged in to Windows at the same time. You'd remember this because if you wanted to login as another user, you had to logout of your current session. So in that sense, it wasn't actually relevant for your apps to be concerned about which session it was running in.

However, since Vista, you could login multiple users in your machine which I really appreciated because you didn't have to lose your session. I hated having to have my session restarted and to get things back to the way they were, and it took an amount of time I wasn't interested in waiting on.

So now, in Vista and Windows 7, the session in which your process runs becomes relevant. There are two basic scenarios here:
  1. There should only be one instance of your app running in Windows
  2. There should be only one instance of your app running in each user session
In the first case, it is pretty straightforward. You would enumerate all processes and see if there is already in instance running.

In the second scenario, it is commonly the case where an instance of your app is required to deal with one (instance of a) user specifically. Therefore, if there was another user logged in, you needed another instance of your app to run in that user's context as well. In most cases, this would be a UI app. Also, it is an app where you only want only a single instance running (per user session). So in the old days before multiple concurrent user sessions, you would just check to see if the app is already running, and not start up a second process. If that was done now, you will end up with only one instance of the app running regardless of the number of user sessions.

So now we get to the gist of it, which is: ensuring only one instance of your app runs per user session.

To do this, we need to enumerate all instances of the app in question, and check the session in which it belongs to. As an aside, you can use Process Explorer from Sysinternals to look at the process tree to see all the session running, and their associated processes. And obviously, you are not able to access the processes that do not belong to your current session.

The Session ID of a process can be obtained from the Process Environment Block (PEB) Win32 structure. This one of those magic data structures that are largely undocumented, and in most instances, for good reason. Following is the struct from the Windows SDK version 7.0:

typedef struct _PEB {
BYTE Reserved1[2];
BYTE BeingDebugged;
BYTE Reserved2[1];
PVOID Reserved3[2];
PPEB_LDR_DATA Ldr;
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
BYTE Reserved4[104];
PVOID Reserved5[52];
PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine;
BYTE Reserved6[128];
PVOID Reserved7[1];
ULONG SessionId;
} PEB, *PPEB;

You can see the SessionId field in the struct, which is what we're interested in. Another field of potential interest is the Ldr field which contains a doubly-linked list of the modules loaded by the process.

The information of this struct is obtained via the Win32 API function:
NTSTATUS WINAPI NtQueryInformationProcess(
__in HANDLE ProcessHandle,
__in PROCESSINFOCLASS ProcessInformationClass,
__out PVOID ProcessInformation,
__in ULONG ProcessInformationLength,
__out_opt PULONG ReturnLength
);


Pass in
ProcessBasicInformation in the
ProcessInformationClass
parameter, and the function will return a pointer to a PEB structure.

So there you have it, the session in which the process is running.