Windows XP X64 shares the same binaries with Windows 2003 X64, but Terminal Server has some restrictions on XP. This article shows you how to get rid of them and is based on cw2k ideas from the original Windows XP Terminal Server patch.

Version 1.1 contains bug#1 fix and is smaller (less bytes are changed).

1) Winlogon.exe contains a function, called EnumerateMatchingUsers which in turn calls IsProfessionalTerminalServer function. We need to patch this function to return zero (false):

.text:0000000100042F77 IsProfessionalTerminalServer proc near ; CODE XREF: EnumerateMatchingUsers:loc_10002B44Bp
.text:0000000100042F77
; DATA XREF: .pdata:00000001000D01DCo …
.text:0000000100042F77

.text:0000000100042F77
VersionInformation= _OSVERSIONINFOW ptr -138h
.text:0000000100042F77
var_20 = word ptr -20h
.text:0000000100042F77
var_ 1E = byte ptr -1Eh
.text:0000000100042F77
var_18 = qword ptr -18h
.text:0000000100042F77

.text:0000000100042F77 48 81 EC 58 01 00 00 sub rsp, 158h => 31 C0 C3 xor eax, eax; retn
.text:0000000100042F7E 48 8B 05 F3 3A 08 00 mov rax, cs:__security_cookie
.text:0000000100042F85 48 89 84 24 40 01 00 00 mov [rsp+158h+var_18], rax
.text:0000000100042F8D 48 8D 4C 24 20 lea rcx, [rsp+158h+VersionInformation] ; void *
.text:0000000100042F92 33 D2 xor edx, edx ; int

But it’s not the only place; there is a function named IsPerOrProTerminalServer. It has a few usages; however we will patch only 1 usage usages in a function called MultiUserLogonAttempt:

.text:0000000100044A91 E8 71 E5 FF FF call IsPerOrProTerminalServer
.text:0000000100044A96 85 C0 test eax, eax
.text:0000000100044A98 0F 84 C9 00 00 00 jz loc_100044B67 => 0F 8D C9 00 00 00 jge loc_100044B67 Not required in version 1.1, causes bug #1 to happen.

.text:0000000100044B13 E8 EF E4 FF FF call IsPerOrProTerminalServer
.text:0000000100044B18 85 C0 test eax, eax
.text:0000000100044B1A 74 0C jz short loc_100044B28 => EB 0C jmp short loc_100044B28

2) Termsrv.dll. Let’s make Terminal Server think it’s running on a server OS:
In ServiceMain, termsrv.dll initializes a global variable called gbServer. We need its value to be true (1):

.text:000007FF7B877DAF 33 FF xor edi, edi

.text:000007FF7B877F77 33 C9 xor ecx, ecx ; ConditionMask
.text:000007FF7B877F79 C7 05 9D 2C 05 00 1C 01 00 00 mov cs:gOsVersion.dwOSVersionInfoSize, 11Ch
.text:000007FF7B877F83 C6 05 B0 2D 05 00 01 mov cs:gOsVersion.wProductType, 1
.text:000007FF7B877F8A FF 15 F8 9A FF FF call cs:__imp_VerSetConditionMask
.text:000007FF7B877F90 48 8D 0D 89 2C 05 00 lea rcx, gOsVersion ; lpVersionInformation
.text:000007FF7B877F97 BA 80 00 00 00 mov edx, 80h ; dwTypeMask
.text:000007FF7B877F9C 4C 8B C0 mov r8, rax ; dwlConditionMask
.text:000007FF7B877F9F FF 15 73 95 FF FF call cs:__imp_VerifyVersionInfoW
.text:000007FF7B877FA5 8B CF mov ecx, edi => 31 C9< xor ecx, ecx Not required in version 1.1 since edi contains zero already
.text:000007FF7B877FA7 48 8D 15 B2 AE FF FF lea rdx, SubKey ; “System\\CurrentControlSet\\Control\\Termin”…
.text:000007FF7B877FAE 85 C0 test eax, eax
.text:000007FF7B877FB0 48 8D 44 24 60 lea rax, [rsp+78h+hKey]
.text:000007FF7B877FB5 41 B9 19 00 02 00 mov r9d, 20019h ; samDesired
.text:000007FF7B877FBB 0F 94 C1 setz cl => FF C1 90 inc ecx; nop
.text:000007FF7B877FBE 45 33 C0 xor r8d, r8d ; ulOptions
.text:000007FF7B877FC1 48 89 44 24 20 mov [rsp+78h+var_58], rax
.text:000007FF7B877FC6 89 0D 30 B0 04 00 mov cs:gbServer, ecx

So we have promoted ourselves to server. However on server OS it’s not allowed to disconnect the console (STATUS_CTX_CONSOLE_DISCONNECT = $C00A0027). So we need to patch 2 places where this code is used:

.text:000007FF7B889D99 85 C0 test eax, eax
.text:000007FF7B889D9B 75 21 jnz short loc_7FF7B889DBE => EB 21 jmp short loc_7FF7B889DBE
.text:000007FF7B889D9D 48 8D 4B 18 lea rcx, [rbx+18h]
.text:000007FF7B889DA1 BF 27 00 0A C0 mov edi, 0C00A0027h

and one more

.text:000007FF7B88AA1B 45 85 E4 test r12d, r12d
.text:000007FF7B88AA1E 74 0A jz short loc_7FF7B88AA2A => EB 0A jmp short loc_7FF7B88AA2A
.text:000007FF7B88AA20 BB 27 00 0A C0 mov ebx, 0C00A0027h

That’s all for the mandatory patches! To apply them, you need to

  • At first, make sure you’re doing it from 64 bit process (for example, 64 bit explorer.exe)! 32 bit processes are redirected to %windir%\SysWOW64 directory (read http://msdn.microsoft.com/en-us/library/aa384187(VS.85).aspx).
  • Delete (or move) all termsrv.dll and winlogon.exe files (they are usually in %windir%\system32\DllCache, in %windir%\ServicePackFiles, or maybe somewhere else), except files in %windir%\system32 . You also need to remove the Windows XP x64 distributive from your CD and/or network location.
  • Rename %windir%\system32 winlogon.exe and termsrv.dll to (for example) winlogon.bak and termsrv.bak. If you’ve done it correctly, you will notice messages likewpa.
  • Copy winlogon.bak and termsrv.bak to some other directory (e.g. c:\temp) and rename them back to winlogon.exe and termsrv.dll. Apply patch to them.
  • Move patched files back to their original location (%windir%\system32). You will see the WPA message (like picture above) again.
  • Reboot
  • In case the system will not boot, you’ll need to restore your backups (winlogon.bak and termsrv.bak)

Downloads:

version 1.1
Windows Xp X64 Terminal Server Mandatory Patch (5141 downloads ) version 1.0

Registry changes: make sure that the following keys are set:

  • HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\AllowMultipleTSSessions => 1
  • HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\WinStationsDisabled => 0
  • HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\Console\fEnableWinStation => 1
  • HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp\fEnableWinStation => 1

Some issues remains though: you cannot start a session to localhost (a workaround is connecting to 127.0.0.2), and if you lock your workstation from an RPD Session (not console), you’ll just get disconnected (in case of FUS). I will address these issues in the part 2.

Bug #1 Other known issue: a zero-session winlogon will NOT connect to the existing session, but will create a new one instead. This is being investigated currently. Fixed in version 1.1