Windows Vista makes great changes to quick User Switching, user account permissions, and the session space running by the service program, so that some programs that can work normally no longer can work normally, we have to make some improvements to keep up with Vista. Our software installs a system service in the Windows NT/2000/XP/Vista system, which is responsible for starting our main program with system permissions. After the main program is started, an icon will be added to the system tray. Click this icon to bring up the control menu. Through this menu, you can also activate the configuration program preference dialog box. In Windows NT/2000/XP, all our programs work normally. Oh no, when XP has the quick user switching function, our problem has already occurred. After XP is started, we log on with user A. Our icon appears on the system tray and everything works normally. However, when we use Quick User Switching, after switching to user B (User A is also logged on and has not logged off), although user B is already a local console session (the session attribute is console) however, our icons can no longer appear, and there is no way to talk about natural menus and dialogs. Our program is related to the desktop of the local console, which is undoubtedly a defect. Let's take a look at what happened on the Vista platform. After the system is started, log on to user A. Our icon does not appear. Check the process list in the Process Manager and find that our program has been started, when we check our services from the remote end and find that our services are working properly and try to remotely log on to our services, Vista will pop up a message box in the local console, prompting that there is an interactive service message, check whether to view the message. Click View now to switch to another desktop. Then we began to analyze the cause of this situation. In Windows NT/2000, both the system service process and the user with interactive logon on the local console run in session0, and the user desktop runs in winsta0 window station by default, therefore, when our program is started by a service program, it is still in the same session as the local user, even if a dialog box cannot be displayed or the system tray icon cannot be added in some cases, you only need to modify the process desktop to winsta0/default (refer to openinputdesktop in msdn, setthreaddesktop and other API descriptions ). XP brings us Fast User Switching and the software architecture problems we adopt emerge. When we quickly switch to user B, user A is still in the Session (session0), while user B is in the newly started SESSION (session1 or another ), in this case, the Service Program and the local console program are not in the same session. openinputdesktop, setthreaddesktop, and other APIs only work in this session. User A does not exit, session0 still exists but is already in the disconnected state. When the Session of the process is in the disconnected state, calling openinputdesktop will return an error "invalid API ". The session to which processes and threads belong is determined by the tokensessionid in their token structure (see the description of settokeninformation and token_information_class in msdn ), I tried to modify the tokensessionid of Running Processes and threads using the APIS provided by Microsoft, so as to modify the desktop environment. I haven't succeeded yet (maybe I can try to refer to Rootkit Technology, however, even if the modification is successful, we are not sure whether the modification can meet our needs ). Our processes cannot span the boundaries of sessions, and naturally cannot interact with the desktop in another session of the current activity. In Vista, how is it? In terms of security and other factors, Vista and all service programs are placed in session0, and session1 is created for the first interactive login user on the machine, after switching to user B quickly, it is session2. No one is in the same session with the service process, whether it is a user logging on to the local machine, the user after the quick switch, or the user logging on to the Remote Desktop, our program is still running in session0. Naturally, no user can see the tray icon. In fact, this icon can still appear. Session0 does not start the Explorer program as it is initialized in other user environments because it is not an interactive session, but we can start it manually, after starting explorer in session0, we still see our icons after the taskbar appears (the method for starting explorer is not discussed here). menus and dialogs can also be used. Since our program must run in session0, we have no way to throw our icons and dialogs to the user desktop of the next session. We can only find other methods. Microsoft does not advocate that our service programs provide direct GUI interaction with users, but they suggest using the C/S architecture, the Client/Server communicates with each other through socket, pipe, and rpc. In this way, you only need to place the entire client process in the user session to interact with the user, then, the configuration information and other content are transmitted to the server through the above channels, and the server can respond accordingly. It is not that difficult to separate the GUI, and then add an interface for communication through pipe in the previous direct call, so that the GUI (client) can be flexibly controlled. At first, I want to put the user interface program in startup (start) and automatically start with user login. In this way, when both user a and user B log on, there will be two user interface programs running, and our service only interacts with the currently active console login user, so this does not meet the needs. Next, let's take a look at how to determine which active session is and how to start our user interface program in this active session. Microsoft has provided us with a set of Windows Terminal Service APIs starting with XP/2003. These APIs all start with WTS (Please install msdn2005 for instructions ), there are more than one way to get the Active session, the simplest is to use it directly.
DWORD
Wtsgetactiveconsolesessionid (void );To obtain the Active session ID. To use these APIs in a program, you need the latest Platform SDK (if you are using Visual Studio 2005, it already has the relevant header files and library files that can be used directly ), if you are using VC ++ 6.0 and you do not have or intend to install the latest SDK, you can directly use loadlibrary () to load wtsapi32.dll and then use getprocaddress () obtain the addresses of related functions to call them. After obtaining the activity sessionid, we can use
Bool
Wtsqueryusertoken (
UlongSessionid
,
PhandlePhtoken
);To obtain the User Token in the current active session. With this token, we can create a new process in the Active session,
Bool
Createprocessasuser (
HandleHtoken
,
LpctstrLpapplicationname
,
LptstrLpcommandline
,
Lpsecurity_attributesLpprocessattributes
,
Lpsecurity_attributesLpthreadattributes
,
BoolBinherithandles
,
DWORDDwcreationflags
,
LpvoidLpenvironment
,
LpctstrLpcurrentdirectory
,
LpstartupinfoLpstartupinfo
,
Lpprocess_informationLpprocessinformation
);Let's take a look at our tokenization as the first example of api. you can try to run notepad.exe first. How is it? You can see the new process on the console desktop. Check the process list. The user name of the process is the user logged on to the console. However, we have another problem here. We need to collect some information about the users currently logged on to the local machine through mutual logon, and some operations require high permissions to complete, in Vista, even members of the administraotrs user group start the process with the users permission by default. Therefore, the newly created process only has the users permission and cannot complete some operations, of course, we can use the UI provided by Vista to ask users to escalate to administrator permissions, but some operations or even administrator tokens cannot be completed, and the user needs to confirm that the usability is greatly reduced, so I decided to start our user interaction program with the system permission in the Active session. Obviously, wtsqueryusertoken () is not easy to use. Previously, we mentioned that the session to which the process belongs is determined by the tokensessionid in the process token. Can we copy the token of the service process and modify the tokensessionid in it, so as to create a new process with system permissions on the user's desktop? The answer is yes. Here is the code for implementing this operation. In order to narrow down the space, I deleted the exception handling code.
Token = NULL; token = NULL; handlehthisprocess = getcurrentprocess (); openprocesstoken (hthisprocess, Token, & htokenthis); token (htokenthis, Token, null, securityidentification, tokenprimary, & htokendup ); attributes = attributes (); settokeninformation (htokendup, tokensessionid, & dwsessionid, sizeof (DWORD); startupinfosi; process_information PI; zeromemory (& Si, sizeof (startupinfo )); zeromemory (& Pi, sizeof (process_information); SI. CB = sizeof (startupinfo); SI. lpdesktop = "winsta0 // default"; lpvoidpenv = NULL; cursor = cursor | create_new_console; createenvironmentblock (& penv, htokendup, false); createprocessasuser (htokendup, null, (char *) "Notepad", null, null, false, dwcreationflag, penv, null, & Si, & PI ); |
Most of our work has been completed, and we still need to monitor the changes in the Active session, that is, user login, logout, and quick switch. The WTS series APIs and the APIs that provide us with these capabilities can be roughly implemented using the following methods: 1. set a timer and use wtsgetactiveconsolesessionid () to poll the Active Desktop ID. When a change is detected, let the previous instance of the user interaction program exit and create a new process in the new active session. 2. Use the wtsregistersessionnotification () function to register a window to receive the wtssession_notification message and determine the session changes. 3. use wtsenumeratesessions to enumerate all sessions, then judge the session status based on the State member in the returned wts_session_info structure, and find the Active session. select one of your other requirements and then make a response. This article only briefly describes the problems I encountered during the transfer to Windows Vista and my solutions. If you have any omissions or errors, please correct them, please enlighten me if you have good ideas and implementations. my mailbox is zhong.felix@gmail.com