JclHookExcept.pas 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864
  1. {**************************************************************************************************}
  2. { }
  3. { Project JEDI Code Library (JCL) }
  4. { }
  5. { The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); }
  6. { you may not use this file except in compliance with the License. You may obtain a copy of the }
  7. { License at http://www.mozilla.org/MPL/ }
  8. { }
  9. { Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF }
  10. { ANY KIND, either express or implied. See the License for the specific language governing rights }
  11. { and limitations under the License. }
  12. { }
  13. { The Original Code is JclHookExcept.pas. }
  14. { }
  15. { The Initial Developer of the Original Code is Petr Vones. Portions created by Petr Vones are }
  16. { Copyright (C) Petr Vones. All Rights Reserved. }
  17. { }
  18. { Contributor(s): }
  19. { Petr Vones (pvones) }
  20. { Robert Marquardt (marquardt) }
  21. { Andreas Hausladen (ahuser) }
  22. { }
  23. {**************************************************************************************************}
  24. { }
  25. { Exception hooking routines }
  26. { }
  27. {**************************************************************************************************}
  28. { }
  29. { Last modified: $Date:: $ }
  30. { Revision: $Rev:: $ }
  31. { Author: $Author:: $ }
  32. { }
  33. {**************************************************************************************************}
  34. unit JclHookExcept;
  35. interface
  36. {$I jcl.inc}
  37. {$I windowsonly.inc}
  38. uses
  39. {$IFDEF UNITVERSIONING}
  40. JclUnitVersioning,
  41. {$ENDIF UNITVERSIONING}
  42. {$IFDEF HAS_UNITSCOPE}
  43. Winapi.Windows, System.SysUtils, System.Classes;
  44. {$ELSE ~HAS_UNITSCOPE}
  45. Windows, SysUtils, Classes;
  46. {$ENDIF ~HAS_UNITSCOPE}
  47. type
  48. // Exception hooking notifiers routines
  49. {$IFDEF BORLAND}
  50. TJclExceptFilterProc = function(ExceptRecord: PExceptionRecord): Exception;
  51. {$ENDIF BORLAND}
  52. TJclExceptNotifyProc = procedure(ExceptObj: TObject; ExceptAddr: Pointer; OSException: Boolean);
  53. TJclExceptNotifyProcEx = procedure(ExceptObj: TObject; ExceptAddr: Pointer; OSException: Boolean; StackPointer: Pointer);
  54. TJclExceptNotifyMethod = procedure(ExceptObj: TObject; ExceptAddr: Pointer; OSException: Boolean) of object;
  55. TJclExceptNotifyPriority = (npNormal, npFirstChain);
  56. {$IFDEF BORLAND}
  57. function JclAddExceptFilter(const FilterProc: TJclExceptFilterProc; Priority: TJclExceptNotifyPriority = npNormal): Boolean;
  58. {$ENDIF BORLAND}
  59. function JclAddExceptNotifier(const NotifyProc: TJclExceptNotifyProc; Priority: TJclExceptNotifyPriority = npNormal): Boolean; overload;
  60. function JclAddExceptNotifier(const NotifyProc: TJclExceptNotifyProcEx; Priority: TJclExceptNotifyPriority = npNormal): Boolean; overload;
  61. function JclAddExceptNotifier(const NotifyMethod: TJclExceptNotifyMethod; Priority: TJclExceptNotifyPriority = npNormal): Boolean; overload;
  62. {$IFDEF BORLAND}
  63. function JclRemoveExceptFilter(const FilterProc: TJclExceptFilterProc): Boolean;
  64. {$ENDIF BORLAND}
  65. function JclRemoveExceptNotifier(const NotifyProc: TJclExceptNotifyProc): Boolean; overload;
  66. function JclRemoveExceptNotifier(const NotifyProc: TJclExceptNotifyProcEx): Boolean; overload;
  67. function JclRemoveExceptNotifier(const NotifyMethod: TJclExceptNotifyMethod): Boolean; overload;
  68. procedure JclReplaceExceptObj(NewExceptObj: Exception);
  69. // Exception hooking routines
  70. function JclHookExceptions: Boolean;
  71. function JclUnhookExceptions: Boolean;
  72. function JclExceptionsHooked: Boolean;
  73. function JclHookExceptionsInModule(Module: HMODULE): Boolean;
  74. function JclUnhookExceptionsInModule(Module: HMODULE): Boolean;
  75. // Exceptions hooking in libraries
  76. type
  77. TJclModuleArray = array of HMODULE;
  78. function JclInitializeLibrariesHookExcept: Boolean;
  79. function JclHookedExceptModulesList(out ModulesList: TJclModuleArray): Boolean;
  80. // Hooking routines location info helper
  81. function JclBelongsHookedCode(Address: Pointer): Boolean;
  82. {$IFDEF UNITVERSIONING}
  83. const
  84. UnitVersioning: TUnitVersionInfo = (
  85. RCSfile: '$URL$';
  86. Revision: '$Revision$';
  87. Date: '$Date$';
  88. LogPath: 'JCL\source\windows';
  89. Extra: '';
  90. Data: nil
  91. );
  92. {$ENDIF UNITVERSIONING}
  93. implementation
  94. uses
  95. JclBase,
  96. JclPeImage,
  97. JclSysInfo, JclSysUtils;
  98. type
  99. PExceptionArguments = ^TExceptionArguments;
  100. TExceptionArguments = record
  101. ExceptAddr: Pointer;
  102. ExceptObj: Exception;
  103. end;
  104. {$IFDEF BORLAND}
  105. TFilterItem = class(TObject)
  106. private
  107. FExceptFilterProc: TJclExceptFilterProc;
  108. FPriority: TJclExceptNotifyPriority;
  109. public
  110. constructor Create(const ExceptFilterProc: TJclExceptFilterProc; APriority: TJclExceptNotifyPriority);
  111. function DoFilterException(ExceptRecord: PExceptionRecord; out ExceptObj: Exception): Boolean;
  112. property Priority: TJclExceptNotifyPriority read FPriority;
  113. end;
  114. {$ENDIF BORLAND}
  115. TNotifierItem = class(TObject)
  116. private
  117. FNotifyMethod: TJclExceptNotifyMethod;
  118. FNotifyProc: TJclExceptNotifyProc;
  119. FNotifyProcEx: TJclExceptNotifyProcEx;
  120. FPriority: TJclExceptNotifyPriority;
  121. public
  122. constructor Create(const NotifyProc: TJclExceptNotifyProc; Priority: TJclExceptNotifyPriority); overload;
  123. constructor Create(const NotifyProc: TJclExceptNotifyProcEx; Priority: TJclExceptNotifyPriority); overload;
  124. constructor Create(const NotifyMethod: TJclExceptNotifyMethod; Priority: TJclExceptNotifyPriority); overload;
  125. procedure DoNotify(ExceptObj: TObject; ExceptAddr: Pointer; OSException: Boolean; StackPointer: Pointer);
  126. property Priority: TJclExceptNotifyPriority read FPriority;
  127. end;
  128. var
  129. ExceptionsHooked: Boolean;
  130. Kernel32_RaiseException: procedure (dwExceptionCode, dwExceptionFlags,
  131. nNumberOfArguments: DWORD; lpArguments: PDWORD); stdcall;
  132. {$IFDEF BORLAND}
  133. SysUtils_ExceptObjProc: function (P: PExceptionRecord): Exception;
  134. {$ENDIF BORLAND}
  135. {$IFDEF FPC}
  136. SysUtils_ExceptProc: TExceptProc;
  137. {$ENDIF FPC}
  138. Notifiers: TThreadList;
  139. {$IFDEF BORLAND}
  140. Filters: TThreadList;
  141. {$ENDIF BORLAND}
  142. {$IFDEF HOOK_DLL_EXCEPTIONS}
  143. const
  144. JclHookExceptDebugHookName = '__JclHookExcept';
  145. type
  146. TJclHookExceptDebugHook = procedure(Module: HMODULE; Hook: Boolean); stdcall;
  147. TJclHookExceptModuleList = class(TObject)
  148. private
  149. FModules: TThreadList;
  150. protected
  151. procedure HookStaticModules;
  152. public
  153. constructor Create;
  154. destructor Destroy; override;
  155. class function JclHookExceptDebugHookAddr: Pointer;
  156. procedure HookModule(Module: HMODULE);
  157. procedure List(out ModulesList: TJclModuleArray);
  158. procedure UnhookModule(Module: HMODULE);
  159. end;
  160. var
  161. HookExceptModuleList: TJclHookExceptModuleList;
  162. JclHookExceptDebugHook: Pointer;
  163. exports
  164. JclHookExceptDebugHook name JclHookExceptDebugHookName;
  165. {$ENDIF HOOK_DLL_EXCEPTIONS}
  166. {$STACKFRAMES OFF}
  167. threadvar
  168. Recursive: Boolean;
  169. NewResultExc: Exception;
  170. //=== Helper routines ========================================================
  171. function RaiseExceptionAddress: Pointer;
  172. begin
  173. Result := GetProcAddress(GetModuleHandle(kernel32), 'RaiseException');
  174. Assert(Result <> nil);
  175. end;
  176. procedure FreeThreadObjList(var TheList: TThreadList);
  177. var
  178. I: Integer;
  179. begin
  180. with TheList.LockList do
  181. try
  182. for I := 0 to Count - 1 do
  183. TObject(Items[I]).Free;
  184. finally
  185. TheList.UnlockList;
  186. end;
  187. FreeAndNil(TheList);
  188. end;
  189. //=== { TFilterItem } ========================================================
  190. {$IFDEF BORLAND}
  191. constructor TFilterItem.Create(const ExceptFilterProc: TJclExceptFilterProc; APriority: TJclExceptNotifyPriority);
  192. begin
  193. FExceptFilterProc := ExceptFilterProc;
  194. FPriority := APriority;
  195. end;
  196. function TFilterItem.DoFilterException(ExceptRecord: PExceptionRecord; out ExceptObj: Exception): Boolean;
  197. begin
  198. if Assigned(FExceptFilterProc) then
  199. begin
  200. ExceptObj := FExceptFilterProc(ExceptRecord);
  201. Result := ExceptObj <> nil;
  202. end
  203. else
  204. Result := False;
  205. end;
  206. {$ENDIF BORLAND}
  207. //=== { TNotifierItem } ======================================================
  208. constructor TNotifierItem.Create(const NotifyProc: TJclExceptNotifyProc; Priority: TJclExceptNotifyPriority);
  209. begin
  210. inherited Create;
  211. FNotifyProc := NotifyProc;
  212. FPriority := Priority;
  213. end;
  214. constructor TNotifierItem.Create(const NotifyProc: TJclExceptNotifyProcEx; Priority: TJclExceptNotifyPriority);
  215. begin
  216. inherited Create;
  217. FNotifyProcEx := NotifyProc;
  218. FPriority := Priority;
  219. end;
  220. constructor TNotifierItem.Create(const NotifyMethod: TJclExceptNotifyMethod; Priority: TJclExceptNotifyPriority);
  221. begin
  222. inherited Create;
  223. FNotifyMethod := NotifyMethod;
  224. FPriority := Priority;
  225. end;
  226. procedure TNotifierItem.DoNotify(ExceptObj: TObject; ExceptAddr: Pointer;
  227. OSException: Boolean; StackPointer: Pointer);
  228. begin
  229. if Assigned(FNotifyProc) then
  230. FNotifyProc(ExceptObj, ExceptAddr, OSException)
  231. else
  232. if Assigned(FNotifyProcEx) then
  233. FNotifyProcEx(ExceptObj, ExceptAddr, OSException, StackPointer)
  234. else
  235. if Assigned(FNotifyMethod) then
  236. FNotifyMethod(ExceptObj, ExceptAddr, OSException);
  237. end;
  238. function GetFramePointer: Pointer;
  239. asm
  240. {$IFDEF CPU32}
  241. MOV EAX, EBP
  242. {$ENDIF CPU32}
  243. {$IFDEF CPU64}
  244. MOV RAX, RBP
  245. {$ENDIF CPU64}
  246. end;
  247. {$STACKFRAMES ON}
  248. {$IFDEF BORLAND}
  249. function DoExceptFilter(ExceptRecord: PExceptionRecord): Exception;
  250. var
  251. Priorities: TJclExceptNotifyPriority;
  252. I: Integer;
  253. begin
  254. if Recursive then
  255. Exit;
  256. if Assigned(Filters) then
  257. begin
  258. Recursive := True;
  259. try
  260. with Filters.LockList do
  261. try
  262. for Priorities := High(Priorities) downto Low(Priorities) do
  263. for I := 0 to Count - 1 do
  264. with TFilterItem(Items[I]) do
  265. if Priority = Priorities then
  266. if DoFilterException(ExceptRecord, Result) then
  267. Exit;
  268. finally
  269. Filters.UnlockList;
  270. end;
  271. // Nobody wanted to handle the external exception. Call the default handler.
  272. Result := SysUtils_ExceptObjProc(ExceptRecord);
  273. finally
  274. Recursive := False;
  275. end;
  276. end;
  277. end;
  278. {$ENDIF BORLAND}
  279. procedure DoExceptNotify(ExceptObj: TObject; ExceptAddr: Pointer; OSException: Boolean; StackPointer: Pointer);
  280. var
  281. Priorities: TJclExceptNotifyPriority;
  282. I: Integer;
  283. begin
  284. if Recursive then
  285. Exit;
  286. if Assigned(Notifiers) then
  287. begin
  288. Recursive := True;
  289. NewResultExc := nil;
  290. try
  291. with Notifiers.LockList do
  292. try
  293. if Count = 1 then
  294. begin
  295. with TNotifierItem(Items[0]) do
  296. DoNotify( ExceptObj, ExceptAddr, OSException, StackPointer);
  297. end
  298. else
  299. begin
  300. for Priorities := High(Priorities) downto Low(Priorities) do
  301. for I := 0 to Count - 1 do
  302. with TNotifierItem(Items[I]) do
  303. if Priority = Priorities then
  304. DoNotify(ExceptObj, ExceptAddr, OSException, StackPointer);
  305. end;
  306. finally
  307. Notifiers.UnlockList;
  308. end;
  309. finally
  310. Recursive := False;
  311. end;
  312. end;
  313. end;
  314. procedure HookedRaiseException(ExceptionCode, ExceptionFlags, NumberOfArguments: DWORD;
  315. Arguments: PExceptionArguments); stdcall;
  316. const
  317. MS_VC_EXCEPTION = $406D1388;
  318. cDelphiException = $0EEDFADE;
  319. cNonContinuable = 1; // Delphi exceptions
  320. cNonContinuableException = $C0000025; // C++Builder exceptions (sounds like a bug)
  321. DelphiNumberOfArguments = 7;
  322. CBuilderNumberOfArguments = 8;
  323. begin
  324. if ((ExceptionFlags = cNonContinuable) or (ExceptionFlags = cNonContinuableException)) and
  325. (ExceptionCode = cDelphiException) and
  326. (NumberOfArguments in [DelphiNumberOfArguments, CBuilderNumberOfArguments])
  327. //TODO: The difference for Win64 is bigger than 100 Byte and the comment of JVCS revision 0.3 of
  328. // JclDebug.pas, where HookedRaiseException has been added by Petr, isn't very informative
  329. {$IFDEF CPU32}
  330. and (TJclAddr(Arguments) = TJclAddr(@Arguments) + SizeOf(Pointer))
  331. {$ENDIF CPU32}
  332. and (ExceptionCode <> MS_VC_EXCEPTION) // ignore TThread.NameThreadForDebugging
  333. then
  334. begin
  335. DoExceptNotify(Arguments.ExceptObj, Arguments.ExceptAddr, False, GetFramePointer);
  336. end;
  337. Kernel32_RaiseException(ExceptionCode, ExceptionFlags, NumberOfArguments, PDWORD(Arguments));
  338. end;
  339. {$IFDEF BORLAND}
  340. function HookedExceptObjProc(P: PExceptionRecord): Exception;
  341. const
  342. MS_VC_EXCEPTION = $406D1388;
  343. var
  344. NewResultExcCache: Exception; // TLS optimization
  345. begin
  346. if P.ExceptionCode <> MS_VC_EXCEPTION then
  347. begin
  348. Result := DoExceptFilter(P);
  349. DoExceptNotify(Result, P^.ExceptionAddress, True, GetFramePointer);
  350. NewResultExcCache := NewResultExc;
  351. if NewResultExcCache <> nil then
  352. Result := NewResultExcCache;
  353. end
  354. else
  355. Result := SysUtils_ExceptObjProc(P);
  356. end;
  357. {$ENDIF BORLAND}
  358. {$IFDEF FPC}
  359. procedure HookedExceptProc(Obj : TObject; Addr : Pointer; FrameCount:Longint; Frame: PPointer);
  360. var
  361. NewResultExcCache: Exception; // TLS optimization
  362. begin
  363. DoExceptNotify(Obj, Addr, True, GetFramePointer);
  364. NewResultExcCache := NewResultExc;
  365. if NewResultExcCache <> nil then
  366. SysUtils_ExceptProc(NewResultExcCache, Addr, FrameCount, Frame)
  367. else
  368. SysUtils_ExceptProc(Obj, Addr, FrameCount, Frame)
  369. end;
  370. {$ENDIF FPC}
  371. {$IFNDEF STACKFRAMES_ON}
  372. {$STACKFRAMES OFF}
  373. {$ENDIF ~STACKFRAMES_ON}
  374. // Do not change ordering of HookedRaiseException, HookedExceptObjProc and JclBelongsHookedCode routines
  375. function JclBelongsHookedCode(Address: Pointer): Boolean;
  376. begin
  377. Result := (TJclAddr(@HookedRaiseException) < TJclAddr(@JclBelongsHookedCode)) and
  378. (TJclAddr(@HookedRaiseException) <= TJclAddr(Address)) and
  379. (TJclAddr(@JclBelongsHookedCode) > TJclAddr(Address));
  380. end;
  381. {$IFDEF BORLAND}
  382. function JclAddExceptFilter(const FilterProc: TJclExceptFilterProc; Priority: TJclExceptNotifyPriority = npNormal): Boolean;
  383. begin
  384. Result := Assigned(FilterProc);
  385. if Result then
  386. with Filters.LockList do
  387. try
  388. Add(TFilterItem.Create(FilterProc, Priority));
  389. finally
  390. Filters.UnlockList;
  391. end;
  392. end;
  393. {$ENDIF BORLAND}
  394. function JclAddExceptNotifier(const NotifyProc: TJclExceptNotifyProc; Priority: TJclExceptNotifyPriority): Boolean;
  395. begin
  396. Result := Assigned(NotifyProc);
  397. if Result then
  398. with Notifiers.LockList do
  399. try
  400. Add(TNotifierItem.Create(NotifyProc, Priority));
  401. finally
  402. Notifiers.UnlockList;
  403. end;
  404. end;
  405. function JclAddExceptNotifier(const NotifyProc: TJclExceptNotifyProcEx; Priority: TJclExceptNotifyPriority): Boolean;
  406. begin
  407. Result := Assigned(NotifyProc);
  408. if Result then
  409. with Notifiers.LockList do
  410. try
  411. Add(TNotifierItem.Create(NotifyProc, Priority));
  412. finally
  413. Notifiers.UnlockList;
  414. end;
  415. end;
  416. function JclAddExceptNotifier(const NotifyMethod: TJclExceptNotifyMethod; Priority: TJclExceptNotifyPriority): Boolean;
  417. begin
  418. Result := Assigned(NotifyMethod);
  419. if Result then
  420. with Notifiers.LockList do
  421. try
  422. Add(TNotifierItem.Create(NotifyMethod, Priority));
  423. finally
  424. Notifiers.UnlockList;
  425. end;
  426. end;
  427. {$IFDEF BORLAND}
  428. function JclRemoveExceptFilter(const FilterProc: TJclExceptFilterProc): Boolean;
  429. var
  430. O: TFilterItem;
  431. I: Integer;
  432. begin
  433. Result := Assigned(FilterProc);
  434. if Result then
  435. with Filters.LockList do
  436. try
  437. for I := 0 to Count - 1 do
  438. begin
  439. O := TFilterItem(Items[I]);
  440. if @O.FExceptFilterProc = @FilterProc then
  441. begin
  442. O.Free;
  443. Items[I] := nil;
  444. end;
  445. end;
  446. Pack;
  447. finally
  448. Filters.UnlockList;
  449. end;
  450. end;
  451. {$ENDIF BORLAND}
  452. function JclRemoveExceptNotifier(const NotifyProc: TJclExceptNotifyProc): Boolean;
  453. var
  454. O: TNotifierItem;
  455. I: Integer;
  456. begin
  457. Result := Assigned(NotifyProc);
  458. if Result then
  459. with Notifiers.LockList do
  460. try
  461. for I := 0 to Count - 1 do
  462. begin
  463. O := TNotifierItem(Items[I]);
  464. if @O.FNotifyProc = @NotifyProc then
  465. begin
  466. O.Free;
  467. Items[I] := nil;
  468. end;
  469. end;
  470. Pack;
  471. finally
  472. Notifiers.UnlockList;
  473. end;
  474. end;
  475. function JclRemoveExceptNotifier(const NotifyProc: TJclExceptNotifyProcEx): Boolean;
  476. var
  477. O: TNotifierItem;
  478. I: Integer;
  479. begin
  480. Result := Assigned(NotifyProc);
  481. if Result then
  482. with Notifiers.LockList do
  483. try
  484. for I := 0 to Count - 1 do
  485. begin
  486. O := TNotifierItem(Items[I]);
  487. if @O.FNotifyProcEx = @NotifyProc then
  488. begin
  489. O.Free;
  490. Items[I] := nil;
  491. end;
  492. end;
  493. Pack;
  494. finally
  495. Notifiers.UnlockList;
  496. end;
  497. end;
  498. function JclRemoveExceptNotifier(const NotifyMethod: TJclExceptNotifyMethod): Boolean;
  499. var
  500. O: TNotifierItem;
  501. I: Integer;
  502. begin
  503. Result := Assigned(NotifyMethod);
  504. if Result then
  505. with Notifiers.LockList do
  506. try
  507. for I := 0 to Count - 1 do
  508. begin
  509. O := TNotifierItem(Items[I]);
  510. if (TMethod(O.FNotifyMethod).Code = TMethod(NotifyMethod).Code) and
  511. (TMethod(O.FNotifyMethod).Data = TMethod(NotifyMethod).Data) then
  512. begin
  513. O.Free;
  514. Items[I] := nil;
  515. end;
  516. end;
  517. Pack;
  518. finally
  519. Notifiers.UnlockList;
  520. end;
  521. end;
  522. procedure JclReplaceExceptObj(NewExceptObj: Exception);
  523. begin
  524. Assert(Recursive);
  525. NewResultExc := NewExceptObj;
  526. end;
  527. {$IFDEF BORLAND}
  528. function GetCppRtlBase: Pointer;
  529. const
  530. {$IFDEF COMPILER6} { Delphi/C++Builder 6 }
  531. CppRtlVersion = 60;
  532. {$ELSE ~COMPILER6}
  533. {$IFDEF RTL185} { Delphi/C++Builder 2007 were aiming for
  534. binary compatibility with BDS2006, which
  535. complicates things a bit }
  536. CppRtlVersion = 80;
  537. {$ELSE ~RTL185}
  538. { Successive RTLDLL version numbers in the remaining cases: CB2006 has cc3270mt.dll,
  539. CB2009 (= CB2006 + 2 releases) has cc3290mt.dll, CB2010 has cc32100mt.dll etc. }
  540. CppRtlVersion = 70 + Trunc(RtlVersion - 18.0) * 10;
  541. {$ENDIF ~RTL185}
  542. {$ENDIF ~COMPILER6}
  543. begin
  544. Result := Pointer(GetModuleHandle(PChar(Format('cc32%dmt.dll', [CppRtlVersion]))));
  545. { 'Result = nil' means that the C++ RTL has been linked statically or is not available at all;
  546. in this case TJclPeMapImgHooks.ReplaceImport() is a no-op. The base module is also being
  547. hooked separately, so we're covered. }
  548. end;
  549. function HasCppRtl: Boolean;
  550. begin
  551. Result := GetCppRtlBase <> TJclPeMapImgHooks.SystemBase;
  552. end;
  553. {$ENDIF BORLAND}
  554. function JclHookExceptions: Boolean;
  555. var
  556. RaiseExceptionAddressCache: Pointer;
  557. begin
  558. RaiseExceptionAddressCache := RaiseExceptionAddress;
  559. { Detect C++Builder applications and C++ packages loaded into Delphi applications.
  560. Hook the C++ RTL regardless of ExceptionsHooked so that users can call JclHookException() after
  561. loading a C++ package which might pull in the C++ RTL DLL. }
  562. {$IFDEF BORLAND}
  563. if HasCppRtl then
  564. TJclPeMapImgHooks.ReplaceImport(GetCppRtlBase, kernel32, RaiseExceptionAddressCache, @HookedRaiseException);
  565. {$ENDIF BORLAND}
  566. if not ExceptionsHooked then
  567. begin
  568. Recursive := False;
  569. with TJclPeMapImgHooks do
  570. Result := ReplaceImport(SystemBase, kernel32, RaiseExceptionAddressCache, @HookedRaiseException);
  571. if Result then
  572. begin
  573. @Kernel32_RaiseException := RaiseExceptionAddressCache;
  574. {$IFDEF BORLAND}
  575. SysUtils_ExceptObjProc := System.ExceptObjProc;
  576. System.ExceptObjProc := @HookedExceptObjProc;
  577. {$ENDIF BORLAND}
  578. {$IFDEF FPC}
  579. SysUtils_ExceptProc := System.ExceptProc;
  580. System.ExceptProc := @HookedExceptProc;
  581. {$ENDIF FPC}
  582. end;
  583. ExceptionsHooked := Result;
  584. end
  585. else
  586. Result := True;
  587. end;
  588. function JclUnhookExceptions: Boolean;
  589. begin
  590. {$IFDEF BORLAND}
  591. if HasCppRtl then
  592. TJclPeMapImgHooks.ReplaceImport (GetCppRtlBase, kernel32, @HookedRaiseException, @Kernel32_RaiseException);
  593. {$ENDIF BORLAND}
  594. if ExceptionsHooked then
  595. begin
  596. with TJclPeMapImgHooks do
  597. ReplaceImport(SystemBase, kernel32, @HookedRaiseException, @Kernel32_RaiseException);
  598. {$IFDEF BORLAND}
  599. System.ExceptObjProc := @SysUtils_ExceptObjProc;
  600. @SysUtils_ExceptObjProc := nil;
  601. {$ENDIF BORLAND}
  602. {$IFDEF FPC}
  603. System.ExceptProc := @SysUtils_ExceptProc;
  604. @SysUtils_ExceptProc := nil;
  605. {$ENDIF FPC}
  606. @Kernel32_RaiseException := nil;
  607. Result := True;
  608. ExceptionsHooked := False;
  609. end
  610. else
  611. Result := True;
  612. end;
  613. function JclExceptionsHooked: Boolean;
  614. begin
  615. Result := ExceptionsHooked;
  616. end;
  617. function JclHookExceptionsInModule(Module: HMODULE): Boolean;
  618. begin
  619. Result := ExceptionsHooked and
  620. TJclPeMapImgHooks.ReplaceImport(Pointer(Module), kernel32, RaiseExceptionAddress, @HookedRaiseException);
  621. end;
  622. function JclUnhookExceptionsInModule(Module: HMODULE): Boolean;
  623. begin
  624. Result := ExceptionsHooked and
  625. TJclPeMapImgHooks.ReplaceImport(Pointer(Module), kernel32, @HookedRaiseException, @Kernel32_RaiseException);
  626. end;
  627. {$IFDEF HOOK_DLL_EXCEPTIONS}
  628. // Exceptions hooking in libraries
  629. procedure JclHookExceptDebugHookProc(Module: HMODULE; Hook: Boolean); stdcall;
  630. begin
  631. if Hook then
  632. HookExceptModuleList.HookModule(Module)
  633. else
  634. HookExceptModuleList.UnhookModule(Module);
  635. end;
  636. function CallExportedHookExceptProc(Module: HMODULE; Hook: Boolean): Boolean;
  637. var
  638. HookExceptProcPtr: PPointer;
  639. HookExceptProc: TJclHookExceptDebugHook;
  640. begin
  641. HookExceptProcPtr := TJclHookExceptModuleList.JclHookExceptDebugHookAddr;
  642. Result := Assigned(HookExceptProcPtr);
  643. if Result then
  644. begin
  645. @HookExceptProc := HookExceptProcPtr^;
  646. if Assigned(HookExceptProc) then
  647. HookExceptProc(Module, True);
  648. end;
  649. end;
  650. {$ENDIF HOOK_DLL_EXCEPTIONS}
  651. function JclInitializeLibrariesHookExcept: Boolean;
  652. begin
  653. {$IFDEF HOOK_DLL_EXCEPTIONS}
  654. if IsLibrary then
  655. Result := CallExportedHookExceptProc(SystemTObjectInstance, True)
  656. else
  657. begin
  658. if not Assigned(HookExceptModuleList) then
  659. HookExceptModuleList := TJclHookExceptModuleList.Create;
  660. Result := True;
  661. end;
  662. {$ELSE HOOK_DLL_EXCEPTIONS}
  663. Result := True;
  664. {$ENDIF HOOK_DLL_EXCEPTIONS}
  665. end;
  666. function JclHookedExceptModulesList(out ModulesList: TJclModuleArray): Boolean;
  667. begin
  668. {$IFDEF HOOK_DLL_EXCEPTIONS}
  669. Result := Assigned(HookExceptModuleList);
  670. if Result then
  671. HookExceptModuleList.List(ModulesList);
  672. {$ELSE HOOK_DLL_EXCEPTIONS}
  673. Result := False;
  674. SetLength(ModulesList, 0);
  675. {$ENDIF HOOK_DLL_EXCEPTIONS}
  676. end;
  677. {$IFDEF HOOK_DLL_EXCEPTIONS}
  678. procedure FinalizeLibrariesHookExcept;
  679. begin
  680. FreeAndNil(HookExceptModuleList);
  681. if IsLibrary then
  682. CallExportedHookExceptProc(SystemTObjectInstance, False);
  683. end;
  684. //=== { TJclHookExceptModuleList } ===========================================
  685. constructor TJclHookExceptModuleList.Create;
  686. begin
  687. inherited Create;
  688. FModules := TThreadList.Create;
  689. HookStaticModules;
  690. JclHookExceptDebugHook := @JclHookExceptDebugHookProc;
  691. end;
  692. destructor TJclHookExceptModuleList.Destroy;
  693. begin
  694. JclHookExceptDebugHook := nil;
  695. FreeAndNil(FModules);
  696. inherited Destroy;
  697. end;
  698. procedure TJclHookExceptModuleList.HookModule(Module: HMODULE);
  699. begin
  700. with FModules.LockList do
  701. try
  702. if IndexOf(Pointer(Module)) = -1 then
  703. begin
  704. Add(Pointer(Module));
  705. JclHookExceptionsInModule(Module);
  706. end;
  707. finally
  708. FModules.UnlockList;
  709. end;
  710. end;
  711. procedure TJclHookExceptModuleList.HookStaticModules;
  712. var
  713. ModulesList: TStringList;
  714. I: Integer;
  715. Module: HMODULE;
  716. begin
  717. ModulesList := nil;
  718. with FModules.LockList do
  719. try
  720. ModulesList := TStringList.Create;
  721. if LoadedModulesList(ModulesList, GetCurrentProcessId, True) then
  722. for I := 0 to ModulesList.Count - 1 do
  723. begin
  724. Module := HMODULE(ModulesList.Objects[I]);
  725. if GetProcAddress(Module, JclHookExceptDebugHookName) <> nil then
  726. HookModule(Module);
  727. end;
  728. finally
  729. FModules.UnlockList;
  730. ModulesList.Free;
  731. end;
  732. end;
  733. class function TJclHookExceptModuleList.JclHookExceptDebugHookAddr: Pointer;
  734. var
  735. HostModule: HMODULE;
  736. begin
  737. HostModule := GetModuleHandle(nil);
  738. Result := GetProcAddress(HostModule, JclHookExceptDebugHookName);
  739. end;
  740. procedure TJclHookExceptModuleList.List(out ModulesList: TJclModuleArray);
  741. var
  742. I: Integer;
  743. begin
  744. with FModules.LockList do
  745. try
  746. SetLength(ModulesList, Count);
  747. for I := 0 to Count - 1 do
  748. ModulesList[I] := HMODULE(Items[I]);
  749. finally
  750. FModules.UnlockList;
  751. end;
  752. end;
  753. procedure TJclHookExceptModuleList.UnhookModule(Module: HMODULE);
  754. begin
  755. with FModules.LockList do
  756. try
  757. Remove(Pointer(Module));
  758. finally
  759. FModules.UnlockList;
  760. end;
  761. end;
  762. {$ENDIF HOOK_DLL_EXCEPTIONS}
  763. initialization
  764. Notifiers := TThreadList.Create;
  765. {$IFDEF BORLAND}
  766. Filters := TThreadList.Create;
  767. {$ENDIF BORLAND}
  768. {$IFDEF UNITVERSIONING}
  769. RegisterUnitVersion(HInstance, UnitVersioning);
  770. {$ENDIF UNITVERSIONING}
  771. finalization
  772. {$IFDEF UNITVERSIONING}
  773. UnregisterUnitVersion(HInstance);
  774. {$ENDIF UNITVERSIONING}
  775. {$IFDEF HOOK_DLL_EXCEPTIONS}
  776. FinalizeLibrariesHookExcept;
  777. {$ENDIF HOOK_DLL_EXCEPTIONS}
  778. FreeThreadObjList(Notifiers);
  779. {$IFDEF BORLAND}
  780. FreeThreadObjList(Filters);
  781. {$ENDIF BORLAND}
  782. end.