The first thing you should do about it is that, don't do it. There are many limitations, bad implications and restrictions involved into it.
In case, you are in a state that you cannot avoid launching an interactive process from Windows Service then you might want to read this article.
Launching an interactive application from a Windows service is different in Windows Vista
To maintain isolation and provide rich user experience Windows uses sessions. When a user logs in a Terminal Services enabled computer a new session is created with each logon. Each session is associated with Logon LUID.
The interactive window station, Winsta0, is the only window station that can display a user interface or receive user input.
In Windows Vista and later Windows service run in a separate session.
In all the interactive sessions Winsta0 is created by default. A Window station acts as container to the desktop. Each session has at least one Window Station (default windows station "winsta0") and a Window station have three desktops
Name of the window station is case in-sensitive however name of desktops are case sensitive.
Windows station and desktop is provided to secure UI and GDI objects. UI and GDI objects are associated with Windows station and desktops and Window station and desktops have DACLs associated with them. They are considered as securable objects in Windows.
Effect of session on launching a process
A process is associated with the access token of a user. This access token is compared against securable objects DACLs when the process tries to access it. There are 14 type of securable objects present in Windows for example Files, folders and registry keys. For a process it is must to have an access token which is derived from a user under whose context the process is launched.
There are three most effective ways to launch a process using Windows APIs.
a. Access token is derived from the parent. The process is launched in the session of the parent and access the window station desktop of the parent by default.
b. You can change the desktop accessed by this process by populating lpDesktop member of STARTUPINFO structure. Make sure that parent process has appropriate access to that window station and desktop and they are in the same session of the parent process.
a. You need a primary access token to pass as the first parameter of this API.
b. LogonUser API can be used to get the primary access token of a user. LogonUser expects credentials of the user in plain text.
3) CreateProcessWithLogonW and CreateProcessWithTokenW
a. The CreateProcessWithLogonW and CreateProcessWithTokenW functions are similar to the CreateProcessAsUser function, except that the caller does not need to call the LogonUser function to authenticate the user and get a token. CreateProcessWithLogonW cannot be called from Local System context. Therefore if you have a service running in Local System context you cannot use CreateProcessWithLogonW.
Session information is kept in User's access token. A process gets a session depending on the token used to launch it. The session information of an access token can be changed.
Note: A process cannot change the session id of its own token.
Both 2nd and 3rd method requires a user to login in the local system. The newly created process's access token will be checked against the DACLs of Windows Station and desktop while it will try to attach itself to them. Also, as mentioned above Windows create a session on each logon. While launching the process by using method 2 and 3 either you would need to create and specify windows station and desktop for lunching process or Windows by default will create a Window station and desktop
For 1st method, process is launched in the same session of parent process with same access token. By default this child process will have access to the Window station and desktop of the parent. If you are changing these two you would need to make sure that parent process have access to the new window station and desktop.
Process to launch the interactive process
Until Windows Server 2003 the services and the first logged on user used to run in the same session, Session 0. Therefore, the Window station and desktop of the first logged on user was shared by services running in the same session. Therefore services were able to obtain the handle of the user's desktop and modify its DACL.
Up to and including Windows 2003 you check the check box saying "Interact with desktop" in Properties dialog box of the Service and you can launch the process interactively.
With security enhancement in Windows Vista and later versions of Windows, services run in a separate session called Session 0. Now, in Session 0 a process can only get handle of Window station and desktop associated with session 0.
Let's categorize the process in two by two cases; we will discuss each of them. These cases are listed in decreasing order of feasibility.
1) Your service is running in LocalSystem account context.
A. You are willing to launch the application as currently interactively logged on user.
B. You are willing to launch the application as some user other that interactively logged on user.
2) Your service is running in any account other than LocalSystem account.
You are willing to launch the application as some user other that interactively logged on user.
1.A. ) Your service is running in LocalSystem account and you are willing to launch the application as currently interactively logged on user.
This is how you do it:
Get the user token by calling
WTSQueryUserToken (WTSGetActiveConsoleSessionId (), &hToken) ;
Use this token in CreateProcessAsUser.
This token will have the right session ID of the interactive user. You will not have to change anything you are good to go.
1.B. ) Your service is running in LocalSystem account and you are willing to launch the application as some user other than interactively logged on user.
For an interactive application we need two essential things
1) An access token with right session ID
a. You need an access token of that user. You would need to call LogonUser to get the primary token. This call will create a new session and associate the session ID with token. Now, your token does not have the right Session ID. You can change it by calling SetTokenInformation. After this call you have the right session ID in the token.
2) You need a Window station and desktop to associate with your application.
Newly created application will not have access right to the interactive session’s window station and desktop. This application cannot associate itself with interactive desktop.
Therefore, even though we have good token the launched process will not survive. It will be killed by Windows desktop manager because the desktop it is trying to access is not accessible to it.
You can change the permissions of the Window station and desktop. The only way to change the permission is to get the handle of the desktop. The process cannot obtain the handle of the desktop of session other than their session. This implies that service which is running in session 0 cannot obtain the handle of the desktop in the interactive session.
To workaround this you would need to perform following steps
a) Launch a hidden temporary process as explained in step 1.A.
I. Get the user token by calling WTSQueryUserToken (WTSGetActiveConsoleSessionId (), &hToken) ;
II. Use this token in CreateProcessAsUser to launch the temporary process.
Call CreateProcessWithLogonW from the temporary process to create the target application. CreateProcessWithLogonW saves you from explicitly modifying the DACL of the interactive desktop. However, you need the credentials of the target user to provide in this API.
2.A.) Your service is running in any other account and you are willing to launch the application as currently interactively logged on user.
You cannot call WTSQueryUserToken. This API can only be called from services running under LocalSystem account.
There is no definite way to do this because there is no legitimate way to obtain the logged on user's token with right Session ID. Here is what you should never, never and never do.
You would need to steal the access token of a process running on interactive user’s desktop. The best process to steal the access token would be Explorer provided only one interactive user is logged in.
a) Enumerate the process to get the handle of the Explorer.
b) OpenProcessToken should give you the access token. Note : The account under which your service is running must have appropriate privileges to call this API and get process token.
c) Once you have the token call CreateProcessAsUser with this token. This token already have the right session Id.
2.B.) Your service is running in any account other than LocalSystem and you are willing to launch the application as some user other that interactively logged on user.
Same text goes again:: Here is what you should never, never and never do.
You would again need follow the technique we followed in 1.B . Launch a hidden application by performing
2.A.a, 2.A.b and 2.A.c
Once hidden application is launched follow 1.B.a- 1.B.f to launch the target application.
When interactive user log off
I have seen people saying that my application is running in a user's context which is not same as interactively logged in user, should my application be running when the interactive user logs off ?
When the user logs off, the system destroys the session associated with that user. Therefore the Windows stations and the desktops associated with that user will be destroyed. Even if your application is running in a different user's context it is attached to the interactive user's desktop.
All the applications running on the interactive desktop will be terminated. So is yours.
However, you can expect a WM_QUERYENDSESSION and WM_ENDSISSION for your application.
Where is the code example ??
You got to write your own!!!
Remaining case: Service is Local System, want to spawn interactive process as Local System.
Easy. Steal the token from winlogon.exe in the right session.
BTW, the temporary process method in 2.A method is not secure. Instead, steal the winlogon token for your temporary process.
You frickin rock. Thanks for the help!
Great, could you send me the example?
This is a very good article.
But only theorie.
The problem is writing the code and passing the correct parameters to the APIs.
The usability of the article in the real life is low.
It happened to me that after calling LoadUserProfile() the desktop falshed for 2 seconds
and afterwards the screen resolution had changed!
As you see it is easy to write a theory, but it is really useless without any code!
A very importent point is not treated in the article:
What do I have to do that the created process runs elevated ?
What is the difference between CreateProcessAsUser and CreateProcessWithToken ?
I cant see any.
And why is it so bad to copy the token from Explorer.exe ?
You never explain that.
It would be very nice if you update your article.
I found a lot of code in the internet but it is useless because it was written before Vista.
ElmueSoft - you might get some use from the method I used here: cjwdev.wordpress.com/.../vb-net-start-process-in-console-session-from-windows-service-on-windows-7
It is VB.NET code but as it is so short and simple it should be pretty easy to see what it is doing even if you don't know VB.NET. Tested on Windows 7 and it works fine.
thank you for sharing amazing expertise! im wondering if you could shed some more light on the elevation question:
I have the SYSTEM service spawning a non-elevated process, which is based on explorer.exe token. but how would you run process elevated (as administrator)? because using winlogon.exe token gets the process killed after about 10 minutes...
is the temporary process approach relevant here? does winlogon.exe token not provide the correct session id and desktop already - if it does, why is the process terminated? or is the winlogon desktop incorrect and that is why dwm kills my process?
or should i try to adjust TokenInformation for explorer.exe to make it elevated?
more details here - stackoverflow.com/.../create-interactive-elevated-process-from-windows-service-and-show-to-logged-on-u
You should also call ImpersonateLoggedOnUser API, otherwise CreateProcessAsUser may not be able to open process EXE file (e.g. if it's placed on a network share).