MS Windows: assistive technology, code signing, send keystrokes to other applications and Windows login password field

HaO 2023-12-07:

Goal

Send keystrokes from a TCL application to other applications and the Windows login password field

Application

My company authors integration software where data scanned by barcode scanners are handled and sent to other programs.

First tests

Using TWAPI, one may send keystrokes to the foreground program. Here is a simple test line sending an "a" (this is over-complicated, TWAPI can do it better, but only for testing procedures with maximum control):

twapi::send_input [list [list key 65 0x1e]]

I wrap the script using basekits from Ashok [L1 ], test file is "tclkit-gui-8_6_13-twapi-4_7_2-x86-max.exe". My platform is Windows 10 64 bit with German localisation.

If the program is started as normal user, one can send keystrokes to other user programs, but not to elevated programs. If the program is started with Adminstrator rights, you can send keystrokes to all user programs and administrator programs.

If you lock the screen, you may open the login screen of Windows while the program is running. You can change from the picture screen to the password entry screen using the program. But you can not enter any data to the password entry field to unlock the session.

The situation is the same, if you run the program as a Windows service.

Assistive technology

Assistive technology may help you to handle your computer and allows programs to send keystrokes to any program including the login password screen. You may test this using the on-screen keyboard. It also works within the login password screen.

The important point is the Integrity Level (IL) of the application, which will allow or disallow to send keystrokes [L2 ]. Assistive technology is allowed to get a higher IL [L3 ].

Programs for assistive technology have the manifest switch uiAccess set to true [L4 ]. The scandard wish and starkit manifest contains the following trust information (use ResHacker.exe to extract).

    <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
        <security>
            <requestedPrivileges>
                <requestedExecutionLevel level="asInvoker" uiAccess="false"></requestedExecutionLevel>
            </requestedPrivileges>
        </security>
    </trustInfo>

You may use ResHacker.exe to change uiAccess="true". The result was a smaller and not starting any more executable. So, I asked Ashok, to build a starkit with this set from scratch. The starting error message is normal and will always show-up, if the executable is not signed with a certifificate which goes to the trusted root certificates.

The error message looks in German localization like that:

win_error_message_uiaccess_true_not_signed.png

A translation of the text would be: "A reference evaluation was returned from the server.".

The requirements for assistive technology programs are: [L5 ]

  • Executable signed with a certificate which goes to a trusted root certificate
  • Secure location, for example "C:\program files(x86)\..."
  • Manifest with uiAcess = true

Code signing

So, lets attack code signing [L6 ].

Paul has given a recipe to sign starkits on this page: SDX under Windows. That works great.

My company has purchased an EV certificate from GlobalSign, which took two weeks of processing time and costs around 300$ per year. It is on a secure USB stick. I also downloaded all the "cross certificates" and made a double-click on them to get them in the certificate store of my computer.

I use the signtool provided by MS-VisualStudio and enter the following command:

"C:\Program Files (x86)\Windows Kits\8.1\bin\x86\signtool.exe" sign /n "<CN of the certificate>" /tr http://timestamp.globalsign.com/tsa/r6advanced1 /td SHA256 starkit.exe

The "/n" is required, as I now have a lot of certificates on the computer and I shall choose the right one, otherwise "/a" may be used.

After entering the USB stick password, the comment "signing succeeded" is shown. Now, the executable may be executed, even with "uiAccess=true".

Side note: Remove signature to unwrap starkit

Later, I wanted to unwrap the starkit and this did not work. I had to remove the signature with "delcert" [L7 ]. Then, I had to remove trailing zeros from the file with frhed hex editor.

Secure position

The program is moved to "c:\program files(x86)" using "InnoSetup" (what is also signed with the same key by the setup change:

[Setup]
SignTool=MsSign $f

and Tools->Signtool configuration->MsSign:

"C:\Program Files (x86)\Windows Kits\8.1\bin\x86\signtool.exe" sign /n "<CN of the certificate>" /tr http://timestamp.globalsign.com/tsa/r6advanced1 /td SHA256  /fd SHA256 $p

)

Test

Now, it is possible to send keystrokes from a normal user process to administrator processes. So, something is happenning and a certain aim is acheved.

Another property is observed: the env array in the interpreter does not match the environment of the program start. If keys are just set before, they are missing. Also the CITRIX "DISPLAYNAME" variable is set to "UNKNOWN", even if the current user has a correct value included.

When starting the program with Administrator rights, the login password can still not be changed. This does not work as user program or as a service with an administrator or LocalSystemAdmin user.

Registering with the Ease of Access Center

Assistive technology programs may be registered within the Windows Ease of Access Center as explained here: [L8 ].

Inno-Setup

The following Inno-Setup registry key section is used:

[Registry]
Root: "HKLM64"; Subkey: "SOFTWARE\Microsoft\Windows NT\CurrentVersion\Accessibility\ATs\Company_Program_v1"; ValueType: expandsz; ValueName: "ApplicationName"; ValueData: "ProgramName"; Flags: uninsdeletekey; Check: IsWin64
Root: "HKLM64"; Subkey: "SOFTWARE\Microsoft\Windows NT\CurrentVersion\Accessibility\ATs\Company_Program_v1"; ValueType: string; ValueName: "ATExe"; ValueData: "ExeName.exe"; Check: IsWin64
Root: "HKLM64"; Subkey: "SOFTWARE\Microsoft\Windows NT\CurrentVersion\Accessibility\ATs\Company_Program_v1"; ValueType: string; ValueName: "Description"; ValueData: "Usage description"; Check: IsWin64
Root: "HKLM64"; Subkey: "SOFTWARE\Microsoft\Windows NT\CurrentVersion\Accessibility\ATs\Company_Program_v1"; ValueType: string; ValueName: "Profile"; ValueData: "<HCIModel><Accommodation type=""mild dexterity"" /><Accommodation type=""severe dexterity"" /></HCIModel>"; Check: IsWin64
Root: "HKLM64"; Subkey: "SOFTWARE\Microsoft\Windows NT\CurrentVersion\Accessibility\ATs\Company_Program_v1"; ValueType: string; ValueName: "SimpleProfile"; ValueData: "Barcode scanner"; Check: IsWin64
Root: "HKLM64"; Subkey: "SOFTWARE\Microsoft\Windows NT\CurrentVersion\Accessibility\ATs\Company_Program_v1"; ValueType: string; ValueName: "StartExe"; ValueData: "{app}\ExeName.exe"; Check: IsWin64
Root: "HKLM64"; Subkey: "SOFTWARE\Microsoft\Windows NT\CurrentVersion\Accessibility\ATs\Elmicron_ScanLink_v7"; ValueType: string; ValueName: "StartParams"; ValueData: "-easeofaccessname Company_Program_v1"; Check: IsWin64Root: "HKLM64"; Subkey: "SOFTWARE\Microsoft\Windows NT\CurrentVersion\Accessibility\ATs\Company_Program_v1"; ValueType: dword; ValueName: "TerminateOnDesktopSwitch"; ValueData: "1"; Check: IsWin64

When opening the Windows Ease of Access Center (System control panel->Ease of Access Center), there is a button on the left "Change login options". When pressing the button, the just registered application is listed and may be selected to be started before login and/or after login. Here is the German localized page:

ms-windows_assistent_technology_start.png

If both checkboxes are checked for the application, it is imediately started.

It is started when the user changes from the login picture screen to the password dialog screen. And now, it is possible to send data to the password entry field -> GOAL!

The program is started quite often by the Ease of Access Center. The program is stopped without warning, e.g. "wm protocol . WM_SAVE_YOURSELF" is not invoked.

Communicate with MS-Windows Ease of Access Center

As configured in the upper registry, the program gets a parameter "-easeofaccessname Company_Program_v1" if started by the Ease of Access Center.

This should trigger the following actions:

Check for running as a batch

The following should happen on program start:

BOOL fAlreadyInJob;
BOOL fSuccess = IsProcessInJob(GetCurrentProcess(), NULL, &fAlreadyInJob);

It would be great, if twapi could do this. The program should start the Ease of Access Center and exit, if running in a batch.

There was a solution for this on clt using CFFI. I had tagged it, but the news sever says "Expired".

Notify Ease of Access Center about program start/end

If the program does not run within a batch (Registry entry "TerminateOnDesktopSwitch" above true), it should notify the Ease of Access Center about a program start.

If the program has a program end possibility (X in the Taskbar), it should notify the Ease of Access Center on exit.

The Center itself initializes the registry key "Company_Program_v1" in the registry path "HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\AccessibilityTemp" with a value of "1". The program should write a 3 on start (only if not in batch mode) and a 2 on program exit.

Then, Windows+u should be virtually pressed. Unfortunately, there is no LWIN press and hold modifier for twapi::send_keys like "+" for shift.

So, here is my code for the later:

# Check if started by the MS Windows Ease of Access Center
if {[dict exists $args -easeofaccessname]} {
    package require registry
    registry -64bit set\
            {HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\AccessibilityTemp}\
            [dict get $args -easeofaccessname]\
            2 dword
    package require twapi_input
    twapi::send_input {"keydown 0x5B 0" "key 0x55 0" "keyup 0x5B 0"}

Users and environment

If checked in the Ease of Access Center, the program is started on User login or if the login password screen is shown. If started after user login, the program is started with the logged in user. If started with the login screen, the environment is as follows:

env(USER) = feuerstein$
env(USERPROFILE) = C:\Windows\system32\config\systemprofile

where "feuerstein" is the name of the computer.

In addition, the program is stopped and started again, if a program with administrative rights get started. I suppose, then, the administrative user is used, but I can not verify, as it is the same user.

Restrictions

The following restrictions were observed:

  • Subwindows like combo box dropdown and bubble help appear in the upper left corner. There is apparently a coordinate not well delivered. Those applications are allowed to overlay even the application with the focus. This may do the difference.
  • TWAPI Taskbar icons do not work in the login screen. The error is as follows:
Unknown error
    while executing
"Shell_NotifyIcon 0 [_make_NOTIFYICONW $id -hicon $hicon]"
    (procedure "::twapi::systemtray::addicon" line 7)
    invoked from within
"::twapi::systemtray addicon [dict get $StateRun(dIconHandles) $FileIn] [namespace code TrayCallback]"
--Traceback--
INNER:invokeStk1 Shell_NotifyIcon 0 {...}
CALL:::twapi::systemtray::addicon {656083 HICON} {::namespace inscope ::gui TrayCallback}
  • Beware of kill and restarts at any moment.

Pointers

Here is a set of other pointers about this issue:

  • Credential providers: [L9 ] -> for new ways to login, but not to enhance present logins
  • Run with Windows welcome process: [L10 ]
  • Driver method [L11 ]

Ideas welcome

If others have any ideas - you are welcome. I want to thank ET99 for his constant testing in the clt thread "MS-Windows: Send keys to login/unlock page/Asisstive Technology Application" started on 2023-06-07. I also want to thank Ashok for his constant support.