mirror of
https://github.com/stascorp/rdpwrap.git
synced 2024-11-27 16:07:23 +00:00
729 lines
23 KiB
ObjectPascal
729 lines
23 KiB
ObjectPascal
{
|
|
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: DWORD;
|
|
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('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('Result: ' + IntToStr(pdwValue^))
|
|
else
|
|
WriteLog('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('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('Result: ' + IntToStr(pdwValue^))
|
|
else
|
|
WriteLog('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('[0x'+IntToHex(DWORD(bServerSku), 1)+'] bServerSku = ' + IntToStr(bServerSku^));
|
|
end;
|
|
if bRemoteConnAllowed <> nil then begin
|
|
bRemoteConnAllowed^ := INIReadDWord(INI, 'SLInit', 'bRemoteConnAllowed', 1);
|
|
WriteLog('[0x'+IntToHex(DWORD(bRemoteConnAllowed), 1)+'] bRemoteConnAllowed = ' + IntToStr(bRemoteConnAllowed^));
|
|
end;
|
|
if bFUSEnabled <> nil then begin
|
|
bFUSEnabled^ := INIReadDWord(INI, 'SLInit', 'bFUSEnabled', 1);
|
|
WriteLog('[0x'+IntToHex(DWORD(bFUSEnabled), 1)+'] bFUSEnabled = ' + IntToStr(bFUSEnabled^));
|
|
end;
|
|
if bAppServerAllowed <> nil then begin
|
|
bAppServerAllowed^ := INIReadDWord(INI, 'SLInit', 'bAppServerAllowed', 1);
|
|
WriteLog('[0x'+IntToHex(DWORD(bAppServerAllowed), 1)+'] bAppServerAllowed = ' + IntToStr(bAppServerAllowed^));
|
|
end;
|
|
if bMultimonAllowed <> nil then begin
|
|
bMultimonAllowed^ := INIReadDWord(INI, 'SLInit', 'bMultimonAllowed', 1);
|
|
WriteLog('[0x'+IntToHex(DWORD(bMultimonAllowed), 1)+'] bMultimonAllowed = ' + IntToStr(bMultimonAllowed^));
|
|
end;
|
|
if lMaxUserSessions <> nil then begin
|
|
lMaxUserSessions^ := INIReadDWord(INI, 'SLInit', 'lMaxUserSessions', 0);
|
|
WriteLog('[0x'+IntToHex(DWORD(lMaxUserSessions), 1)+'] lMaxUserSessions = ' + IntToStr(lMaxUserSessions^));
|
|
end;
|
|
if ulMaxDebugSessions <> nil then begin
|
|
ulMaxDebugSessions^ := INIReadDWord(INI, 'SLInit', 'ulMaxDebugSessions', 0);
|
|
WriteLog('[0x'+IntToHex(DWORD(ulMaxDebugSessions), 1)+'] ulMaxDebugSessions = ' + IntToStr(ulMaxDebugSessions^));
|
|
end;
|
|
if bInitialized <> nil then begin
|
|
bInitialized^ := INIReadDWord(INI, 'SLInit', 'bInitialized', 1);
|
|
WriteLog('[0x'+IntToHex(DWORD(bInitialized), 1)+'] bInitialized = ' + IntToStr(bInitialized^));
|
|
end;
|
|
Result := S_OK;
|
|
end;
|
|
|
|
procedure HookFunctions;
|
|
var
|
|
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...');
|
|
INILoad(INI, ExtractFilePath(GetBinaryPath) + 'rdpwrap.ini');
|
|
if Length(INI) = 0 then begin
|
|
WriteLog('Error: Failed to load configuration');
|
|
Exit;
|
|
end;
|
|
|
|
LogFile := INIReadString(INI, 'Main', 'LogFile', ExtractFilePath(GetBinaryPath) + 'rdpwrap.txt');
|
|
WriteLog('init');
|
|
|
|
// 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;
|
|
WriteLog('Base addr: 0x'+IntToHex(TS_Handle, 8));
|
|
TSMain := GetProcAddress(TS_Handle, 'ServiceMain');
|
|
WriteLog('SvcMain: termsrv.dll+0x'+IntToHex(Cardinal(@TSMain) - TS_Handle, 1));
|
|
TSGlobals := GetProcAddress(TS_Handle, 'SvchostPushServiceGlobals');
|
|
WriteLog('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));
|
|
WriteLog('Release: '+IntToStr(FV.Release));
|
|
WriteLog('Build: '+IntToStr(FV.Build));
|
|
|
|
// temporarily freeze threads
|
|
WriteLog('freeze');
|
|
StopThreads();
|
|
|
|
WriteLog('Loading 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('resume');
|
|
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);
|
|
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);
|
|
end;
|
|
|
|
// export section
|
|
|
|
exports
|
|
TermServiceMain index 1 name 'ServiceMain';
|
|
exports
|
|
TermServiceGlobals index 2 name 'SvchostPushServiceGlobals';
|
|
|
|
begin
|
|
// DllMain procedure is not used
|
|
end. |