Programming the Winlogon desktop
Written by Wojciech Kocjan   
This article is very different from what you people are used to read on this blog. I came across this as an accident and frankly I'm quite interested in what ways other people think of using it for. The article is about accessing and working with the Winlogon desktop on Windows systems - this is the screen used for logging in and unlocking computer.

Recently I set up a Synergy application that allows using a single keyboard and mouse to manage more than one physical computer. What's interesting is that when it's run as a normal binary on your system it does not work when you lock the computer. When you set up synergy as a system service though it runs fine all the time. It got me wondering - then I came across SetThreadDesktop() Windows function.

One step lead to another and I noticed that if you run your application as a system service with permission to access desktop you can create a thread and change desktop to the desktop that's used to log you into the system and unlock your computer.

I found out that TWAPI package already offers this function along with twapi::get_desktop_handle function to open desktops. However due to how Tcl communicates it's not switch desktops at all. Then I thought - what if thread package would allow creating a thread in different desktops?

I patched it and quite interesting things started becoming possible.

I patched Thread package version 2.6.5. Below are some downloads:
thread-2.6.5-desktop.patch (patch against source code)
thread2.6.5-desktop.zip (pre-compiled binaries)
Since the patch is mainly a fun project I don't even suggest it to Tcl core. It would probably need to be implemented in a more system-independant way. The more interesting part is what you can do later on!

Assuming you have a threaded Tcl installed, simply backup and overwrite Thread package with the one above. You also need to run your Tcl as a service - either tclsvc or srvany can be used for this purpose.

Assuming you have Tcl in C:/Tcl and have twapi package installed, installing Tkcon as a service is a matter of downloading srvany.exe from Windows Server 2003 Resource Kit Tools, copying it into C:/tcl/bin/srvany.exe and running the following snippet of code:

Intallation of TkCon as a service
package require twapi
package require registry
 
# service name
set servicename TkCon
 
# registry for actual service
set reg "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\$servicename"
 
# create service
twapi::create_service $servicename \
    C:\\tcl\\bin\\srvany.exe \
    -interactive 1 -starttype demand_start \
    -displayname "Tk Console"
 
# set up parameters for srvany
registry set "$reg\\Parameters" Application \
    "C:\\tcl\\bin\\wish.exe"
registry set "$reg\\Parameters" AppParameters \
    "C:/tcl/bin/tkcon.tcl"
 
# start TkCon service
twapi::start_service $servicename
 
exit 0
 

Now we have a console that runs as LocalSystem user. This gives you great power to do some really nasty tricks. First let's check if we're actually a system admin. Example below shows how:

Checking current user
% package require twapi ; puts [::twapi::get_current_user]
SYSTEM

That was easy. Now some fun exercise - you can type that into the console or write in a separate file and source it - but be sure not to mistype the code:

Creating a window on Winlogon desktop
package require Thread
set tid [thread::create -desktop Winlogon]
 
thread::send $tid {::twapi::lock_workstation
after 1000
set h [twapi::find_windows -toplevel 1 -visible 1 -single]
twapi::hide_window $h
after 1000
twapi::show_window $h
}
 

What you should have seen is that your workstation was locked and for a second the unlock computer window was gone. Hopefully it came back. If you mistyped the twapi::show_window part, you'll have little chance to do anything except for rebooting your system or logging in via remote desktop.

Now another fun part:

Moving the unlock window randomly
thread::send $tid {
    package require Tk ; wm withdraw .
    ::twapi::lock_workstation
    after 100
    set ::h [twapi::find_windows -toplevel 1 -visible 1 -single]
    twapi::hide_window $h
    while {[tk_messageBox -message "Do you want to unlock your computer?" \
        -type yesno] != "yes"} {}
    twapi::show_window $h
    proc randommove {} {
        catch {
            twapi::move_window $::h \
                [expr {(([winfo pointerx .] + 400) % [winfo screenwidth .])}] \
                [expr {(([winfo pointery .] + 400) % [winfo screenheight .])}]
            after 500 randommove
        }
    }
    randommove
}
 

You'll need to click on yes and then try to chase the window to set focus to it.

Anyway, there are two main points I'd like to make. First one is that it seems to be a security issue - I can imagine someone hiding and creating an identical window to hijack your password. It makes little sense to do it at home, but if you're a corporate user - then probably this is the same password that your email account and all corporate resources use. But I didn't find any malicious software that does this, so perhaps it's not possible...

Another, more interesting point is that you can easily code an application that prints out current time or whatever you might be interested in even when your desktop is locked.

For me it makes it possible to lock my computer and have a small application running that'll monitor if my calculations, download or build has been completed.

If I'll have some time I'll follow up on this issue with hopefully some useful use case.

Keep checking the blog!