How to detect Windows is locked? - winapi

How to detect Windows is locked (Win+L or locked by screensaver)? Is it possible?

There is no documented way to check if the session is locked. There is however a way to detect that the session is getting locked. Use WTSRegisterSessionNotification(), you'll get a WM_WTSSESSION_CHANGE message with the WTS_SESSION_LOCK value when the workstation is being locked.
If you are planning to do this from a service then be sure to google "session 0 isolation" to find out why that doesn't work.

WTSRegisterSessionNotification() will require the elevated privileges on Windows 10/11.
Call WTSQuerySessionInformation with WTS_INFO_CLASS set to WTSSessionInfoEx.
struct _WTSINFOEX_LEVEL1_A has a member, LONG SessionFlags, which indicates whether the calling session is locked or not.

Related

Make WTSQuerySessionInformation and/or %CLIENTNAME% work during loginskript?

For users logging in to a Windows 2008 R2 Remote Desktop Server, I need to know the name of the client PC. As I learned the hard way, it seems that WTSQuerySessionInformation() does not work properly during the execution of login skripts. I've seen it return the name the user logged in from the day before, for example. Remarkably, the function itself never failed (as in: return false), it merely "lied". A quick cross-check revealed that the environment variable %CLIENTNAME% is not set during the execution of login skripts either (though I only observed it empty, not wrong in my handful of tests).
I found this info about problems during logoff cause the (lengthy) logoff may take place with the session closing already handled.
But the way I understand it, the session is established before the login process even begins, so it seems strange that the session information is not yet available. Apparently I am wrong, so my question is:
Q: How can I find out the name of the client PC (or at least its IP) during login?
Alternatively: Q: How can I execute a program in the user context as soon as his SessionInformation gets set to its correct value?
(An answer to the alternative question might even be more interesting as it could also deal with the case of a user "stealing" his active session from another client)

How do I get the caller SID in kernel mode hook? (windows)

I'm developing a driver that hooks some functions in windows (hardening driver that will block some actions). The problem is, that I want to log which was the user who attempted to run those actions.
For example, I've put an hook on ZwSetValueKey in order to filter registry writing.
The hook works perfectly, But I don't know how to get the caller SID. I've found out that I can determine whether the mode of the caller(i.e. user mode or kernel mode) using ExGetPreviousMode. But I'm not really sure how to determine the SID if the caller was in user mode.
Thanks.
If this were user mode, you'd start with GetCurrentProcess or GetCurrentThread, and then call GetProcessToken or GetThreadToken. This would get you an access token, from which the SID can be extracted directly. In kernel model, there's PsGetCurrentProcess and ZwOpenProcessTokenEx, and the like for threads.
Here's the equivalent question for user space: How to get Calling-Process Windows User Access Token.
I haven't tested this, but I hope it gets you started.
VOID SeCaptureSubjectContext(Out PSECURITY_SUBJECT_CONTEXT) is probably the kernel-mode API you're looking for, at least if you have ntifs.h available.

How can I autostart a UI-application under windows that a non-admin user cannot close?

I have developed a C# Windows Forms application that runs in the background as a systray icon and does some stuff when it's clicked. It is supposed to launch when Windows starts and run continously, and a normal user without administrator rights shall not be allowed to shut it down.
What is the best way to do this? I initially intended to run it on the LocalSystem account through Task Scheduler, but then I learned (the hard way) about Session 0 isolation (i.e. the application will run but its UI elements do not show). When I run it with the logged in user, even if it runs elevated, the user can still shut it down through task manager (without requiring elevation).
Is there any way to give a process from LocalSystem access to the UI? I have a winlogon and a csrss process from LocalSystem running in session 1, so I guess it can be done, I just don't know how. Or is there maybe an easier way to disallow users to shut down a process of their own through the task manager? The only other option I can think of is to create an extra Windows Service that keeps polling if the app is running, and immediately launches it again if someone kills it - but that seems incredibly clumsy (also, I want it to stay dead when it crashed by itself, to avoid a single bug causing infinite loops of process creation).
Deponds on why they can't shut it down.
The natural way to go would to have created a service, started by a high priv account, and then had the desktop app just show what it was doing.
If there's something that they should see, but don't becasue they aren't running the service monitor app. (and acknowledge message back to the service), send them an email, send their boss an email, send yourself one and then go shout at them.....
Be a lot easier than trying to get the lid back on this tin of worms.
A nice way to make sure the desktop app is ruuning, would be simply to schedule it to run every X, but drop out immediately if it already is or the somethingwenthorriblywrong flkag is set.
Not worth writing a service to check if it's still there, as they could kill that as well, unless you want to make that a service they can't kill. :(
You are trying to be too draconian with this. Add some sort of auditing so you can see it dies or was shutdown, monitor that and deal with any adverse reports. It's a heck of a lot easier, and gives manage something to do...
You can run an administrative process in the user's logon session. One method would be to for a master process (a system service) to duplicate its own token, use SetTokenInformation to change the session associated with the token, and then call CreateProcessAsUser. The lpStartupInfo parameter can be used to associate the process with a particular window station and desktop. It may be necessary to explicitly change the permissions on the window station and desktop first.
HOWEVER, this is a bad idea from a security standpoint, because GUI applications are vulnerable to malicious messages sent from other processes on the same desktop ("shatter attacks").
It would be safer to run the process in the user's own context but apply an ACL to it. This can be done using the lpProcessAttributes parameter to CreateProcess or CreateProcessAsUser, or with the SetSecurityInfo function. I've never tried this, but it should in theory prevent the user from using task manager to close the process.
If you are creating the process from the user's context, then the user will be the owner, so a sufficiently knowledgeable person could change the permissions back in order to terminate the process. If you need to block this hole, using CreateProcessAsUser from a privileged master process (the token can be duplicated from one of the existing processes in the user's session) should (again, in theory) mean that the user is not the process owner.
Another potential issue: if you listen for messages indicating that the user is logging out (in order to exit) such a message could be faked. If you don't then you need to find some other way of exiting when the user logs out. You may need to experiment here, I'm not sure how the system will react.

Blocking mouse input from a service in Vista

I maintain a variety of managed userlabs on a university campus. These machines all currently run Windows XP and we have a windows service that is used to "lock" a machine by blocking any keyboard or mouse input. The locking happens during our scripted OS installation so that users aren't able to accidentally halt or break the process. It is also used to prevent users from logging into machines until they are checked out at the front desk of a given lab. Ctrl+Alt+Del is blocked via a keyboard filter driver and the rest of the keys and mouse are currently blocked using the BlockInput() function from user32.dll.
In XP, the service runs as Local System and the checkbox for "Allow service to interact with desktop" must be enabled from the BlockInput() call to succeed. Under Vista, this no longer works I'm guessing because of the Session 0 isolation changes. The call succeeds, but input is not actually blocked.
The keyboard filter driver still works just fine and we can use that to block the whole keyboard instead of just Ctrl+Alt+Del. But I'm at a loss as to how we're going to block the mouse now. I'm not even entirely sure the Session 0 isolation is to blame.
Can anyone recommend a fix or a workaround to allow us to block mouse input from a service in Vista and beyond? I've looked for alternative win32 API's without luck. Assuming Session 0 isolation is to blame, is there a legitimate way to call the function from Session 1 or would that sort of defeat the purpose of the isolation? Will I have to rely on an elevated companion exe that runs on user login and communicates with the service?
From a service, you can use WTSEnumerateSessions to get all logged on user sessions, WTSQueryUserToken to get the logged on user's token, and then use CreateProcessAsUser with that token to get code running on the user's desktop. This has the disadvantage that the code will run as the logged on user. Since all input is disabled, this is probably safe enough and is at least as safe as the existing XP solution.]
EDIT: BlockInput requires a high mandatory integrity level. You could try adding this group to the token, but then you have code with higher privileges running on the desktop and potentially open to attacks.
An alternative might be to use your service just to disable all HID class devices on the machine.

CreateProcessAsUser vs ShellExecute

I need to ShellExecute something as another user, currently I start a helper process with CreateProcessAsUser that calls ShellExecute, but that seems like too much of a hack (Wrong parent process etc.) Is there a better way to do this?
#PabloG: ImpersonateLoggedOnUser does not work:
HANDLE hTok;
VERIFY(LogonUser("otheruser",0,"password",LOGON32_LOGON_INTERACTIVE,LOGON32_PROVIDER_DEFAULT,&hTok));
VERIFY(ImpersonateLoggedOnUser(hTok));
ShellExecute(0,0,"calc.exe",0,0,SW_SHOW);
RevertToSelf();
CloseHandle(hTok);
will just start calc as the logged in user, not "otheruser"
#1800 INFORMATION: CreateProcess/CreateProcessAsUser is not the same as ShellExecute, with UAC on Vista, CreateProcess is useless when you don't have control over what program the user is executing (CreateProcess will return with a error if you give it a exe file with a manifest marked as requireAdmin)
#Brian R. Bondy: I already know this info (And don't get me wrong, its good stuff), but it is off topic (IMHO) I am asking for a ShellExecuteAsUser, not about starting processes as another user, I already know how to do that.
The solution really depends on what your needs are, and can be pretty complex (Thanks fully to Windows Vista). This is probably going to be beyond your need, but this will help others that find this page via search.
If you do not need the process to run with a GUI and you do not require elevation
If the user you want to run as is already logged into a session
If you need to run the process with a GUI, and the user may, or may not be logged in
If you need to run the process with elevation
Regarding 1:
In windows Vista there exists something called session 0 isolation. All services run as session 0 and you are not supposed to have a GUI in session 0. The first logged on user is logged into session 1. In previous versions of windows (pre Vista), the first logged on user was also ran fully in session 0.
You can run several different processes with different usernames in the same session. You can find a good document about session 0 isolation here.
Since we're dealing with option 1), you don't need a GUI. Therefore you can start your process in session 0.
You'll want a call sequence something like this:
LogonUser, ExpandEnvironmentStringsForUser, GetLogonSID, LoadUserProfile, CreateEnvironmentBlock, CreateProcessAsUser.
Example code for this can be found via any search engine, or via Google code search
Regarding 2: If the user you'd like to run the process as is already logged in, you can simply use: WTSEnumerateSessions, and WTSQuerySessionInformation to get the session ID, and then WTSQueryUserToken to get the user token. From there you can just use the user token in the CreateProcessAsUser Win32 API.
This is a great method because you don't even need to login as the user nor know the user's username/password. I believe this is only possible via a service though running as local system account.
You can get the current session via WTSGetActiveConsoleSessionId.
Regarding 3:
You would follow the same steps as #1, but in addition you would use the STARTUPINFO's lpDesktop field. Set this to winsta0\Default. You will also need to try to use the OpenDesktop Win32 API and if this fails you can CreateDesktop. Before using the station and desktop handles you should use SetSecurityInfo on each of them with SE_WINDOW_OBJECT, and GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION.
If the user in question later tries to login, he will actually see the running process.
Regarding 4:
This can be done as well, but it requires you to already be running an elevated process. A service running as local system account does run as elevated. I could also only get it to work by having an authenticode signed process that I wanted to start. The process you want to start also must have a manifest file associated with it with the requestedExecutionLevel level="requireAdministrator"
Other notes:
You can set a token's session via SetTokenInformation and TokenSessionId
You cannot change the session ID of an already running process.
This whole process would be drastically more simple if Vista was not in the equation.
If you need ShellExecute semantics you can feed following:
C:\windwos\system32\cmd.exe /k" start <your_target_to_be_ShellExecuted>"
to CreateProcessAsUser and you are done.
You can wrap the ShellExecute between ImpersonateLoggedOnUser / RevertToSelf
links:
ImpersonateLoggedOnUser: http://msdn.microsoft.com/en-us/library/aa378612(VS.85).aspx
RevertToSelf: http://msdn.microsoft.com/en-us/library/aa379317.aspx
sorry, cannot hyperlink URLs with "()"
Why don't you just do CreateProcessAsUser specifying the process you want to run?
You may also be able to use SHCreateProcessAsUserW.

Resources