mirror of https://github.com/stascorp/rdpwrap
				
				
				
			
			You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							737 lines
						
					
					
						
							24 KiB
						
					
					
				
			
		
		
	
	
							737 lines
						
					
					
						
							24 KiB
						
					
					
				{
 | 
						|
  Copyright 2014 Stas'M Corp.
 | 
						|
 | 
						|
  Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
  you may not use this file except in compliance with the License.
 | 
						|
  You may obtain a copy of the License at
 | 
						|
 | 
						|
      http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
 | 
						|
  Unless required by applicable law or agreed to in writing, software
 | 
						|
  distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
  See the License for the specific language governing permissions and
 | 
						|
  limitations under the License.
 | 
						|
}
 | 
						|
 | 
						|
library rdpwrap;
 | 
						|
 | 
						|
uses
 | 
						|
  SysUtils,
 | 
						|
  Windows,
 | 
						|
  TlHelp32,
 | 
						|
  LiteINI;
 | 
						|
 | 
						|
{$R rdpwrap.res}
 | 
						|
 | 
						|
// Hook core definitions
 | 
						|
 | 
						|
type
 | 
						|
  OldCode = packed record
 | 
						|
    One: DWORD;
 | 
						|
    two: Word;
 | 
						|
  end;
 | 
						|
 | 
						|
  far_jmp = packed record
 | 
						|
    PushOp: Byte;
 | 
						|
    PushArg: Pointer;
 | 
						|
    RetOp: Byte;
 | 
						|
  end;
 | 
						|
 | 
						|
  mov_far_jmp = packed record
 | 
						|
    MovOp: Byte;
 | 
						|
    MovArg: Byte;
 | 
						|
    PushOp: Byte;
 | 
						|
    PushArg: Pointer;
 | 
						|
    RetOp: Byte;
 | 
						|
  end;
 | 
						|
 | 
						|
  TTHREADENTRY32 = packed record
 | 
						|
    dwSize: DWORD;
 | 
						|
    cntUsage: DWORD;
 | 
						|
    th32ThreadID: DWORD;
 | 
						|
    th32OwnerProcessID: DWORD;
 | 
						|
    tpBasePri: LongInt;
 | 
						|
    tpDeltaPri: LongInt;
 | 
						|
    dwFlags: DWORD;
 | 
						|
  end;
 | 
						|
  //IntArray = Array of Integer;
 | 
						|
  FILE_VERSION = record
 | 
						|
    Version: record case Boolean of
 | 
						|
      True: (dw: DWORD);
 | 
						|
      False: (w: record
 | 
						|
        Minor, Major: Word;
 | 
						|
      end;)
 | 
						|
    end;
 | 
						|
    Release, Build: Word;
 | 
						|
    bDebug, bPrerelease, bPrivate, bSpecial: Boolean;
 | 
						|
  end;
 | 
						|
 | 
						|
const
 | 
						|
  THREAD_SUSPEND_RESUME = 2;
 | 
						|
  TH32CS_SNAPTHREAD = 4;
 | 
						|
var
 | 
						|
  INI: INIFile;
 | 
						|
  LogFile: String = '\rdpwrap.txt';
 | 
						|
  bw: {$if CompilerVersion>=16} NativeUInt {$else} DWORD {$endif};
 | 
						|
  IsHooked: Boolean = False;
 | 
						|
 | 
						|
// Unhooked import
 | 
						|
 | 
						|
function OpenThread(dwDesiredAccess: DWORD; bInheritHandle: BOOL;
 | 
						|
  dwThreadId: DWORD): DWORD; stdcall; external kernel32;
 | 
						|
 | 
						|
function CreateToolhelp32Snapshot(dwFlags, th32ProcessID: DWORD): DWORD;
 | 
						|
  stdcall; external kernel32;
 | 
						|
 | 
						|
function Thread32First(hSnapshot: THandle; var lpte: TTHREADENTRY32): bool;
 | 
						|
  stdcall; external kernel32;
 | 
						|
 | 
						|
function Thread32Next(hSnapshot: THandle; var lpte: TTHREADENTRY32): bool;
 | 
						|
  stdcall; external kernel32;
 | 
						|
 | 
						|
// Wrapped import
 | 
						|
 | 
						|
var
 | 
						|
  TSMain: function(dwArgc: DWORD; lpszArgv: PWideChar): DWORD; stdcall;
 | 
						|
  TSGlobals: function(lpGlobalData: Pointer): DWORD; stdcall;
 | 
						|
 | 
						|
// Hooked import and vars
 | 
						|
 | 
						|
var
 | 
						|
  SLGetWindowsInformationDWORD: function(pwszValueName: PWideChar;
 | 
						|
    pdwValue: PDWORD): HRESULT; stdcall;
 | 
						|
  TermSrvBase: Pointer;
 | 
						|
  FV: FILE_VERSION;
 | 
						|
 | 
						|
var
 | 
						|
  Stub_SLGetWindowsInformationDWORD: far_jmp;
 | 
						|
  Old_SLGetWindowsInformationDWORD: OldCode;
 | 
						|
 | 
						|
// Main code
 | 
						|
 | 
						|
procedure WriteLog(S: AnsiString);
 | 
						|
var
 | 
						|
  F: TextFile;
 | 
						|
begin
 | 
						|
  if not FileExists(LogFile) then
 | 
						|
    Exit;
 | 
						|
  AssignFile(F, LogFile);
 | 
						|
  Append(F);
 | 
						|
  Write(F, S+#13#10);
 | 
						|
  CloseFile(F);
 | 
						|
end;
 | 
						|
 | 
						|
function GetModuleHandleEx(dwFlags: DWORD; lpModuleName: PWideChar;
 | 
						|
  var phModule: HMODULE): BOOL; stdcall; external kernel32 name 'GetModuleHandleExW';
 | 
						|
 | 
						|
function GetCurrentModule: HMODULE;
 | 
						|
const
 | 
						|
  GET_MODULE_HANDLE_EX_FLAG_PIN = 1;
 | 
						|
  GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT = 2;
 | 
						|
  GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS = 4;
 | 
						|
begin
 | 
						|
  Result := 0;
 | 
						|
  GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, @GetCurrentModule, Result);
 | 
						|
end;
 | 
						|
 | 
						|
function GetBinaryPath: String;
 | 
						|
var
 | 
						|
  Buf: Array[0..511] of Byte;
 | 
						|
begin
 | 
						|
  ZeroMemory(@Buf[0], Length(Buf));
 | 
						|
  GetModuleFileName(GetCurrentModule, PWideChar(@Buf[0]), Length(Buf));
 | 
						|
  Result := PWideChar(@Buf[0]);
 | 
						|
end;
 | 
						|
 | 
						|
procedure StopThreads;
 | 
						|
var
 | 
						|
  h, CurrTh, ThrHandle, CurrPr: DWORD;
 | 
						|
  Thread: TTHREADENTRY32;
 | 
						|
begin
 | 
						|
  CurrTh := GetCurrentThreadId;
 | 
						|
  CurrPr := GetCurrentProcessId;
 | 
						|
  h := CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
 | 
						|
  if h <> INVALID_HANDLE_VALUE then
 | 
						|
  begin
 | 
						|
    Thread.dwSize := SizeOf(TTHREADENTRY32);
 | 
						|
    if Thread32First(h, Thread) then
 | 
						|
      repeat
 | 
						|
        if (Thread.th32ThreadID <> CurrTh) and
 | 
						|
          (Thread.th32OwnerProcessID = CurrPr) then
 | 
						|
        begin
 | 
						|
          ThrHandle := OpenThread(THREAD_SUSPEND_RESUME, false,
 | 
						|
            Thread.th32ThreadID);
 | 
						|
          if ThrHandle > 0 then
 | 
						|
          begin
 | 
						|
            SuspendThread(ThrHandle);
 | 
						|
            CloseHandle(ThrHandle);
 | 
						|
          end;
 | 
						|
        end;
 | 
						|
      until not Thread32Next(h, Thread);
 | 
						|
      CloseHandle(h);
 | 
						|
  end;
 | 
						|
end;
 | 
						|
 | 
						|
procedure RunThreads;
 | 
						|
var
 | 
						|
  h, CurrTh, ThrHandle, CurrPr: DWORD;
 | 
						|
  Thread: TTHREADENTRY32;
 | 
						|
begin
 | 
						|
  CurrTh := GetCurrentThreadId;
 | 
						|
  CurrPr := GetCurrentProcessId;
 | 
						|
  h := CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
 | 
						|
  if h <> INVALID_HANDLE_VALUE then
 | 
						|
  begin
 | 
						|
    Thread.dwSize := SizeOf(TTHREADENTRY32);
 | 
						|
    if Thread32First(h, Thread) then
 | 
						|
      repeat
 | 
						|
        if (Thread.th32ThreadID <> CurrTh) and
 | 
						|
          (Thread.th32OwnerProcessID = CurrPr) then
 | 
						|
        begin
 | 
						|
          ThrHandle := OpenThread(THREAD_SUSPEND_RESUME, false,
 | 
						|
            Thread.th32ThreadID);
 | 
						|
          if ThrHandle > 0 then
 | 
						|
          begin
 | 
						|
            ResumeThread(ThrHandle);
 | 
						|
            CloseHandle(ThrHandle);
 | 
						|
          end;
 | 
						|
        end;
 | 
						|
      until not Thread32Next(h, Thread);
 | 
						|
      CloseHandle(h);
 | 
						|
  end;
 | 
						|
end;
 | 
						|
 | 
						|
function GetModuleAddress(ModuleName: String; ProcessId: DWORD; var BaseAddr: Pointer; var BaseSize: DWORD): Boolean;
 | 
						|
var
 | 
						|
  hSnap: THandle;
 | 
						|
  md: MODULEENTRY32;
 | 
						|
begin
 | 
						|
  Result := False;
 | 
						|
  hSnap := CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, ProcessId);
 | 
						|
  if hSnap = INVALID_HANDLE_VALUE Then
 | 
						|
    Exit;
 | 
						|
  md.dwSize := SizeOf(MODULEENTRY32);
 | 
						|
  if Module32First(hSnap, md) then
 | 
						|
  begin
 | 
						|
    if LowerCase(ExtractFileName(md.szExePath)) = LowerCase(ModuleName) then
 | 
						|
    begin
 | 
						|
      Result := True;
 | 
						|
      BaseAddr := Pointer(md.modBaseAddr);
 | 
						|
      BaseSize := md.modBaseSize;
 | 
						|
      CloseHandle(hSnap);
 | 
						|
      Exit;
 | 
						|
    end;
 | 
						|
    while Module32Next(hSnap, md) Do
 | 
						|
    begin
 | 
						|
      if LowerCase(ExtractFileName(md.szExePath)) = LowerCase(ModuleName) then
 | 
						|
      begin
 | 
						|
        Result := True;
 | 
						|
        BaseAddr := Pointer(md.modBaseAddr);
 | 
						|
        BaseSize := md.modBaseSize;
 | 
						|
        Break;
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
  end;
 | 
						|
  CloseHandle(hSnap);
 | 
						|
end;
 | 
						|
 | 
						|
{procedure FindMem(Mem: Pointer; MemSz: DWORD; Buf: Pointer; BufSz: DWORD;
 | 
						|
  From: DWORD; var A: IntArray);
 | 
						|
var
 | 
						|
  I: Integer;
 | 
						|
begin
 | 
						|
  SetLength(A, 0);
 | 
						|
  I:=From;
 | 
						|
  if From>0 then
 | 
						|
    Inc(PByte(Mem), From);
 | 
						|
  while I < MemSz - BufSz + 1 do
 | 
						|
  begin
 | 
						|
    if (not IsBadReadPtr(Mem, BufSz)) and (CompareMem(Mem, Buf, BufSz)) then
 | 
						|
    begin
 | 
						|
      SetLength(A, Length(A)+1);
 | 
						|
      A[Length(A)-1] := I;
 | 
						|
    end;
 | 
						|
    Inc(I);
 | 
						|
    Inc(PByte(Mem));
 | 
						|
  end;
 | 
						|
end;}
 | 
						|
 | 
						|
function GetModuleVersion(const ModuleName: String; var FileVersion: FILE_VERSION): Boolean;
 | 
						|
type
 | 
						|
  VS_VERSIONINFO = record
 | 
						|
    wLength, wValueLength, wType: Word;
 | 
						|
    szKey: Array[1..16] of WideChar;
 | 
						|
    Padding1: Word;
 | 
						|
    Value: VS_FIXEDFILEINFO;
 | 
						|
    Padding2, Children: Word;
 | 
						|
  end;
 | 
						|
  PVS_VERSIONINFO = ^VS_VERSIONINFO;
 | 
						|
const
 | 
						|
  VFF_DEBUG = 1;
 | 
						|
  VFF_PRERELEASE = 2;
 | 
						|
  VFF_PRIVATE = 8;
 | 
						|
  VFF_SPECIAL = 32;
 | 
						|
var
 | 
						|
  hMod: HMODULE;
 | 
						|
  hResourceInfo: HRSRC;
 | 
						|
  VersionInfo: PVS_VERSIONINFO;
 | 
						|
begin
 | 
						|
  Result := False;
 | 
						|
 | 
						|
  if ModuleName = '' then
 | 
						|
    hMod := GetModuleHandle(nil)
 | 
						|
  else
 | 
						|
    hMod := GetModuleHandle(PWideChar(ModuleName));
 | 
						|
  if hMod = 0 then
 | 
						|
    Exit;
 | 
						|
 | 
						|
  hResourceInfo := FindResource(hMod, PWideChar(1), PWideChar($10));
 | 
						|
  if hResourceInfo = 0 then
 | 
						|
    Exit;
 | 
						|
 | 
						|
  VersionInfo := Pointer(LoadResource(hMod, hResourceInfo));
 | 
						|
  if VersionInfo = nil then
 | 
						|
    Exit;
 | 
						|
 | 
						|
  FileVersion.Version.dw := VersionInfo.Value.dwFileVersionMS;
 | 
						|
  FileVersion.Release := Word(VersionInfo.Value.dwFileVersionLS shr 16);
 | 
						|
  FileVersion.Build := Word(VersionInfo.Value.dwFileVersionLS);
 | 
						|
  FileVersion.bDebug := (VersionInfo.Value.dwFileFlags and VFF_DEBUG) = VFF_DEBUG;
 | 
						|
  FileVersion.bPrerelease := (VersionInfo.Value.dwFileFlags and VFF_PRERELEASE) = VFF_PRERELEASE;
 | 
						|
  FileVersion.bPrivate := (VersionInfo.Value.dwFileFlags and VFF_PRIVATE) = VFF_PRIVATE;
 | 
						|
  FileVersion.bSpecial := (VersionInfo.Value.dwFileFlags and VFF_SPECIAL) = VFF_SPECIAL;
 | 
						|
 | 
						|
  Result := True;
 | 
						|
end;
 | 
						|
 | 
						|
function GetFileVersion(const FileName: String; var FileVersion: FILE_VERSION): Boolean;
 | 
						|
type
 | 
						|
  VS_VERSIONINFO = record
 | 
						|
    wLength, wValueLength, wType: Word;
 | 
						|
    szKey: Array[1..16] of WideChar;
 | 
						|
    Padding1: Word;
 | 
						|
    Value: VS_FIXEDFILEINFO;
 | 
						|
    Padding2, Children: Word;
 | 
						|
  end;
 | 
						|
  PVS_VERSIONINFO = ^VS_VERSIONINFO;
 | 
						|
const
 | 
						|
  VFF_DEBUG = 1;
 | 
						|
  VFF_PRERELEASE = 2;
 | 
						|
  VFF_PRIVATE = 8;
 | 
						|
  VFF_SPECIAL = 32;
 | 
						|
var
 | 
						|
  hFile: HMODULE;
 | 
						|
  hResourceInfo: HRSRC;
 | 
						|
  VersionInfo: PVS_VERSIONINFO;
 | 
						|
begin
 | 
						|
  Result := False;
 | 
						|
 | 
						|
  hFile := LoadLibraryEx(PWideChar(FileName), 0, LOAD_LIBRARY_AS_DATAFILE);
 | 
						|
  if hFile = 0 then
 | 
						|
    Exit;
 | 
						|
 | 
						|
  hResourceInfo := FindResource(hFile, PWideChar(1), PWideChar($10));
 | 
						|
  if hResourceInfo = 0 then
 | 
						|
    Exit;
 | 
						|
 | 
						|
  VersionInfo := Pointer(LoadResource(hFile, hResourceInfo));
 | 
						|
  if VersionInfo = nil then
 | 
						|
    Exit;
 | 
						|
 | 
						|
  FileVersion.Version.dw := VersionInfo.Value.dwFileVersionMS;
 | 
						|
  FileVersion.Release := Word(VersionInfo.Value.dwFileVersionLS shr 16);
 | 
						|
  FileVersion.Build := Word(VersionInfo.Value.dwFileVersionLS);
 | 
						|
  FileVersion.bDebug := (VersionInfo.Value.dwFileFlags and VFF_DEBUG) = VFF_DEBUG;
 | 
						|
  FileVersion.bPrerelease := (VersionInfo.Value.dwFileFlags and VFF_PRERELEASE) = VFF_PRERELEASE;
 | 
						|
  FileVersion.bPrivate := (VersionInfo.Value.dwFileFlags and VFF_PRIVATE) = VFF_PRIVATE;
 | 
						|
  FileVersion.bSpecial := (VersionInfo.Value.dwFileFlags and VFF_SPECIAL) = VFF_SPECIAL;
 | 
						|
 | 
						|
  Result := True;
 | 
						|
end;
 | 
						|
 | 
						|
function OverrideSL(ValueName: String; var Value: DWORD): Boolean;
 | 
						|
begin
 | 
						|
  Result := True;
 | 
						|
  if INIValueExists(INI, 'SLPolicy', ValueName) then begin
 | 
						|
    Value := INIReadDWord(INI, 'SLPolicy', ValueName, 0);
 | 
						|
    Exit;
 | 
						|
  end;
 | 
						|
  Result := False;
 | 
						|
end;
 | 
						|
 | 
						|
function New_SLGetWindowsInformationDWORD(pwszValueName: PWideChar;
 | 
						|
  pdwValue: PDWORD): HRESULT; stdcall;
 | 
						|
var
 | 
						|
  dw: DWORD;
 | 
						|
begin
 | 
						|
  // wrapped SLGetWindowsInformationDWORD function
 | 
						|
  // termsrv.dll will call this function instead of original SLC.dll
 | 
						|
 | 
						|
  // Override SL Policy
 | 
						|
 | 
						|
  WriteLog('Policy query: ' + pwszValueName);
 | 
						|
  if OverrideSL(pwszValueName, dw) then begin
 | 
						|
    pdwValue^ := dw;
 | 
						|
    Result := S_OK;
 | 
						|
    WriteLog('Policy rewrite: ' + IntToStr(pdwValue^));
 | 
						|
    Exit;
 | 
						|
  end;
 | 
						|
 | 
						|
  // If the requested value name is not defined above
 | 
						|
 | 
						|
  // revert to original SL Policy function
 | 
						|
  WriteProcessMemory(GetCurrentProcess, @SLGetWindowsInformationDWORD,
 | 
						|
    @Old_SLGetWindowsInformationDWORD, SizeOf(OldCode), bw);
 | 
						|
 | 
						|
  // get result
 | 
						|
  Result := SLGetWindowsInformationDWORD(pwszValueName, pdwValue);
 | 
						|
  if Result = S_OK then
 | 
						|
    WriteLog('Policy result: ' + IntToStr(pdwValue^))
 | 
						|
  else
 | 
						|
    WriteLog('Policy request failed');
 | 
						|
  // wrap it back
 | 
						|
  WriteProcessMemory(GetCurrentProcess, @SLGetWindowsInformationDWORD,
 | 
						|
    @Stub_SLGetWindowsInformationDWORD, SizeOf(far_jmp), bw);
 | 
						|
end;
 | 
						|
 | 
						|
function New_Win8SL(pwszValueName: PWideChar; pdwValue: PDWORD): HRESULT; register;
 | 
						|
var
 | 
						|
  dw: DWORD;
 | 
						|
begin
 | 
						|
  // wrapped unexported function SLGetWindowsInformationDWORDWrapper in termsrv.dll
 | 
						|
  // for Windows 8 support
 | 
						|
 | 
						|
  // Override SL Policy
 | 
						|
 | 
						|
  WriteLog('Policy query: ' + pwszValueName);
 | 
						|
  if OverrideSL(pwszValueName, dw) then begin
 | 
						|
    pdwValue^ := dw;
 | 
						|
    Result := S_OK;
 | 
						|
    WriteLog('Policy rewrite: ' + IntToStr(pdwValue^));
 | 
						|
    Exit;
 | 
						|
  end;
 | 
						|
 | 
						|
  // If the requested value name is not defined above
 | 
						|
  // use function from SLC.dll
 | 
						|
 | 
						|
  Result := SLGetWindowsInformationDWORD(pwszValueName, pdwValue);
 | 
						|
  if Result = S_OK then
 | 
						|
    WriteLog('Policy result: ' + IntToStr(pdwValue^))
 | 
						|
  else
 | 
						|
    WriteLog('Policy request failed');
 | 
						|
end;
 | 
						|
 | 
						|
function New_Win8SL_CP(eax: DWORD; pdwValue: PDWORD; ecx: DWORD; pwszValueName: PWideChar): HRESULT; register;
 | 
						|
begin
 | 
						|
  // wrapped unexported function SLGetWindowsInformationDWORDWrapper in termsrv.dll
 | 
						|
  // for Windows 8 Consumer Preview support
 | 
						|
 | 
						|
  Result := New_Win8SL(pwszValueName, pdwValue);
 | 
						|
end;
 | 
						|
 | 
						|
function New_CSLQuery_Initialize: HRESULT; stdcall;
 | 
						|
var
 | 
						|
  Sect: String;
 | 
						|
  bServerSku,
 | 
						|
  bRemoteConnAllowed,
 | 
						|
  bFUSEnabled,
 | 
						|
  bAppServerAllowed,
 | 
						|
  bMultimonAllowed,
 | 
						|
  lMaxUserSessions,
 | 
						|
  ulMaxDebugSessions,
 | 
						|
  bInitialized: PDWORD;
 | 
						|
begin
 | 
						|
  bServerSku := nil;
 | 
						|
  bRemoteConnAllowed := nil;
 | 
						|
  bFUSEnabled := nil;
 | 
						|
  bAppServerAllowed := nil;
 | 
						|
  bMultimonAllowed := nil;
 | 
						|
  lMaxUserSessions := nil;
 | 
						|
  ulMaxDebugSessions := nil;
 | 
						|
  bInitialized := nil;
 | 
						|
  WriteLog('>>> CSLQuery::Initialize');
 | 
						|
  Sect := IntToStr(FV.Version.w.Major)+'.'+IntToStr(FV.Version.w.Minor)+'.'+
 | 
						|
          IntToStr(FV.Release)+'.'+IntToStr(FV.Build)+'-SLInit';
 | 
						|
  if INISectionExists(INI, Sect) then begin
 | 
						|
    bServerSku := Pointer(Cardinal(TermSrvBase) + INIReadDWordHex(INI, Sect, 'bServerSku.x86', 0));
 | 
						|
    bRemoteConnAllowed := Pointer(Cardinal(TermSrvBase) + INIReadDWordHex(INI, Sect, 'bRemoteConnAllowed.x86', 0));
 | 
						|
    bFUSEnabled := Pointer(Cardinal(TermSrvBase) + INIReadDWordHex(INI, Sect, 'bFUSEnabled.x86', 0));
 | 
						|
    bAppServerAllowed := Pointer(Cardinal(TermSrvBase) + INIReadDWordHex(INI, Sect, 'bAppServerAllowed.x86', 0));
 | 
						|
    bMultimonAllowed := Pointer(Cardinal(TermSrvBase) + INIReadDWordHex(INI, Sect, 'bMultimonAllowed.x86', 0));
 | 
						|
    lMaxUserSessions := Pointer(Cardinal(TermSrvBase) + INIReadDWordHex(INI, Sect, 'lMaxUserSessions.x86', 0));
 | 
						|
    ulMaxDebugSessions := Pointer(Cardinal(TermSrvBase) + INIReadDWordHex(INI, Sect, 'ulMaxDebugSessions.x86', 0));
 | 
						|
    bInitialized := Pointer(Cardinal(TermSrvBase) + INIReadDWordHex(INI, Sect, 'bInitialized.x86', 0));
 | 
						|
  end;
 | 
						|
 | 
						|
  if bServerSku <> nil then begin
 | 
						|
    bServerSku^ := INIReadDWord(INI, 'SLInit', 'bServerSku', 1);
 | 
						|
    WriteLog('SLInit [0x'+IntToHex(DWORD(bServerSku), 1)+'] bServerSku = ' + IntToStr(bServerSku^));
 | 
						|
  end;
 | 
						|
  if bRemoteConnAllowed <> nil then begin
 | 
						|
    bRemoteConnAllowed^ := INIReadDWord(INI, 'SLInit', 'bRemoteConnAllowed', 1);
 | 
						|
    WriteLog('SLInit [0x'+IntToHex(DWORD(bRemoteConnAllowed), 1)+'] bRemoteConnAllowed = ' + IntToStr(bRemoteConnAllowed^));
 | 
						|
  end;
 | 
						|
  if bFUSEnabled <> nil then begin
 | 
						|
    bFUSEnabled^ := INIReadDWord(INI, 'SLInit', 'bFUSEnabled', 1);
 | 
						|
    WriteLog('SLInit [0x'+IntToHex(DWORD(bFUSEnabled), 1)+'] bFUSEnabled = ' + IntToStr(bFUSEnabled^));
 | 
						|
  end;
 | 
						|
  if bAppServerAllowed <> nil then begin
 | 
						|
    bAppServerAllowed^ := INIReadDWord(INI, 'SLInit', 'bAppServerAllowed', 1);
 | 
						|
    WriteLog('SLInit [0x'+IntToHex(DWORD(bAppServerAllowed), 1)+'] bAppServerAllowed = ' + IntToStr(bAppServerAllowed^));
 | 
						|
  end;
 | 
						|
  if bMultimonAllowed <> nil then begin
 | 
						|
    bMultimonAllowed^ := INIReadDWord(INI, 'SLInit', 'bMultimonAllowed', 1);
 | 
						|
    WriteLog('SLInit [0x'+IntToHex(DWORD(bMultimonAllowed), 1)+'] bMultimonAllowed = ' + IntToStr(bMultimonAllowed^));
 | 
						|
  end;
 | 
						|
  if lMaxUserSessions <> nil then begin
 | 
						|
    lMaxUserSessions^ := INIReadDWord(INI, 'SLInit', 'lMaxUserSessions', 0);
 | 
						|
    WriteLog('SLInit [0x'+IntToHex(DWORD(lMaxUserSessions), 1)+'] lMaxUserSessions = ' + IntToStr(lMaxUserSessions^));
 | 
						|
  end;
 | 
						|
  if ulMaxDebugSessions <> nil then begin
 | 
						|
    ulMaxDebugSessions^ := INIReadDWord(INI, 'SLInit', 'ulMaxDebugSessions', 0);
 | 
						|
    WriteLog('SLInit [0x'+IntToHex(DWORD(ulMaxDebugSessions), 1)+'] ulMaxDebugSessions = ' + IntToStr(ulMaxDebugSessions^));
 | 
						|
  end;
 | 
						|
  if bInitialized <> nil then begin
 | 
						|
    bInitialized^ := INIReadDWord(INI, 'SLInit', 'bInitialized', 1);
 | 
						|
    WriteLog('SLInit [0x'+IntToHex(DWORD(bInitialized), 1)+'] bInitialized = ' + IntToStr(bInitialized^));
 | 
						|
  end;
 | 
						|
  Result := S_OK;
 | 
						|
  WriteLog('<<< CSLQuery::Initialize');
 | 
						|
end;
 | 
						|
 | 
						|
procedure HookFunctions;
 | 
						|
var
 | 
						|
  ConfigFile, Sect, FuncName: String;
 | 
						|
  V: DWORD;
 | 
						|
  TS_Handle, SLC_Handle: THandle;
 | 
						|
  TermSrvSize: DWORD;
 | 
						|
  SignPtr: Pointer;
 | 
						|
  I: Integer;
 | 
						|
  PatchList: SList;
 | 
						|
  Patch: Array of TBytes;
 | 
						|
  Jump: far_jmp;
 | 
						|
  MovJump: mov_far_jmp;
 | 
						|
begin
 | 
						|
  { hook function ^^
 | 
						|
     (called once)   }
 | 
						|
  IsHooked := True;
 | 
						|
  TSMain := nil;
 | 
						|
  TSGlobals := nil;
 | 
						|
  SLGetWindowsInformationDWORD := nil;
 | 
						|
 | 
						|
  WriteLog('Loading configuration...');
 | 
						|
  ConfigFile := ExtractFilePath(GetBinaryPath) + 'rdpwrap.ini';
 | 
						|
  WriteLog('Configuration file: ' + ConfigFile);
 | 
						|
  INILoad(INI, ConfigFile);
 | 
						|
  if Length(INI) = 0 then begin
 | 
						|
    WriteLog('Error: Failed to load configuration');
 | 
						|
    Exit;
 | 
						|
  end;
 | 
						|
 | 
						|
  LogFile := INIReadString(INI, 'Main', 'LogFile', ExtractFilePath(GetBinaryPath) + 'rdpwrap.txt');
 | 
						|
  WriteLog('Initializing RDP Wrapper...');
 | 
						|
 | 
						|
  // load termsrv.dll and get functions
 | 
						|
  TS_Handle := LoadLibrary('termsrv.dll');
 | 
						|
  if TS_Handle = 0 then begin
 | 
						|
    WriteLog('Error: Failed to load Terminal Services library');
 | 
						|
    Exit;
 | 
						|
  end;
 | 
						|
  TSMain := GetProcAddress(TS_Handle, 'ServiceMain');
 | 
						|
  TSGlobals := GetProcAddress(TS_Handle, 'SvchostPushServiceGlobals');
 | 
						|
  WriteLog(
 | 
						|
    'Base addr:  0x' + IntToHex(TS_Handle, 8) + #13#10 +
 | 
						|
    'SvcMain:    termsrv.dll+0x' + IntToHex(Cardinal(@TSMain) - TS_Handle, 1) + #13#10 +
 | 
						|
    'SvcGlobals: termsrv.dll+0x' + IntToHex(Cardinal(@TSGlobals) - TS_Handle, 1)
 | 
						|
  );
 | 
						|
 | 
						|
  V := 0;
 | 
						|
  // check termsrv version
 | 
						|
  if GetModuleVersion('termsrv.dll', FV) then
 | 
						|
    V := Byte(FV.Version.w.Minor) or (Byte(FV.Version.w.Major) shl 8)
 | 
						|
  else begin
 | 
						|
    // check NT version
 | 
						|
    // V := GetVersion; // deprecated
 | 
						|
    // V := ((V and $FF) shl 8) or ((V and $FF00) shr 8);
 | 
						|
  end;
 | 
						|
  if V = 0 then begin
 | 
						|
    WriteLog('Error: Failed to detect Terminal Services version');
 | 
						|
    Exit;
 | 
						|
  end;
 | 
						|
 | 
						|
  WriteLog('Version:    '+
 | 
						|
  IntToStr(FV.Version.w.Major)+'.'+
 | 
						|
  IntToStr(FV.Version.w.Minor)+'.'+
 | 
						|
  IntToStr(FV.Release)+'.'+
 | 
						|
  IntToStr(FV.Build));
 | 
						|
 | 
						|
  // temporarily freeze threads
 | 
						|
  WriteLog('Freezing threads...');
 | 
						|
  StopThreads();
 | 
						|
 | 
						|
  WriteLog('Caching patch codes...');
 | 
						|
  PatchList := INIReadSection(INI, 'PatchCodes');
 | 
						|
  SetLength(Patch, Length(PatchList));
 | 
						|
  for I := 0 to Length(Patch) - 1 do begin
 | 
						|
    Patch[I] := INIReadBytes(INI, 'PatchCodes', PatchList[I]);
 | 
						|
    if Length(Patch[I]) > 16 then  // for security reasons
 | 
						|
      SetLength(Patch[I], 16);     // not more than 16 bytes
 | 
						|
  end;
 | 
						|
 | 
						|
  if (V = $0600) and (INIReadBool(INI, 'Main', 'SLPolicyHookNT60', True)) then begin
 | 
						|
    // Windows Vista
 | 
						|
    // uses SL Policy API (slc.dll)
 | 
						|
 | 
						|
    // load slc.dll and hook function
 | 
						|
    SLC_Handle := LoadLibrary('slc.dll');
 | 
						|
    SLGetWindowsInformationDWORD := GetProcAddress(SLC_Handle, 'SLGetWindowsInformationDWORD');
 | 
						|
 | 
						|
    if @SLGetWindowsInformationDWORD <> nil then
 | 
						|
    begin
 | 
						|
      // rewrite original function to call our function (make hook)
 | 
						|
 | 
						|
      WriteLog('Hook SLGetWindowsInformationDWORD');
 | 
						|
      Stub_SLGetWindowsInformationDWORD.PushOp := $68;
 | 
						|
      Stub_SLGetWindowsInformationDWORD.PushArg := @New_SLGetWindowsInformationDWORD;
 | 
						|
      Stub_SLGetWindowsInformationDWORD.RetOp := $C3;
 | 
						|
      ReadProcessMemory(GetCurrentProcess, @SLGetWindowsInformationDWORD,
 | 
						|
        @Old_SLGetWindowsInformationDWORD, SizeOf(OldCode), bw);
 | 
						|
      WriteProcessMemory(GetCurrentProcess, @SLGetWindowsInformationDWORD,
 | 
						|
        @Stub_SLGetWindowsInformationDWORD, SizeOf(far_jmp), bw);
 | 
						|
    end;
 | 
						|
  end;
 | 
						|
  if (V = $0601) and (INIReadBool(INI, 'Main', 'SLPolicyHookNT61', True)) then begin
 | 
						|
    // Windows 7
 | 
						|
    // uses SL Policy API (slc.dll)
 | 
						|
 | 
						|
    // load slc.dll and hook function
 | 
						|
    SLC_Handle := LoadLibrary('slc.dll');
 | 
						|
    SLGetWindowsInformationDWORD := GetProcAddress(SLC_Handle, 'SLGetWindowsInformationDWORD');
 | 
						|
 | 
						|
    if @SLGetWindowsInformationDWORD <> nil then
 | 
						|
    begin
 | 
						|
      // rewrite original function to call our function (make hook)
 | 
						|
 | 
						|
      WriteLog('Hook SLGetWindowsInformationDWORD');
 | 
						|
      Stub_SLGetWindowsInformationDWORD.PushOp := $68;
 | 
						|
      Stub_SLGetWindowsInformationDWORD.PushArg := @New_SLGetWindowsInformationDWORD;
 | 
						|
      Stub_SLGetWindowsInformationDWORD.RetOp := $C3;
 | 
						|
      ReadProcessMemory(GetCurrentProcess, @SLGetWindowsInformationDWORD,
 | 
						|
        @Old_SLGetWindowsInformationDWORD, SizeOf(OldCode), bw);
 | 
						|
      WriteProcessMemory(GetCurrentProcess, @SLGetWindowsInformationDWORD,
 | 
						|
        @Stub_SLGetWindowsInformationDWORD, SizeOf(far_jmp), bw);
 | 
						|
    end;
 | 
						|
  end;
 | 
						|
  if V = $0602 then begin
 | 
						|
    // Windows 8
 | 
						|
    // uses SL Policy internal unexported function
 | 
						|
 | 
						|
    // load slc.dll and get function
 | 
						|
    // (will be used on intercepting undefined values)
 | 
						|
    SLC_Handle := LoadLibrary('slc.dll');
 | 
						|
    SLGetWindowsInformationDWORD := GetProcAddress(SLC_Handle, 'SLGetWindowsInformationDWORD');
 | 
						|
  end;
 | 
						|
  if V = $0603 then begin
 | 
						|
    // Windows 8.1
 | 
						|
    // uses SL Policy internal inline code
 | 
						|
  end;
 | 
						|
  if V = $0604 then begin
 | 
						|
    // Windows 10
 | 
						|
    // uses SL Policy internal inline code
 | 
						|
  end;
 | 
						|
 | 
						|
  Sect := IntToStr(FV.Version.w.Major)+'.'+IntToStr(FV.Version.w.Minor)+'.'+
 | 
						|
          IntToStr(FV.Release)+'.'+IntToStr(FV.Build);
 | 
						|
 | 
						|
  if INISectionExists(INI, Sect) then
 | 
						|
    if GetModuleAddress('termsrv.dll', GetCurrentProcessId, TermSrvBase, TermSrvSize) then begin
 | 
						|
      if INIReadBool(INI, Sect, 'LocalOnlyPatch.x86', False) then begin
 | 
						|
        WriteLog('Patch CEnforcementCore::GetInstanceOfTSLicense');
 | 
						|
        SignPtr := Pointer(Cardinal(TermSrvBase) + INIReadDWordHex(INI, Sect, 'LocalOnlyOffset.x86', 0));
 | 
						|
        I := SListFind(PatchList, INIReadString(INI, Sect, 'LocalOnlyCode.x86', ''));
 | 
						|
        if I >= 0 then
 | 
						|
          WriteProcessMemory(GetCurrentProcess, SignPtr, @Patch[I][0], Length(Patch[I]), bw);
 | 
						|
      end;
 | 
						|
      if INIReadBool(INI, Sect, 'SingleUserPatch.x86', False) then begin
 | 
						|
        WriteLog('Patch CSessionArbitrationHelper::IsSingleSessionPerUserEnabled');
 | 
						|
        SignPtr := Pointer(Cardinal(TermSrvBase) + INIReadDWordHex(INI, Sect, 'SingleUserOffset.x86', 0));
 | 
						|
        I := SListFind(PatchList, INIReadString(INI, Sect, 'SingleUserCode.x86', ''));
 | 
						|
        if I >= 0 then
 | 
						|
          WriteProcessMemory(GetCurrentProcess, SignPtr, @Patch[I][0], Length(Patch[I]), bw);
 | 
						|
      end;
 | 
						|
      if INIReadBool(INI, Sect, 'DefPolicyPatch.x86', False) then begin
 | 
						|
        WriteLog('Patch CDefPolicy::Query');
 | 
						|
        SignPtr := Pointer(Cardinal(TermSrvBase) + INIReadDWordHex(INI, Sect, 'DefPolicyOffset.x86', 0));
 | 
						|
        I := SListFind(PatchList, INIReadString(INI, Sect, 'DefPolicyCode.x86', ''));
 | 
						|
        if I >= 0 then
 | 
						|
          WriteProcessMemory(GetCurrentProcess, SignPtr, @Patch[I][0], Length(Patch[I]), bw);
 | 
						|
      end;
 | 
						|
      if INIReadBool(INI, Sect, 'SLPolicyInternal.x86', False) then begin
 | 
						|
        WriteLog('Hook SLGetWindowsInformationDWORDWrapper');
 | 
						|
        SignPtr := Pointer(Cardinal(TermSrvBase) + INIReadDWordHex(INI, Sect, 'SLPolicyOffset.x86', 0));
 | 
						|
        MovJump.MovOp := $89;  // mov eax, ecx
 | 
						|
        MovJump.MovArg := $C8; // __msfastcall compatibility
 | 
						|
        MovJump.PushOp := $68;
 | 
						|
        MovJump.PushArg := @New_Win8SL;
 | 
						|
        MovJump.RetOp := $C3;
 | 
						|
        FuncName := INIReadString(INI, Sect, 'SLPolicyFunc.x86', 'New_Win8SL');
 | 
						|
        if FuncName = 'New_Win8SL' then
 | 
						|
          MovJump.PushArg := @New_Win8SL;
 | 
						|
        if FuncName = 'New_Win8SL_CP' then
 | 
						|
          MovJump.PushArg := @New_Win8SL_CP;
 | 
						|
        WriteProcessMemory(GetCurrentProcess, SignPtr,
 | 
						|
          @MovJump, SizeOf(mov_far_jmp), bw);
 | 
						|
      end;
 | 
						|
      if INIReadBool(INI, Sect, 'SLInitHook.x86', False) then begin
 | 
						|
        WriteLog('Hook CSLQuery::Initialize');
 | 
						|
        SignPtr := Pointer(Cardinal(TermSrvBase) + INIReadDWordHex(INI, Sect, 'SLInitOffset.x86', 0));
 | 
						|
        Jump.PushOp := $68;
 | 
						|
        Jump.PushArg := @New_CSLQuery_Initialize;
 | 
						|
        Jump.RetOp := $C3;
 | 
						|
        FuncName := INIReadString(INI, Sect, 'SLInitFunc.x86', 'New_CSLQuery_Initialize');
 | 
						|
        if FuncName = 'New_CSLQuery_Initialize' then
 | 
						|
          Jump.PushArg := @New_CSLQuery_Initialize;
 | 
						|
        WriteProcessMemory(GetCurrentProcess, SignPtr,
 | 
						|
          @Jump, SizeOf(far_jmp), bw);
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
 | 
						|
  // unfreeze threads
 | 
						|
  WriteLog('Resumimg threads...');
 | 
						|
  RunThreads();
 | 
						|
end;
 | 
						|
 | 
						|
function TermServiceMain(dwArgc: DWORD; lpszArgv: PWideChar): DWORD; stdcall;
 | 
						|
begin
 | 
						|
  // wrap ServiceMain function
 | 
						|
  WriteLog('>>> ServiceMain');
 | 
						|
  if not IsHooked then
 | 
						|
    HookFunctions;
 | 
						|
  Result := 0;
 | 
						|
  if @TSMain <> nil then
 | 
						|
    Result := TSMain(dwArgc, lpszArgv);
 | 
						|
  WriteLog('<<< ServiceMain');
 | 
						|
end;
 | 
						|
 | 
						|
function TermServiceGlobals(lpGlobalData: Pointer): DWORD; stdcall;
 | 
						|
begin
 | 
						|
  // wrap SvchostPushServiceGlobals function
 | 
						|
  WriteLog('>>> SvchostPushServiceGlobals');
 | 
						|
  if not IsHooked then
 | 
						|
    HookFunctions;
 | 
						|
  Result := 0;
 | 
						|
  if @TSGlobals <> nil then
 | 
						|
    Result := TSGlobals(lpGlobalData);
 | 
						|
  WriteLog('<<< SvchostPushServiceGlobals');
 | 
						|
end;
 | 
						|
 | 
						|
// export section
 | 
						|
 | 
						|
exports
 | 
						|
  TermServiceMain index 1 name 'ServiceMain',
 | 
						|
  TermServiceGlobals index 2 name 'SvchostPushServiceGlobals';
 | 
						|
 | 
						|
begin
 | 
						|
  // DllMain procedure is not used
 | 
						|
end. |