tags:

views:

473

answers:

6

I have an application that I wrote in .NET. It needs to remain running and have access the desktop that the UAC dialog windows open on and interact with that desktop using keyboard and mouse events.

It's sort of like a VNC program. Imagine you are running a VNC program and a UAC window pops up, you want your VNC program to still be able to control the desktop with the UAC window in it so that the user can move the mouse and click the OK button on the UAC dialog. Can anyone tell me how I would go about doing that?

Thanks

A: 

I would imagine that you probably need to be running as a Windows Service to be allowed to execute while UAC is active.

Jason Williams
Yes, but running as a service isn't enough.
Ray
+4  A: 

The problem is that the UAC prompt doesn't open on the current desktop, but rather in a completely new Secure Desktop, which suspends any currently open desktops.

This is intentional, as programs are not allowed to interact with this dialog.

There is one exception: "trusted SYSTEM processes can run on the Secure Desktop," according to this blog entry.

R. Bemrose
Yes, that is correct but it still is possible to get your program to run on the secure desktop when a UAC window opens. VNC programs do it, otherwise they'd be pretty useless.
Ray
Well, they wouldn't be totally useless. You can always turn off UAC, if it's your machine. GotoMyPC has apparently solved this problem, but I don't know how.
Robert Harvey
Turning off the UAC actually isn't a great option because it disables some other functionality of the OS when you do that.
Ray
There is one exception: Trusted processes running as the SYSTEM user can still interact with the Secure Desktop.
R. Bemrose
@Ray: It isn't possible for a "user mode" application to interact with the WinStation (Secure Desktop) displaying the UAC prompt. Otherwise, such user mode applications could interact with the UAC prompt and "Click" the button for you automagically, which would completely defeat the purpose of displaying the prompt in the first place.
Scott Dorman
As a service it should be able to. My application is signed with a certificate too
Ray
A: 

I believe everything you see on your screen during UAC prompt is a screenshot(except the dialog itself)

John Grey
It creates a new desktop and uses a screenshot of your desktop as the background, but it's still a fully functional desktop and other programs can run on it.
Ray
@Ray: It's not a "fully functional desktop" (in fact it's not even a desktop, it's a WinStation) and other programs can't run on it as it's isolated.
Scott Dorman
A: 

You can't do this. By default, when a UAC prompt appears, it is actually running in an isolated WinStation that is different from the one that runs "normal" applications. This WinStation is isolated specifically to prevent user applications from interacting with the UAC prompt. It looks like your desktop because the background of that WinStation is actually a static bitmap image of your desktop as it was just before the UAC prompt was displayed.

I suspect this might be a problem in the version of VNC you are running as I'm fairly certain the Remote Desktop provides the correct behavior and allows you the user to still interact with the UAC prompt through the remote desktop client. Keep in mind, when running any kind of remote client (remote desktop, VNC, etc.) the idea is taht the client should allow you to do anything you would normally be able to do if you were physically sitting at the keyboard.

Scott Dorman
It's possible. A lot of VNC programs are able to do it, including logmein.com.
Ray
A: 

I would suggest you start by reading the documentation. I would guess that maybe you could open the window station and attach your process to it, but I am not very familiar with this area of Windows.

Edit 1:

In Windows XP I was able to access the secure desktop ("winlogon") via OpenDesktop when running as SYSTEM; the ACL on the secure desktop allows access only to the SYSTEM account. After opening it I could enumerate the windows on it, though there were only a handful. Perhaps you could set a window hook and listen for creation of the specific dialog. I'm not sure if Vista changed this model, so maybe it won't work; I don't have a Vista machine in front of me to test against.

Edit 2:

Ok, I got something that mostly works (tested on Windows 7). First you have to have a service running as SYSTEM. From that service, you need to launch a separate application in the user's session. To do this, enumerate all the processes looking for winlogon.exe, open its token, and CreateProcessAsUser. Specify "WinSta0\Winlogon" for the lpDesktop parameter of STARTUPINFO. Now you have a process running as SYSTEM in the user's session on the "Winlogon" desktop. In the new process you can do whatever you want; I did a quick test with EnumDesktopWindows and I was able to get the window class and text for various UAC related windows ("$$$Secure UAP Background Window", "$$$Secure UAP Background Fake Client Window", etc). I'm not sure how to determine when a UAC prompt is being displayed, though; as a quick hack you could just run a loop every 100 ms looking for UAC windows or something. I could paste some code if it would help.

Edit 3:

Ok. I have written a Win32 service that takes the following parameters:

/install - installs the service
/uninstall - uninstalls the service
/service - runs as a service; invoked via SCM
/client - runs as a client; invoked via CreateProcessAsUser

The only interesting code is in the /service and /client modes.

In /service mode it enumerates the running processes via EnumProcesses and GetModuleFileNameEx looking for "winlogon.exe". When it finds one it opens its token and launches itself in /client mode via CreateProcessAsUser:

HANDLE hProcess = ...;
// winlogon.exe runs as SYSTEM in user's session; we need to run the same way
HANDLE hToken = NULL;
if(OpenProcessToken(hProcess, TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY, &hToken))
{
    TCHAR szCommandLine[MAX_PATH];
    GetModuleFileName(NULL, szCommandLine, MAX_PATH);
    PathQuoteSpaces(szCommandLine);
    // run in /client mode
    _tcscat_s(szCommandLine, MAX_PATH, _T(" /client"));
    STARTUPINFO StartupInfo;
    ZeroMemory(&StartupInfo, sizeof(STARTUPINFO));
    StartupInfo.cb = sizeof(STARTUPINFO);
    // run on the Winlogon desktop
    StartupInfo.lpDesktop = _T("WinSta0\\Winlogon");
    PROCESS_INFORMATION ProcessInformation;
    ZeroMemory(&ProcessInformation, sizeof(PROCESS_INFORMATION));
    if(CreateProcessAsUser(hToken, NULL, szCommandLine, NULL, NULL, FALSE, 0, NULL, NULL, &StartupInfo, &ProcessInformation))
    {
        CloseHandle(ProcessInformation.hThread);
        ProcessInformation.hThread = NULL;
        CloseHandle(ProcessInformation.hProcess);
        ProcessInformation.hProcess = NULL;
    }
    CloseHandle(hToken);
    hToken = NULL;
}

In /client mode it clicks the "Yes" button on the UAC prompt via a bunch of FindWindow and FindWindowEx calls. You can use Spy++ to figure out the window hierarchy.

HWND hWnd = ...;
HWND hWndButton = FindWindowEx(hWnd, NULL, _T("Button"), NULL);
if(hWndButton != NULL)
{
    // see if this is the "Yes" button
    TCHAR szText[32];
    if(GetWindowText(hWndButton, szText, 32) && _tcsicmp(szText, _T("&Yes")) == 0)
    {
        // click it
        SendMessage(hWndButton, BM_CLICK, 0, 0);
    }
}

The way I test this is to stick a Sleep(5000); in the /client code. Then I start the service and immediately do something that triggers a UAC prompt (i.e. run regedit). After 5 seconds the /client code will wake up and find and click the "Yes" button. You can run other processes on the Winlogon desktop; cmd.exe and spyxx.exe (Spy++) are most useful. Unfortunately, explorer.exe exhibits a lot of problems when running on the Winlogon desktop and isn't very useful. To get to the Winlogon desktop you can run regedit and then Alt+Tab to switch to the other application. If you want to get fancy you can write your own desktop switching utility (using the SwitchDesktop function) so you don't have to trigger a UAC prompt to get to the Winlogon desktop. If you want to get really fancy you can set a global window hook to monitor window creation; when the UAC dialog is about to be displayed you can prepare to click its "Yes" button. I didn't take it quite that far, though.

Luke
Any code you could post would be VERY helpful. THANKS!
Ray
Thanks for the code. Any interest in working on this for me on a contract basis?
Ray
Thanks, but I'm happily employed already.
Luke
I got this compiled and running in vb.net. Every step returns true, but I'm getting a process ID of zero back in "ProcessInformation", even though the function returns true, so it doesn't appear to be creating the process. When this works, will I literally see the window to my application showing on my windows login screen? Do I have to be on the login screen when I run this? Is running it on the winlogon desktop the same as running it on the desktop that the UAC window shows up on? Any tips you can provide would be appreciated. Thanks!
Ray
It's been so long that I don't really remember the specifics. If I recall correctly I believe it was showing up on the same desktop that the UAC prompt appears on. I think I tested it by running regedit, which is manifested so it always shows the UAC prompt. Once on the UAC desktop you can ALT+TAB to the other app.
Luke
A: 

I got this compiled and running. Every step returns true, but I'm getting a process ID of zero back in "ProcessInformation", so it doesn't appear to be working. Any tips? When this works, will I literally see the window to my application showing on my windows login screen? Do I have to be on the login screen when I run this? Is running it on the winlogon desktop the same as running it on the desktop that the UAC window show up on? Any tips you can provide would be appreciated. Thanks!

Ray