rdpwrap.dpr 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737
  1. {
  2. Copyright 2014 Stas'M Corp.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. }
  13. library rdpwrap;
  14. uses
  15. SysUtils,
  16. Windows,
  17. TlHelp32,
  18. LiteINI;
  19. {$R rdpwrap.res}
  20. // Hook core definitions
  21. type
  22. OldCode = packed record
  23. One: DWORD;
  24. two: Word;
  25. end;
  26. far_jmp = packed record
  27. PushOp: Byte;
  28. PushArg: Pointer;
  29. RetOp: Byte;
  30. end;
  31. mov_far_jmp = packed record
  32. MovOp: Byte;
  33. MovArg: Byte;
  34. PushOp: Byte;
  35. PushArg: Pointer;
  36. RetOp: Byte;
  37. end;
  38. TTHREADENTRY32 = packed record
  39. dwSize: DWORD;
  40. cntUsage: DWORD;
  41. th32ThreadID: DWORD;
  42. th32OwnerProcessID: DWORD;
  43. tpBasePri: LongInt;
  44. tpDeltaPri: LongInt;
  45. dwFlags: DWORD;
  46. end;
  47. //IntArray = Array of Integer;
  48. FILE_VERSION = record
  49. Version: record case Boolean of
  50. True: (dw: DWORD);
  51. False: (w: record
  52. Minor, Major: Word;
  53. end;)
  54. end;
  55. Release, Build: Word;
  56. bDebug, bPrerelease, bPrivate, bSpecial: Boolean;
  57. end;
  58. const
  59. THREAD_SUSPEND_RESUME = 2;
  60. TH32CS_SNAPTHREAD = 4;
  61. var
  62. INI: INIFile;
  63. LogFile: String = '\rdpwrap.txt';
  64. bw: {$if CompilerVersion>=16} NativeUInt {$else} DWORD {$endif};
  65. IsHooked: Boolean = False;
  66. // Unhooked import
  67. function OpenThread(dwDesiredAccess: DWORD; bInheritHandle: BOOL;
  68. dwThreadId: DWORD): DWORD; stdcall; external kernel32;
  69. function CreateToolhelp32Snapshot(dwFlags, th32ProcessID: DWORD): DWORD;
  70. stdcall; external kernel32;
  71. function Thread32First(hSnapshot: THandle; var lpte: TTHREADENTRY32): bool;
  72. stdcall; external kernel32;
  73. function Thread32Next(hSnapshot: THandle; var lpte: TTHREADENTRY32): bool;
  74. stdcall; external kernel32;
  75. // Wrapped import
  76. var
  77. TSMain: function(dwArgc: DWORD; lpszArgv: PWideChar): DWORD; stdcall;
  78. TSGlobals: function(lpGlobalData: Pointer): DWORD; stdcall;
  79. // Hooked import and vars
  80. var
  81. SLGetWindowsInformationDWORD: function(pwszValueName: PWideChar;
  82. pdwValue: PDWORD): HRESULT; stdcall;
  83. TermSrvBase: Pointer;
  84. FV: FILE_VERSION;
  85. var
  86. Stub_SLGetWindowsInformationDWORD: far_jmp;
  87. Old_SLGetWindowsInformationDWORD: OldCode;
  88. // Main code
  89. procedure WriteLog(S: AnsiString);
  90. var
  91. F: TextFile;
  92. begin
  93. if not FileExists(LogFile) then
  94. Exit;
  95. AssignFile(F, LogFile);
  96. Append(F);
  97. Write(F, S+#13#10);
  98. CloseFile(F);
  99. end;
  100. function GetModuleHandleEx(dwFlags: DWORD; lpModuleName: PWideChar;
  101. var phModule: HMODULE): BOOL; stdcall; external kernel32 name 'GetModuleHandleExW';
  102. function GetCurrentModule: HMODULE;
  103. const
  104. GET_MODULE_HANDLE_EX_FLAG_PIN = 1;
  105. GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT = 2;
  106. GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS = 4;
  107. begin
  108. Result := 0;
  109. GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, @GetCurrentModule, Result);
  110. end;
  111. function GetBinaryPath: String;
  112. var
  113. Buf: Array[0..511] of Byte;
  114. begin
  115. ZeroMemory(@Buf[0], Length(Buf));
  116. GetModuleFileName(GetCurrentModule, PWideChar(@Buf[0]), Length(Buf));
  117. Result := PWideChar(@Buf[0]);
  118. end;
  119. procedure StopThreads;
  120. var
  121. h, CurrTh, ThrHandle, CurrPr: DWORD;
  122. Thread: TTHREADENTRY32;
  123. begin
  124. CurrTh := GetCurrentThreadId;
  125. CurrPr := GetCurrentProcessId;
  126. h := CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
  127. if h <> INVALID_HANDLE_VALUE then
  128. begin
  129. Thread.dwSize := SizeOf(TTHREADENTRY32);
  130. if Thread32First(h, Thread) then
  131. repeat
  132. if (Thread.th32ThreadID <> CurrTh) and
  133. (Thread.th32OwnerProcessID = CurrPr) then
  134. begin
  135. ThrHandle := OpenThread(THREAD_SUSPEND_RESUME, false,
  136. Thread.th32ThreadID);
  137. if ThrHandle > 0 then
  138. begin
  139. SuspendThread(ThrHandle);
  140. CloseHandle(ThrHandle);
  141. end;
  142. end;
  143. until not Thread32Next(h, Thread);
  144. CloseHandle(h);
  145. end;
  146. end;
  147. procedure RunThreads;
  148. var
  149. h, CurrTh, ThrHandle, CurrPr: DWORD;
  150. Thread: TTHREADENTRY32;
  151. begin
  152. CurrTh := GetCurrentThreadId;
  153. CurrPr := GetCurrentProcessId;
  154. h := CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
  155. if h <> INVALID_HANDLE_VALUE then
  156. begin
  157. Thread.dwSize := SizeOf(TTHREADENTRY32);
  158. if Thread32First(h, Thread) then
  159. repeat
  160. if (Thread.th32ThreadID <> CurrTh) and
  161. (Thread.th32OwnerProcessID = CurrPr) then
  162. begin
  163. ThrHandle := OpenThread(THREAD_SUSPEND_RESUME, false,
  164. Thread.th32ThreadID);
  165. if ThrHandle > 0 then
  166. begin
  167. ResumeThread(ThrHandle);
  168. CloseHandle(ThrHandle);
  169. end;
  170. end;
  171. until not Thread32Next(h, Thread);
  172. CloseHandle(h);
  173. end;
  174. end;
  175. function GetModuleAddress(ModuleName: String; ProcessId: DWORD; var BaseAddr: Pointer; var BaseSize: DWORD): Boolean;
  176. var
  177. hSnap: THandle;
  178. md: MODULEENTRY32;
  179. begin
  180. Result := False;
  181. hSnap := CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, ProcessId);
  182. if hSnap = INVALID_HANDLE_VALUE Then
  183. Exit;
  184. md.dwSize := SizeOf(MODULEENTRY32);
  185. if Module32First(hSnap, md) then
  186. begin
  187. if LowerCase(ExtractFileName(md.szExePath)) = LowerCase(ModuleName) then
  188. begin
  189. Result := True;
  190. BaseAddr := Pointer(md.modBaseAddr);
  191. BaseSize := md.modBaseSize;
  192. CloseHandle(hSnap);
  193. Exit;
  194. end;
  195. while Module32Next(hSnap, md) Do
  196. begin
  197. if LowerCase(ExtractFileName(md.szExePath)) = LowerCase(ModuleName) then
  198. begin
  199. Result := True;
  200. BaseAddr := Pointer(md.modBaseAddr);
  201. BaseSize := md.modBaseSize;
  202. Break;
  203. end;
  204. end;
  205. end;
  206. CloseHandle(hSnap);
  207. end;
  208. {procedure FindMem(Mem: Pointer; MemSz: DWORD; Buf: Pointer; BufSz: DWORD;
  209. From: DWORD; var A: IntArray);
  210. var
  211. I: Integer;
  212. begin
  213. SetLength(A, 0);
  214. I:=From;
  215. if From>0 then
  216. Inc(PByte(Mem), From);
  217. while I < MemSz - BufSz + 1 do
  218. begin
  219. if (not IsBadReadPtr(Mem, BufSz)) and (CompareMem(Mem, Buf, BufSz)) then
  220. begin
  221. SetLength(A, Length(A)+1);
  222. A[Length(A)-1] := I;
  223. end;
  224. Inc(I);
  225. Inc(PByte(Mem));
  226. end;
  227. end;}
  228. function GetModuleVersion(const ModuleName: String; var FileVersion: FILE_VERSION): Boolean;
  229. type
  230. VS_VERSIONINFO = record
  231. wLength, wValueLength, wType: Word;
  232. szKey: Array[1..16] of WideChar;
  233. Padding1: Word;
  234. Value: VS_FIXEDFILEINFO;
  235. Padding2, Children: Word;
  236. end;
  237. PVS_VERSIONINFO = ^VS_VERSIONINFO;
  238. const
  239. VFF_DEBUG = 1;
  240. VFF_PRERELEASE = 2;
  241. VFF_PRIVATE = 8;
  242. VFF_SPECIAL = 32;
  243. var
  244. hMod: HMODULE;
  245. hResourceInfo: HRSRC;
  246. VersionInfo: PVS_VERSIONINFO;
  247. begin
  248. Result := False;
  249. if ModuleName = '' then
  250. hMod := GetModuleHandle(nil)
  251. else
  252. hMod := GetModuleHandle(PWideChar(ModuleName));
  253. if hMod = 0 then
  254. Exit;
  255. hResourceInfo := FindResource(hMod, PWideChar(1), PWideChar($10));
  256. if hResourceInfo = 0 then
  257. Exit;
  258. VersionInfo := Pointer(LoadResource(hMod, hResourceInfo));
  259. if VersionInfo = nil then
  260. Exit;
  261. FileVersion.Version.dw := VersionInfo.Value.dwFileVersionMS;
  262. FileVersion.Release := Word(VersionInfo.Value.dwFileVersionLS shr 16);
  263. FileVersion.Build := Word(VersionInfo.Value.dwFileVersionLS);
  264. FileVersion.bDebug := (VersionInfo.Value.dwFileFlags and VFF_DEBUG) = VFF_DEBUG;
  265. FileVersion.bPrerelease := (VersionInfo.Value.dwFileFlags and VFF_PRERELEASE) = VFF_PRERELEASE;
  266. FileVersion.bPrivate := (VersionInfo.Value.dwFileFlags and VFF_PRIVATE) = VFF_PRIVATE;
  267. FileVersion.bSpecial := (VersionInfo.Value.dwFileFlags and VFF_SPECIAL) = VFF_SPECIAL;
  268. Result := True;
  269. end;
  270. function GetFileVersion(const FileName: String; var FileVersion: FILE_VERSION): Boolean;
  271. type
  272. VS_VERSIONINFO = record
  273. wLength, wValueLength, wType: Word;
  274. szKey: Array[1..16] of WideChar;
  275. Padding1: Word;
  276. Value: VS_FIXEDFILEINFO;
  277. Padding2, Children: Word;
  278. end;
  279. PVS_VERSIONINFO = ^VS_VERSIONINFO;
  280. const
  281. VFF_DEBUG = 1;
  282. VFF_PRERELEASE = 2;
  283. VFF_PRIVATE = 8;
  284. VFF_SPECIAL = 32;
  285. var
  286. hFile: HMODULE;
  287. hResourceInfo: HRSRC;
  288. VersionInfo: PVS_VERSIONINFO;
  289. begin
  290. Result := False;
  291. hFile := LoadLibraryEx(PWideChar(FileName), 0, LOAD_LIBRARY_AS_DATAFILE);
  292. if hFile = 0 then
  293. Exit;
  294. hResourceInfo := FindResource(hFile, PWideChar(1), PWideChar($10));
  295. if hResourceInfo = 0 then
  296. Exit;
  297. VersionInfo := Pointer(LoadResource(hFile, hResourceInfo));
  298. if VersionInfo = nil then
  299. Exit;
  300. FileVersion.Version.dw := VersionInfo.Value.dwFileVersionMS;
  301. FileVersion.Release := Word(VersionInfo.Value.dwFileVersionLS shr 16);
  302. FileVersion.Build := Word(VersionInfo.Value.dwFileVersionLS);
  303. FileVersion.bDebug := (VersionInfo.Value.dwFileFlags and VFF_DEBUG) = VFF_DEBUG;
  304. FileVersion.bPrerelease := (VersionInfo.Value.dwFileFlags and VFF_PRERELEASE) = VFF_PRERELEASE;
  305. FileVersion.bPrivate := (VersionInfo.Value.dwFileFlags and VFF_PRIVATE) = VFF_PRIVATE;
  306. FileVersion.bSpecial := (VersionInfo.Value.dwFileFlags and VFF_SPECIAL) = VFF_SPECIAL;
  307. Result := True;
  308. end;
  309. function OverrideSL(ValueName: String; var Value: DWORD): Boolean;
  310. begin
  311. Result := True;
  312. if INIValueExists(INI, 'SLPolicy', ValueName) then begin
  313. Value := INIReadDWord(INI, 'SLPolicy', ValueName, 0);
  314. Exit;
  315. end;
  316. Result := False;
  317. end;
  318. function New_SLGetWindowsInformationDWORD(pwszValueName: PWideChar;
  319. pdwValue: PDWORD): HRESULT; stdcall;
  320. var
  321. dw: DWORD;
  322. begin
  323. // wrapped SLGetWindowsInformationDWORD function
  324. // termsrv.dll will call this function instead of original SLC.dll
  325. // Override SL Policy
  326. WriteLog('Policy query: ' + pwszValueName);
  327. if OverrideSL(pwszValueName, dw) then begin
  328. pdwValue^ := dw;
  329. Result := S_OK;
  330. WriteLog('Policy rewrite: ' + IntToStr(pdwValue^));
  331. Exit;
  332. end;
  333. // If the requested value name is not defined above
  334. // revert to original SL Policy function
  335. WriteProcessMemory(GetCurrentProcess, @SLGetWindowsInformationDWORD,
  336. @Old_SLGetWindowsInformationDWORD, SizeOf(OldCode), bw);
  337. // get result
  338. Result := SLGetWindowsInformationDWORD(pwszValueName, pdwValue);
  339. if Result = S_OK then
  340. WriteLog('Policy result: ' + IntToStr(pdwValue^))
  341. else
  342. WriteLog('Policy request failed');
  343. // wrap it back
  344. WriteProcessMemory(GetCurrentProcess, @SLGetWindowsInformationDWORD,
  345. @Stub_SLGetWindowsInformationDWORD, SizeOf(far_jmp), bw);
  346. end;
  347. function New_Win8SL(pwszValueName: PWideChar; pdwValue: PDWORD): HRESULT; register;
  348. var
  349. dw: DWORD;
  350. begin
  351. // wrapped unexported function SLGetWindowsInformationDWORDWrapper in termsrv.dll
  352. // for Windows 8 support
  353. // Override SL Policy
  354. WriteLog('Policy query: ' + pwszValueName);
  355. if OverrideSL(pwszValueName, dw) then begin
  356. pdwValue^ := dw;
  357. Result := S_OK;
  358. WriteLog('Policy rewrite: ' + IntToStr(pdwValue^));
  359. Exit;
  360. end;
  361. // If the requested value name is not defined above
  362. // use function from SLC.dll
  363. Result := SLGetWindowsInformationDWORD(pwszValueName, pdwValue);
  364. if Result = S_OK then
  365. WriteLog('Policy result: ' + IntToStr(pdwValue^))
  366. else
  367. WriteLog('Policy request failed');
  368. end;
  369. function New_Win8SL_CP(eax: DWORD; pdwValue: PDWORD; ecx: DWORD; pwszValueName: PWideChar): HRESULT; register;
  370. begin
  371. // wrapped unexported function SLGetWindowsInformationDWORDWrapper in termsrv.dll
  372. // for Windows 8 Consumer Preview support
  373. Result := New_Win8SL(pwszValueName, pdwValue);
  374. end;
  375. function New_CSLQuery_Initialize: HRESULT; stdcall;
  376. var
  377. Sect: String;
  378. bServerSku,
  379. bRemoteConnAllowed,
  380. bFUSEnabled,
  381. bAppServerAllowed,
  382. bMultimonAllowed,
  383. lMaxUserSessions,
  384. ulMaxDebugSessions,
  385. bInitialized: PDWORD;
  386. begin
  387. bServerSku := nil;
  388. bRemoteConnAllowed := nil;
  389. bFUSEnabled := nil;
  390. bAppServerAllowed := nil;
  391. bMultimonAllowed := nil;
  392. lMaxUserSessions := nil;
  393. ulMaxDebugSessions := nil;
  394. bInitialized := nil;
  395. WriteLog('>>> CSLQuery::Initialize');
  396. Sect := IntToStr(FV.Version.w.Major)+'.'+IntToStr(FV.Version.w.Minor)+'.'+
  397. IntToStr(FV.Release)+'.'+IntToStr(FV.Build)+'-SLInit';
  398. if INISectionExists(INI, Sect) then begin
  399. bServerSku := Pointer(Cardinal(TermSrvBase) + INIReadDWordHex(INI, Sect, 'bServerSku.x86', 0));
  400. bRemoteConnAllowed := Pointer(Cardinal(TermSrvBase) + INIReadDWordHex(INI, Sect, 'bRemoteConnAllowed.x86', 0));
  401. bFUSEnabled := Pointer(Cardinal(TermSrvBase) + INIReadDWordHex(INI, Sect, 'bFUSEnabled.x86', 0));
  402. bAppServerAllowed := Pointer(Cardinal(TermSrvBase) + INIReadDWordHex(INI, Sect, 'bAppServerAllowed.x86', 0));
  403. bMultimonAllowed := Pointer(Cardinal(TermSrvBase) + INIReadDWordHex(INI, Sect, 'bMultimonAllowed.x86', 0));
  404. lMaxUserSessions := Pointer(Cardinal(TermSrvBase) + INIReadDWordHex(INI, Sect, 'lMaxUserSessions.x86', 0));
  405. ulMaxDebugSessions := Pointer(Cardinal(TermSrvBase) + INIReadDWordHex(INI, Sect, 'ulMaxDebugSessions.x86', 0));
  406. bInitialized := Pointer(Cardinal(TermSrvBase) + INIReadDWordHex(INI, Sect, 'bInitialized.x86', 0));
  407. end;
  408. if bServerSku <> nil then begin
  409. bServerSku^ := INIReadDWord(INI, 'SLInit', 'bServerSku', 1);
  410. WriteLog('SLInit [0x'+IntToHex(DWORD(bServerSku), 1)+'] bServerSku = ' + IntToStr(bServerSku^));
  411. end;
  412. if bRemoteConnAllowed <> nil then begin
  413. bRemoteConnAllowed^ := INIReadDWord(INI, 'SLInit', 'bRemoteConnAllowed', 1);
  414. WriteLog('SLInit [0x'+IntToHex(DWORD(bRemoteConnAllowed), 1)+'] bRemoteConnAllowed = ' + IntToStr(bRemoteConnAllowed^));
  415. end;
  416. if bFUSEnabled <> nil then begin
  417. bFUSEnabled^ := INIReadDWord(INI, 'SLInit', 'bFUSEnabled', 1);
  418. WriteLog('SLInit [0x'+IntToHex(DWORD(bFUSEnabled), 1)+'] bFUSEnabled = ' + IntToStr(bFUSEnabled^));
  419. end;
  420. if bAppServerAllowed <> nil then begin
  421. bAppServerAllowed^ := INIReadDWord(INI, 'SLInit', 'bAppServerAllowed', 1);
  422. WriteLog('SLInit [0x'+IntToHex(DWORD(bAppServerAllowed), 1)+'] bAppServerAllowed = ' + IntToStr(bAppServerAllowed^));
  423. end;
  424. if bMultimonAllowed <> nil then begin
  425. bMultimonAllowed^ := INIReadDWord(INI, 'SLInit', 'bMultimonAllowed', 1);
  426. WriteLog('SLInit [0x'+IntToHex(DWORD(bMultimonAllowed), 1)+'] bMultimonAllowed = ' + IntToStr(bMultimonAllowed^));
  427. end;
  428. if lMaxUserSessions <> nil then begin
  429. lMaxUserSessions^ := INIReadDWord(INI, 'SLInit', 'lMaxUserSessions', 0);
  430. WriteLog('SLInit [0x'+IntToHex(DWORD(lMaxUserSessions), 1)+'] lMaxUserSessions = ' + IntToStr(lMaxUserSessions^));
  431. end;
  432. if ulMaxDebugSessions <> nil then begin
  433. ulMaxDebugSessions^ := INIReadDWord(INI, 'SLInit', 'ulMaxDebugSessions', 0);
  434. WriteLog('SLInit [0x'+IntToHex(DWORD(ulMaxDebugSessions), 1)+'] ulMaxDebugSessions = ' + IntToStr(ulMaxDebugSessions^));
  435. end;
  436. if bInitialized <> nil then begin
  437. bInitialized^ := INIReadDWord(INI, 'SLInit', 'bInitialized', 1);
  438. WriteLog('SLInit [0x'+IntToHex(DWORD(bInitialized), 1)+'] bInitialized = ' + IntToStr(bInitialized^));
  439. end;
  440. Result := S_OK;
  441. WriteLog('<<< CSLQuery::Initialize');
  442. end;
  443. procedure HookFunctions;
  444. var
  445. ConfigFile, Sect, FuncName: String;
  446. V: DWORD;
  447. TS_Handle, SLC_Handle: THandle;
  448. TermSrvSize: DWORD;
  449. SignPtr: Pointer;
  450. I: Integer;
  451. PatchList: SList;
  452. Patch: Array of TBytes;
  453. Jump: far_jmp;
  454. MovJump: mov_far_jmp;
  455. begin
  456. { hook function ^^
  457. (called once) }
  458. IsHooked := True;
  459. TSMain := nil;
  460. TSGlobals := nil;
  461. SLGetWindowsInformationDWORD := nil;
  462. WriteLog('Loading configuration...');
  463. ConfigFile := ExtractFilePath(GetBinaryPath) + 'rdpwrap.ini';
  464. WriteLog('Configuration file: ' + ConfigFile);
  465. INILoad(INI, ConfigFile);
  466. if Length(INI) = 0 then begin
  467. WriteLog('Error: Failed to load configuration');
  468. Exit;
  469. end;
  470. LogFile := INIReadString(INI, 'Main', 'LogFile', ExtractFilePath(GetBinaryPath) + 'rdpwrap.txt');
  471. WriteLog('Initializing RDP Wrapper...');
  472. // load termsrv.dll and get functions
  473. TS_Handle := LoadLibrary('termsrv.dll');
  474. if TS_Handle = 0 then begin
  475. WriteLog('Error: Failed to load Terminal Services library');
  476. Exit;
  477. end;
  478. TSMain := GetProcAddress(TS_Handle, 'ServiceMain');
  479. TSGlobals := GetProcAddress(TS_Handle, 'SvchostPushServiceGlobals');
  480. WriteLog(
  481. 'Base addr: 0x' + IntToHex(TS_Handle, 8) + #13#10 +
  482. 'SvcMain: termsrv.dll+0x' + IntToHex(Cardinal(@TSMain) - TS_Handle, 1) + #13#10 +
  483. 'SvcGlobals: termsrv.dll+0x' + IntToHex(Cardinal(@TSGlobals) - TS_Handle, 1)
  484. );
  485. V := 0;
  486. // check termsrv version
  487. if GetModuleVersion('termsrv.dll', FV) then
  488. V := Byte(FV.Version.w.Minor) or (Byte(FV.Version.w.Major) shl 8)
  489. else begin
  490. // check NT version
  491. // V := GetVersion; // deprecated
  492. // V := ((V and $FF) shl 8) or ((V and $FF00) shr 8);
  493. end;
  494. if V = 0 then begin
  495. WriteLog('Error: Failed to detect Terminal Services version');
  496. Exit;
  497. end;
  498. WriteLog('Version: '+
  499. IntToStr(FV.Version.w.Major)+'.'+
  500. IntToStr(FV.Version.w.Minor)+'.'+
  501. IntToStr(FV.Release)+'.'+
  502. IntToStr(FV.Build));
  503. // temporarily freeze threads
  504. WriteLog('Freezing threads...');
  505. StopThreads();
  506. WriteLog('Caching patch codes...');
  507. PatchList := INIReadSection(INI, 'PatchCodes');
  508. SetLength(Patch, Length(PatchList));
  509. for I := 0 to Length(Patch) - 1 do begin
  510. Patch[I] := INIReadBytes(INI, 'PatchCodes', PatchList[I]);
  511. if Length(Patch[I]) > 16 then // for security reasons
  512. SetLength(Patch[I], 16); // not more than 16 bytes
  513. end;
  514. if (V = $0600) and (INIReadBool(INI, 'Main', 'SLPolicyHookNT60', True)) then begin
  515. // Windows Vista
  516. // uses SL Policy API (slc.dll)
  517. // load slc.dll and hook function
  518. SLC_Handle := LoadLibrary('slc.dll');
  519. SLGetWindowsInformationDWORD := GetProcAddress(SLC_Handle, 'SLGetWindowsInformationDWORD');
  520. if @SLGetWindowsInformationDWORD <> nil then
  521. begin
  522. // rewrite original function to call our function (make hook)
  523. WriteLog('Hook SLGetWindowsInformationDWORD');
  524. Stub_SLGetWindowsInformationDWORD.PushOp := $68;
  525. Stub_SLGetWindowsInformationDWORD.PushArg := @New_SLGetWindowsInformationDWORD;
  526. Stub_SLGetWindowsInformationDWORD.RetOp := $C3;
  527. ReadProcessMemory(GetCurrentProcess, @SLGetWindowsInformationDWORD,
  528. @Old_SLGetWindowsInformationDWORD, SizeOf(OldCode), bw);
  529. WriteProcessMemory(GetCurrentProcess, @SLGetWindowsInformationDWORD,
  530. @Stub_SLGetWindowsInformationDWORD, SizeOf(far_jmp), bw);
  531. end;
  532. end;
  533. if (V = $0601) and (INIReadBool(INI, 'Main', 'SLPolicyHookNT61', True)) then begin
  534. // Windows 7
  535. // uses SL Policy API (slc.dll)
  536. // load slc.dll and hook function
  537. SLC_Handle := LoadLibrary('slc.dll');
  538. SLGetWindowsInformationDWORD := GetProcAddress(SLC_Handle, 'SLGetWindowsInformationDWORD');
  539. if @SLGetWindowsInformationDWORD <> nil then
  540. begin
  541. // rewrite original function to call our function (make hook)
  542. WriteLog('Hook SLGetWindowsInformationDWORD');
  543. Stub_SLGetWindowsInformationDWORD.PushOp := $68;
  544. Stub_SLGetWindowsInformationDWORD.PushArg := @New_SLGetWindowsInformationDWORD;
  545. Stub_SLGetWindowsInformationDWORD.RetOp := $C3;
  546. ReadProcessMemory(GetCurrentProcess, @SLGetWindowsInformationDWORD,
  547. @Old_SLGetWindowsInformationDWORD, SizeOf(OldCode), bw);
  548. WriteProcessMemory(GetCurrentProcess, @SLGetWindowsInformationDWORD,
  549. @Stub_SLGetWindowsInformationDWORD, SizeOf(far_jmp), bw);
  550. end;
  551. end;
  552. if V = $0602 then begin
  553. // Windows 8
  554. // uses SL Policy internal unexported function
  555. // load slc.dll and get function
  556. // (will be used on intercepting undefined values)
  557. SLC_Handle := LoadLibrary('slc.dll');
  558. SLGetWindowsInformationDWORD := GetProcAddress(SLC_Handle, 'SLGetWindowsInformationDWORD');
  559. end;
  560. if V = $0603 then begin
  561. // Windows 8.1
  562. // uses SL Policy internal inline code
  563. end;
  564. if V = $0604 then begin
  565. // Windows 10
  566. // uses SL Policy internal inline code
  567. end;
  568. Sect := IntToStr(FV.Version.w.Major)+'.'+IntToStr(FV.Version.w.Minor)+'.'+
  569. IntToStr(FV.Release)+'.'+IntToStr(FV.Build);
  570. if INISectionExists(INI, Sect) then
  571. if GetModuleAddress('termsrv.dll', GetCurrentProcessId, TermSrvBase, TermSrvSize) then begin
  572. if INIReadBool(INI, Sect, 'LocalOnlyPatch.x86', False) then begin
  573. WriteLog('Patch CEnforcementCore::GetInstanceOfTSLicense');
  574. SignPtr := Pointer(Cardinal(TermSrvBase) + INIReadDWordHex(INI, Sect, 'LocalOnlyOffset.x86', 0));
  575. I := SListFind(PatchList, INIReadString(INI, Sect, 'LocalOnlyCode.x86', ''));
  576. if I >= 0 then
  577. WriteProcessMemory(GetCurrentProcess, SignPtr, @Patch[I][0], Length(Patch[I]), bw);
  578. end;
  579. if INIReadBool(INI, Sect, 'SingleUserPatch.x86', False) then begin
  580. WriteLog('Patch CSessionArbitrationHelper::IsSingleSessionPerUserEnabled');
  581. SignPtr := Pointer(Cardinal(TermSrvBase) + INIReadDWordHex(INI, Sect, 'SingleUserOffset.x86', 0));
  582. I := SListFind(PatchList, INIReadString(INI, Sect, 'SingleUserCode.x86', ''));
  583. if I >= 0 then
  584. WriteProcessMemory(GetCurrentProcess, SignPtr, @Patch[I][0], Length(Patch[I]), bw);
  585. end;
  586. if INIReadBool(INI, Sect, 'DefPolicyPatch.x86', False) then begin
  587. WriteLog('Patch CDefPolicy::Query');
  588. SignPtr := Pointer(Cardinal(TermSrvBase) + INIReadDWordHex(INI, Sect, 'DefPolicyOffset.x86', 0));
  589. I := SListFind(PatchList, INIReadString(INI, Sect, 'DefPolicyCode.x86', ''));
  590. if I >= 0 then
  591. WriteProcessMemory(GetCurrentProcess, SignPtr, @Patch[I][0], Length(Patch[I]), bw);
  592. end;
  593. if INIReadBool(INI, Sect, 'SLPolicyInternal.x86', False) then begin
  594. WriteLog('Hook SLGetWindowsInformationDWORDWrapper');
  595. SignPtr := Pointer(Cardinal(TermSrvBase) + INIReadDWordHex(INI, Sect, 'SLPolicyOffset.x86', 0));
  596. MovJump.MovOp := $89; // mov eax, ecx
  597. MovJump.MovArg := $C8; // __msfastcall compatibility
  598. MovJump.PushOp := $68;
  599. MovJump.PushArg := @New_Win8SL;
  600. MovJump.RetOp := $C3;
  601. FuncName := INIReadString(INI, Sect, 'SLPolicyFunc.x86', 'New_Win8SL');
  602. if FuncName = 'New_Win8SL' then
  603. MovJump.PushArg := @New_Win8SL;
  604. if FuncName = 'New_Win8SL_CP' then
  605. MovJump.PushArg := @New_Win8SL_CP;
  606. WriteProcessMemory(GetCurrentProcess, SignPtr,
  607. @MovJump, SizeOf(mov_far_jmp), bw);
  608. end;
  609. if INIReadBool(INI, Sect, 'SLInitHook.x86', False) then begin
  610. WriteLog('Hook CSLQuery::Initialize');
  611. SignPtr := Pointer(Cardinal(TermSrvBase) + INIReadDWordHex(INI, Sect, 'SLInitOffset.x86', 0));
  612. Jump.PushOp := $68;
  613. Jump.PushArg := @New_CSLQuery_Initialize;
  614. Jump.RetOp := $C3;
  615. FuncName := INIReadString(INI, Sect, 'SLInitFunc.x86', 'New_CSLQuery_Initialize');
  616. if FuncName = 'New_CSLQuery_Initialize' then
  617. Jump.PushArg := @New_CSLQuery_Initialize;
  618. WriteProcessMemory(GetCurrentProcess, SignPtr,
  619. @Jump, SizeOf(far_jmp), bw);
  620. end;
  621. end;
  622. // unfreeze threads
  623. WriteLog('Resumimg threads...');
  624. RunThreads();
  625. end;
  626. function TermServiceMain(dwArgc: DWORD; lpszArgv: PWideChar): DWORD; stdcall;
  627. begin
  628. // wrap ServiceMain function
  629. WriteLog('>>> ServiceMain');
  630. if not IsHooked then
  631. HookFunctions;
  632. Result := 0;
  633. if @TSMain <> nil then
  634. Result := TSMain(dwArgc, lpszArgv);
  635. WriteLog('<<< ServiceMain');
  636. end;
  637. function TermServiceGlobals(lpGlobalData: Pointer): DWORD; stdcall;
  638. begin
  639. // wrap SvchostPushServiceGlobals function
  640. WriteLog('>>> SvchostPushServiceGlobals');
  641. if not IsHooked then
  642. HookFunctions;
  643. Result := 0;
  644. if @TSGlobals <> nil then
  645. Result := TSGlobals(lpGlobalData);
  646. WriteLog('<<< SvchostPushServiceGlobals');
  647. end;
  648. // export section
  649. exports
  650. TermServiceMain index 1 name 'ServiceMain',
  651. TermServiceGlobals index 2 name 'SvchostPushServiceGlobals';
  652. begin
  653. // DllMain procedure is not used
  654. end.