In part 1 I showed how winlogon.exe registers its process and main window handle.

In the SasCreate function, winlogon.exe registers hotkeys like this:

objectpascal Download
const
  MOD_SAS = $8000;

  RegisterHotKey(SasWindow, 0, MOD_SAS or MOD_CONTROL or MOD_ALT, VK_DELETE);

{$IFDEF CHECKED_BUILD}
  RegisterHotKey(SasWindow, 1, MOD_ALT or MOD_CONTROL or MOD_SHIFT, VK_DELETE); // handler just calls NtShutdownSystem
  if EnableDesktopSwitching then
    RegisterHotKey(SasWindow, 2, MOD_ALT or MOD_CONTROL, VK_TAB); // handler switches default and winlogon desktops
  if WinlogonInfoLevelFlag then
    RegisterHotkey(SasWindow, 3, MOD_ALT or MOD_CONTROL or MOD_SHIFT, VK_TAB); // handler just calls DebugBreak
{$ENDIF}

  RegisterHotKey(SasWindow, 4, MOD_CONTROL or MOD_SHIFT, VK_ESCAPE); // handler executes task manager
{$IFDEF WINXP_OR_LATER}
  RegisterHotKey(SasWindow, 5, MOD_WIN, Byte('L'); // handler locks the workstation
  RegisterHotkey(SasWindow, 6, MOD_WIN, Byte('U'); // handler executes utilman on current desktop
{$ENDIF}

Did you notice the MOD_SAS constant?

It’s an undocumented value which can be successfully used only by the logon process (read part 1). As you see, ANY hotkey combination can be used as SAS (Secure Attention Sequence) combination; a special behavior of SAS is that it enables input after a call of BlockInput, so it cannot be recorded or played back by Journal Hook and cannot be simulated with the SendInput API.

So, how we can use it? winlogon.exe runs on the secure Winlogon desktop. So we need to be running as system! At first, we need to find the target window. I do not want to bother with SetThreadDesktop, so we’ll just do a cycle in EnumDesktopWindows:

objectpascal Download
const
  SASWindowClass = 'SAS window class';

var
  WinlogonDesktopHandle : HDESK = 0;

function FindSasWindowProc(Window : HWND; var SasWindow : HWND) : BOOL; stdcall;
var
  WindowClassName : array [0..255] of char;
begin;
  Result := True;
  if GetClassName(Window, WindowClassName, SizeOf(WindowClassName)) > 0 then
  begin
    if WindowClassName = SASWindowClass then
    begin
      SasWindow := Window;
      Result := False;
    end;
  end;
end;

procedure InitDesktop;
begin
  WinlogonDesktopHandle := OpenDesktop('Winlogon', 0, false, DESKTOP_READOBJECTS or DESKTOP_ENUMERATE);
  Win32Check(WinlogonDesktopHandle <> 0);
end;

procedure DoneDesktop;
begin
  CloseDesktop(WinlogonDesktopHandle);
  WinlogonDesktopHandle := 0;
end;

function GetSasWindowHandle : HWND;
begin
  Result := 0;

  EnumDesktopWindows(WinlogonDesktopHandle, @FindSasWindowProc, Integer(@Result));
  if Result = 0 then
  begin
    Writeln('Unable to find SAS window');
    Abort;
  end;
end;

Now we can send the messages directly:

objectpascal Download
const  CAD_HOTKEY = 0;

procedure PressCad;
begin
  PostMessage(GetSasWindowHandle, WM_HOTKEY, CAD_HOTKEY, 0);
end;

Windows XP allows you even to unlock the workstation by sending a message:

objectpascal Download
procedure UnlockWorkstation;
begin
  PostMessage(GetSasWindowHandle, WM_LOGONNOTIFY, UNLOCK_WORKSTATION_WPARAM, 0);
end;

Windows 2000 cannot be unlocked this way for now. Maybe… later? ;-)

WinstaLocker-1-3.zip

You can download the sample program with included sources. As a bonus, it allows remote execution on the target machine.

P.S. In Windows Vista and higher the logon mechanism has been changed to RPC interfaces, so this program will NOT work on these platforms.