3

I've a simple question here that is driving me crazy.

I have a windows service in C# which should work on XP, Vista and 7 and be able to enumerate windows of the current user's desktop (if any) for monitoring purposes.

So far :

I have used EnumDesktopWindows passing IntPtr.Zero as the hdesktop parameter because I don't have the handle to user's desktop which results only in enumerating a handful of windows that exist in special desktop which allocated for services (Session0\Winsta0)

I tried EnumWindows, same results as above!

I tried to get Desktop of a known process using GetThreadDesktop API, passing id of one of explorer.exe's threads but it returns 0, so I can't get it's desktop or any other's.

I tried to get input desktop using OpenInputDesktop which apparently returns the desktop inside session0 not desktop of user.

What can I do?!

If you're curious, I'm writing a kiosk application which needs to monitor all windows and prevent dangerous ones like task manager, Internet Options, Cmd, and in general anything that a user should not open.

Any suggestions are welcome. :)

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
Arashv
  • 305
  • 4
  • 15
  • 3
    You've got the wrong solution to the problem. Write the kiosk properly. Don't give the user free rein to have multiple logins and sessions. – David Heffernan Mar 06 '14 at 19:15
  • Why don't you enumerate the processes instead of the windows? `Process.GetProcesses();`? – thepirat000 Mar 06 '14 at 23:15
  • @DavidHeffernan What is multiple logins and sessions have to do with this problem? This scenario involves only one user session, the other session is for services, which is the default behaviour of Vista+! – Arashv Mar 07 '14 at 08:49
  • @thepirat000 Because for example internet explorer is allowed but changing internet options (a specific dialog) is not allowed, so I cannot decide based on just process whether it's allowed or not. – Arashv Mar 07 '14 at 08:50
  • That's two sessions. Run your code in the user session. Run a proper kiosk and the user can't do any of things you worry about and there's no need for the code you discuss here. – David Heffernan Mar 07 '14 at 08:51
  • @DavidHeffernan What do you mean exactly by "Run a proper kiosk" Dear David? – Arashv Mar 07 '14 at 12:39
  • 1
    I mean don't run explorer as your shell. Run your own program that does not allow the user latitude to create new processes. – David Heffernan Mar 07 '14 at 13:05
  • If you block the user from launching Task Manager, and prevent them from running unapproved processes, then there shouldn't be any problem with your monitoring process running on the same desktop as the user, or even in the user's own security context; they won't have any way to attack it. You probably don't need a service at all, but if you wanted you could have one to monitor the state of the main monitoring process. For example, the service could forcibly reboot the machine if the main monitoring process stopped responding. – Harry Johnston Mar 08 '14 at 00:09
  • @DavidHeffernan from what I see you have a good experience in this field, so, I'm gonna ask the question, which always prevented me from doing a shell replacement : when you replace the shell, what happens to the things that should be loaded at startup? I mean I've seen a ton of various (user made) dlls attached to explorer.exe, what happens to them? Who should load them? – Arashv Mar 08 '14 at 06:21
  • Give me some examples? But as I see it your shell is basic. It loads what it needs. – David Heffernan Mar 08 '14 at 07:41
  • @DavidHeffernan for example there are these processes which are loaded under explorer.exe (using prcess explorer) : GrooveMonitor, ismagnet, VCDDaemon, avp, googleupdate, IDMan, etc. and when you look at the threads there are ntdll.dll, IDMShellExt.dll, fsxsst.dll, msiltcfg.dll, UIAutomationCore.dll, WLanMM.dll, etc. on my machine – Arashv Mar 08 '14 at 11:15
  • You are writing a kiosk. You want none of that. – David Heffernan Mar 08 '14 at 11:33

5 Answers5

1

You cannot, without exception, enumerate windows in a another session. You can, on the other hand, create a process in another session if you have the "Act as part of the operating system" (SeTcbPrivilege) Privilege.

See Launching a process in user’s session from a service for how that can be done.

You can end up with two processes, a controller which runs as an NT Service, and the agent which runs in the user's session. The two processes can communicate via a named pipe, with the controller restarting the agent if killed by the user.

You should also be using group policy or other configuration to lock down the client to prevent the other windows from opening in the first place, however. Specifically, Software Restriction Policies will allow you to prevent a non-whitelisted executable from ever running.

If you are using Windows 8.1, you can also use the newly introduced Kiosk Mode.

Community
  • 1
  • 1
Mitch
  • 21,223
  • 6
  • 63
  • 86
  • Infortunately I'm not using Windows 8.1 and my solution should work on XP, Vista and Win 7. But let's take a look back at what you said. I thought of that too, but is it really a clean way to have 2 separate processes to do this? Is it the best way? Can't it be done from the service itself under any circumstances? – Arashv Mar 07 '14 at 08:57
  • I meant *Unfortunately, typo! :) – Arashv Mar 07 '14 at 12:38
  • 1
    "You cannot, *without exception*, enumerate windows in a another session". A process in the user session is how Win32 (`csrss`), WinRT (`RuntimeBroker`), Driver UI's and every printer on the planet run in multiple sessions. Same with RDS's clipboard manager (`rdpclip`) and RemoteApp's `rdpshell`/`rdpinit` which are explained here https://blogs.technet.com/b/askperf/archive/2008/02/22/ws2008-terminal-services-remoteapps.aspx – Mitch Mar 07 '14 at 15:37
0

Try the EnumDesktops function. Then open the desktops using OpenDesktop function.
See also: Sample class, SO question (VB.NET)

Community
  • 1
  • 1
Dmitry
  • 13,797
  • 6
  • 32
  • 48
  • The problem with `EnumDesktops` is that it needs a hWinSta which I don't have and I think you cant open windows station from another session, If you know a way, please advise. – Arashv Mar 06 '14 at 18:52
  • Maybe the [WTSEnumerateSessions](http://msdn.microsoft.com/en-us/library/aa383833%28VS.85%29.aspx) function will be helpful. See also the similar [question](http://stackoverflow.com/questions/308135/how-can-i-enumerate-the-open-windows-enumwindows-of-another-user-session). – Dmitry Mar 06 '14 at 19:05
  • I've seen it, it just gives you session ids which are worthless for this matter. If I only could launch a dll (not a process) in user's session everything would be fine – Arashv Mar 06 '14 at 19:13
  • @Arashv, why do you think a dll would be fine, when a process is not? – Mitch Mar 07 '14 at 15:48
  • @Mitch you know Managing a separate process with different memory space and separate execution flow is annoying in this case. Consider when I want to periodically check for open windows while black-list / white-list changes over time; Should I use IPC to transfer a relatively long black-list/white-list? But when having a dll which runs in that session and desktop, there is no need to worry about these kinds of things, right? – Arashv Mar 08 '14 at 06:11
  • @Arashv, a DLL is a static block of code on disk - just a container; it must be loaded into a process and would not avoid IPC. With respect to IPC, WCF and .Net Remoting, even COM and good ol' RPC make this easy. Since it is in memory, and not in a tight loop, any method you choose to communicate should not place undue burden on even the flimsiest hardware. Keep in mind that the functions you are calling are doing IPC to `csrss` or calling to kernel mode no matter how you call them. – Mitch Mar 08 '14 at 21:56
0

Not sure if this is helpful, but it sounds like you're trying to do this from a Windows Service which runs under Session 0. Starting with Windows 7, session 0 was patch to prevent services from interacting with the desktop without special permission. I think on Windows Server 2008, its not allowed at all.

Search for Session 0 security issues if your trying to create a service. Here's a link on the topic: http://blogs.windows.com/windows/archive/b/developers/archive/2009/10/01/session-0-isolation.aspx

  • Yeah I know about session 0 isolation and I'm already able to launch processes inside user session but in order to make sure that as long as the service is up, everything will be under control I don't want to launch a new process – Arashv Mar 06 '14 at 19:15
  • @Arashv: you don't have any choice. The only way to enumerate windows in a given session is to have your code run in that session. (You could however create your process in a separate window station and give that window station permissions that don't allow the interactive user to access it.) – Harry Johnston Mar 06 '14 at 21:39
  • @HarryJohnston how is it possible harry? How can you prevent interactive user from accessing the newly created session? – Arashv Mar 07 '14 at 08:53
  • @Arashv: Window stations have ACLs. Look up CreateWindowStation for more details. However, in retrospect I don't think it will help - looks like the functions for enumerating windows only work for the current desktop. – Harry Johnston Mar 08 '14 at 00:04
0

On Vista and above, you can't do that - services run in session 0, and you can't get window stations or desktops from other sessions - in particular:

When a window station is created, it is associated with the calling process and assigned to the current session.

So, in order to do what you are asking, you would need to enumerate the windowstations and desktops for a particular session.

However, while you can get the interactive session ID via WTSGetActiveConsoleSessionId, you can't quite get there, as EnumWindowStations doesn't take a session ID, and EnumDesktops requires a valid window station handle.

Eric Brown
  • 13,774
  • 7
  • 30
  • 71
  • Pretty much. The windowstation/desktop data for other sessions isn't really available, even from the kernel. – Eric Brown Mar 06 '14 at 21:36
-1

you could do that by starting the communication from the service process.

the code that creates your service has to broadcast some system wide event, mutex etc.

you can do something like:

HANDLE hToken = NULL;
if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
{
    printf("OpenProcessToken() error %u\n", GetLastError());
    return false;
}
if(!SetPrivilege(hToken, "SeTcbPrivilege", TRUE))
    return false;


SECURITY_ATTRIBUTES sa;
char *sdd = "D:"
        "(D;OICI;GA;;;BG)" //Deny guests
        "(D;OICI;GA;;;AN)" //Deny anonymous
        "(A;OICI;GRGWGX;;;AU)" //Allow read, write and execute for Users
        "(A;OICI;GA;;;BA)"; //Allow all for Administrators

ConvertStringSecurityDescriptorToSecurityDescriptor(sdd, SDDL_REVISION_1, &sa->lpSecurityDescriptor, NULL);

after you get this security descriptor use it on an named event or mutex. this way that particular kernel object can be accessed by processes that don't run on session 0.