3
votes

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. :)

5
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
Why don't you enumerate the processes instead of the windows? Process.GetProcesses();?thepirat000
@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
@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
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

5 Answers

1
votes

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.

0
votes

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

0
votes

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

0
votes

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.

-1
votes

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.