📄 jclhookexcept.pas
字号:
{**************************************************************************************************}
{ }
{ Project JEDI Code Library (JCL) }
{ }
{ The contents of this file are subject to the Mozilla Public License Version 1.1 (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.mozilla.org/MPL/ }
{ }
{ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF }
{ ANY KIND, either express or implied. See the License for the specific language governing rights }
{ and limitations under the License. }
{ }
{ The Original Code is JclHookExcept.pas. }
{ }
{ The Initial Developer of the Original Code is Petr Vones. Portions created by Petr Vones are }
{ Copyright (C) Petr Vones. All Rights Reserved. }
{ }
{ Contributor(s): }
{ Petr Vones (pvones) }
{ Robert Marquardt (marquardt) }
{ }
{**************************************************************************************************}
{ }
{ Exception hooking routines }
{ }
{ Unit owner: Petr Vones }
{ }
{**************************************************************************************************}
// Last modified: $Date: 2005/02/25 07:20:15 $
// For history see end of file
unit JclHookExcept;
interface
{$I jcl.inc}
uses
Windows, SysUtils;
type
// Exception hooking notifiers routines
TJclExceptNotifyProc = procedure(ExceptObj: TObject; ExceptAddr: Pointer; OSException: Boolean);
TJclExceptNotifyMethod = procedure(ExceptObj: TObject; ExceptAddr: Pointer; OSException: Boolean) of object;
TJclExceptNotifyPriority = (npNormal, npFirstChain);
function JclAddExceptNotifier(const NotifyProc: TJclExceptNotifyProc; Priority: TJclExceptNotifyPriority = npNormal): Boolean; overload;
function JclAddExceptNotifier(const NotifyMethod: TJclExceptNotifyMethod; Priority: TJclExceptNotifyPriority = npNormal): Boolean; overload;
function JclRemoveExceptNotifier(const NotifyProc: TJclExceptNotifyProc): Boolean; overload;
function JclRemoveExceptNotifier(const NotifyMethod: TJclExceptNotifyMethod): Boolean; overload;
procedure JclReplaceExceptObj(NewExceptObj: Exception);
// Exception hooking routines
function JclHookExceptions: Boolean;
function JclUnhookExceptions: Boolean;
function JclExceptionsHooked: Boolean;
function JclHookExceptionsInModule(Module: HMODULE): Boolean;
function JclUnkookExceptionsInModule(Module: HMODULE): Boolean;
// Exceptions hooking in libraries
type
TJclModuleArray = array of HMODULE;
function JclInitializeLibrariesHookExcept: Boolean;
function JclHookedExceptModulesList(var ModulesList: TJclModuleArray): Boolean;
// Hooking routines location info helper
function JclBelongsHookedCode(Addr: Pointer): Boolean;
implementation
uses
Classes,
JclBase, JclPeImage, JclSysInfo, JclSysUtils;
type
PExceptionArguments = ^TExceptionArguments;
TExceptionArguments = record
ExceptAddr: Pointer;
ExceptObj: Exception;
end;
TNotifierItem = class(TObject)
private
FNotifyMethod: TJclExceptNotifyMethod;
FNotifyProc: TJclExceptNotifyProc;
FPriority: TJclExceptNotifyPriority;
public
constructor Create(const NotifyProc: TJclExceptNotifyProc; Priority: TJclExceptNotifyPriority); overload;
constructor Create(const NotifyMethod: TJclExceptNotifyMethod; Priority: TJclExceptNotifyPriority); overload;
procedure DoNotify(ExceptObj: TObject; ExceptAddr: Pointer; OSException: Boolean);
property Priority: TJclExceptNotifyPriority read FPriority;
end;
var
ExceptionsHooked: Boolean;
Kernel32_RaiseException: procedure (dwExceptionCode, dwExceptionFlags,
nNumberOfArguments: DWORD; lpArguments: PDWORD); stdcall;
SysUtils_ExceptObjProc: function (P: PExceptionRecord): Exception;
Notifiers: TThreadList;
const
JclHookExceptDebugHookName = '__JclHookExcept';
type
TJclHookExceptDebugHook = procedure(Module: HMODULE; Hook: Boolean); stdcall;
TJclHookExceptModuleList = class(TObject)
private
FModules: TThreadList;
protected
procedure HookStaticModules;
public
constructor Create;
destructor Destroy; override;
class function JclHookExceptDebugHookAddr: Pointer;
procedure HookModule(Module: HMODULE);
procedure List(var ModulesList: TJclModuleArray);
procedure UnhookModule(Module: HMODULE);
end;
var
HookExceptModuleList: TJclHookExceptModuleList;
JclHookExceptDebugHook: Pointer;
{$IFDEF HOOK_DLL_EXCEPTIONS}
exports
JclHookExceptDebugHook name JclHookExceptDebugHookName;
{$ENDIF HOOK_DLL_EXCEPTIONS}
{$STACKFRAMES OFF}
threadvar
Recursive: Boolean;
NewResultExc: Exception;
//=== Helper routines ========================================================
function RaiseExceptionAddress: Pointer;
begin
Result := GetProcAddress(GetModuleHandle(kernel32), 'RaiseException');
Assert(Result <> nil);
end;
procedure FreeNotifiers;
var
I: Integer;
begin
with Notifiers.LockList do
try
for I := 0 to Count - 1 do
TObject(Items[I]).Free;
finally
Notifiers.UnlockList;
end;
FreeAndNil(Notifiers);
end;
//=== { TNotifierItem } ======================================================
constructor TNotifierItem.Create(const NotifyProc: TJclExceptNotifyProc; Priority: TJclExceptNotifyPriority);
begin
inherited Create;
FNotifyProc := NotifyProc;
FPriority := Priority;
end;
constructor TNotifierItem.Create(const NotifyMethod: TJclExceptNotifyMethod; Priority: TJclExceptNotifyPriority);
begin
inherited Create;
FNotifyMethod := NotifyMethod;
FPriority := Priority;
end;
procedure TNotifierItem.DoNotify(ExceptObj: TObject; ExceptAddr: Pointer; OSException: Boolean);
begin
if Assigned(FNotifyProc) then
FNotifyProc(ExceptObj, ExceptAddr, OSException)
else
if Assigned(FNotifyMethod) then
FNotifyMethod(ExceptObj, ExceptAddr, OSException);
end;
{$STACKFRAMES ON}
procedure DoExceptNotify(ExceptObj: Exception; ExceptAddr: Pointer; OSException: Boolean);
var
Priorities: TJclExceptNotifyPriority;
I: Integer;
begin
if Recursive then
Exit;
if Assigned(Notifiers) then
begin
Recursive := True;
NewResultExc := nil;
try
with Notifiers.LockList do
try
for Priorities := High(Priorities) downto Low(Priorities) do
for I := 0 to Count - 1 do
with TNotifierItem(Items[I]) do
if Priority = Priorities then
DoNotify(ExceptObj, ExceptAddr, OSException);
finally
Notifiers.UnlockList;
end;
finally
Recursive := False;
end;
end;
end;
procedure HookedRaiseException(ExceptionCode, ExceptionFlags, NumberOfArguments: DWORD;
Arguments: PExceptionArguments); stdcall;
const
{$IFDEF DELPHI2}
cDelphiException = $0EEDFACE;
{$ELSE}
cDelphiException = $0EEDFADE;
{$ENDIF DELPHI2}
cNonContinuable = 1;
begin
if (ExceptionFlags = cNonContinuable) and (ExceptionCode = cDelphiException) and
(NumberOfArguments = 7) and (DWORD(Arguments) = DWORD(@Arguments) + 4) then
DoExceptNotify(Arguments.ExceptObj, Arguments.ExceptAddr, False);
Kernel32_RaiseException(ExceptionCode, ExceptionFlags, NumberOfArguments, PDWORD(Arguments));
end;
function HookedExceptObjProc(P: PExceptionRecord): Exception;
var
NewResultExcCache: Exception; // TLS optimization
begin
Result := SysUtils_ExceptObjProc(P);
DoExceptNotify(Result, P^.ExceptionAddress, True);
NewResultExcCache := NewResultExc;
if NewResultExcCache <> nil then
Result := NewResultExcCache;
end;
{$IFNDEF STACKFRAMES_ON}
{$STACKFRAMES OFF}
{$ENDIF ~STACKFRAMES_ON}
// Do not change ordering of HookedRaiseException, HookedExceptObjProc and JclBelongsHookedCode routines
function JclBelongsHookedCode(Addr: Pointer): Boolean;
begin
Result := (Cardinal(@HookedRaiseException) < Cardinal(@JclBelongsHookedCode)) and
(Cardinal(@HookedRaiseException) <= Cardinal(Addr)) and
(Cardinal(@JclBelongsHookedCode) > Cardinal(Addr));
end;
function JclAddExceptNotifier(const NotifyProc: TJclExceptNotifyProc; Priority: TJclExceptNotifyPriority): Boolean;
begin
Result := Assigned(NotifyProc);
if Result then
with Notifiers.LockList do
try
Add(TNotifierItem.Create(NotifyProc, Priority));
finally
Notifiers.UnlockList;
end;
end;
function JclAddExceptNotifier(const NotifyMethod: TJclExceptNotifyMethod; Priority: TJclExceptNotifyPriority): Boolean;
begin
Result := Assigned(NotifyMethod);
if Result then
with Notifiers.LockList do
try
Add(TNotifierItem.Create(NotifyMethod, Priority));
finally
Notifiers.UnlockList;
end;
end;
function JclRemoveExceptNotifier(const NotifyProc: TJclExceptNotifyProc): Boolean;
var
O: TNotifierItem;
I: Integer;
begin
Result := Assigned(NotifyProc);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -