參考一下: 本程式來自EurekaLog for Delphi中的EHook.pas, 經過修改後可以單獨運行. 本單元實現了兩種API Hook技術. 一是採用了更改各個模組中函數入口指標,實現各模組調用API時被 Hook.優點是可以選擇性的對某個模組(OCX,DLL..)訪問 某個API時被hook. 二是採用了所有模組都被Hook,通過修改API函數第一條指令為跳轉到使用者API,備份原API內容.用戶可以在自己定義的API上調用原來的API. 本單元還實現了UnHook. 是一個較完整的API Hook 庫程式 有興趣的朋友可以將這個API Hook Lib擴展到對其他進程的API Hook. 本文末尾將介紹兩種Hook技術的使用方法.1. EHookLib.pas: 代碼: {************************************************} { } { EurekaLog v 6.x } { Hook Unit - EHook } { } { Copyright (c) 2001 - 2007 by Fabio Dell'Aria } { } {************************************************}unit EHookLIB;//{$I Exceptions.inc}interfaceuses Windows; type THandle = Cardinal; PPointer = ^Pointer; PShortInt = ^ShortInt;function HookProcedureEx(ProcAddr, NewProc: Pointer; ProcName: string): Pointer; function UnhookProcedure(ProcAddr: Pointer): Boolean;function HookDllProcedureEx(ImportModule, ExportModule, ProcName: string; NewProc: Pointer): Pointer; function TryHookDllProcedureEx(ImportModules: array of string; ExportModule, ProcName: string; NewProc: Pointer; var CallProc: Pointer; CanFail: Boolean): Boolean; function TryHookProcedureEx(ExportModule, ProcName: string; NewProc: Pointer; var CallProc: Pointer): Boolean;function HookVirtualMethod(AClass: TClass; Index: Integer; Method: Pointer): Pointer; function UnhookVirtualMethod(AClass: TClass; Index: Integer): Boolean;procedure JumpToMem(Addr, Jump: Pointer);function GetFunctionSize(Addr, MaxSize: DWord): DWord; function GetAsmSize(Start: Pointer; var Size: Byte): Boolean;var CriticalError: procedure (const Section: string) = nil;implementationuses Classes, SysUtils;const EProcNullStr = 'Cannot hook a null procedure ("%s").'; ESharedAreaStr = 'Cannot hook the module "%s" located into the shared-area.'; EHookingErrorStr = 'Cannot hook the procedure "%s".'; SharedMem = $7FFFFFFF; // Don't use major value because Delphi3 don't support it. ModRmMod = $C0; // XX?????? ModRmRM = $07; // ?????XXX OperSizeOver = $66; // Change the operand size from 32 to 16/8 bits. AddrSizeOver = $67; // Change the address size from 32 to 16/8 bits. OpCodePrefixes: set of Byte = [$F0, $F2, $F3, $2E, $36, $3E, $26, $64, $65, OperSizeOver, AddrSizeOver]; OpCodeShortJump: set of Byte = [$70..$7F, $E0..$E3, $EB]; // 1 OpCode byte OpCodeReturn: set of Byte = [$C2, $C3..$CA, $CB]; // "Return" first byte OpCodes OpCodeLongJump1Byte: set of Byte = [$E8..$E9]; // 1 OpCode byte OpCodeLongJump2Bytes: set of Byte = [$80..$8F]; // 2 OpCode bytes, 1th = $0F AsmConst: array [0..255] of Byte = ($EE, $EE, $EE, $EE, $F1, $0B, $00, $00, $0E, $0E, $FE, $FE, $F1, $EB, $00, $FF, $EE, $EE, $EE, $EE, $E1, $EB, $E0, $E0, $EE, $FE, $FE, $FE, $F1, $FB, $F0, $F0, $EE, $EE, $EE, $EE, $F1, $FB, $FF, $F0, $EE, $EE, $EE, $EE, $E1, $EB, $EF, $E0, $0E, $0E, $0E, $0E, $01, $0B, $FF, $F0, $FE, $FE, $FE, $FE, $F1, $FB, $FF, $F0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $EE, $EE, $EF, $EF, $EF, $EF, $EB, $EE, $E1, $EE, $F0, $F0, $E0, $E0, $E1, $E1, $E1, $E1, $E1, $E1, $E1, $01, $F1, $F1, $F1, $F1, $F1, $F1, $E1, $E1, $BE, $BE, $BE, $BE, $BE, $BE, $BE, $BE, $BE, $BE, $BE, $BE, $BE, $BE, $BE, $BE, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $ED, $E0, $E0, $E0, $E0, $E0, $04, $04, $04, $E4, $E0, $E0, $E0, $E0, $01, $0B, $00, $E0, $E0, $E0, $E0, $E0, $E1, $E1, $E1, $E1, $E1, $E1, $E1, $E1, $FB, $FB, $EB, $EB, $EB, $EB, $EB, $EB, $EE, $EE, $E2, $E0, $EE, $EE, $EE, $EE, $03, $00, $02, $00, $00, $01, $00, $00, $FE, $EE, $EE, $EE, $E1, $E1, $F0, $E0, $EE, $EE, $EE, $EE, $EE, $EE, $EE, $EE, $E1, $E1, $E1, $E1, $E1, $E1, $F1, $E1, $EB, $EB, $ED, $E1, $E0, $E0, $E0, $E0, $FF, $E0, $EF, $EF, $E0, $E0, $EE, $EE, $E0, $E0, $E0, $E0, $E0, $E0, $EE, $FE);type EHookError = class(Exception); EProcNull = class(EHookError); EHookingError = class(EHookError); ESharedArea = class(EHookError); EIgnoreException = class(Exception); TProc = procedure; TRedirectOpCodes = packed record JMPOpCode: Byte; JMPDistance: DWord; end; TPrefixes = set of Byte; THookedProcedure = record OriginalProc, HookedBlockPt: Pointer; HookedBlockSize: DWord; POriginalAsmPt: Pointer; POriginalAsmSize: DWord; end; PHookedProcedure = ^ THookedProcedure; PSaveDLLProc = ^TSaveDLLProc; TSaveDLLProc = packed record HookModule: THandle; ExportModule: string; OldProc, NewProc: Pointer; end; THookedData = packed record ClassType: TClass; OriginalMethod: Pointer; Index: Integer; end; PHookedData = ^THookedData; PWin9xDebugThunk = ^TWin9xDebugThunk; TWin9xDebugThunk = packed record PUSH: Byte; // PUSH instruction opcode ($68) Addr: Pointer; // The actual address of the DLL routine JMP: Byte; // JMP instruction opcode ($E9) Rel: Integer; // Relative displacement (a Kernel32 address) end; IMAGE_IMPORT_DESCRIPTOR = packed record UnUsed: array [0..11] of Byte; Name: DWord; FirstThunk: DWord; // RVA to IAT end; PImageImportDescriptor = ^IMAGE_IMPORT_DESCRIPTOR; IMAGE_THUNK_DATA = packed record Function_: DWord; // PDWord end; PImageThunkData = ^IMAGE_THUNK_DATA; PImageDosHeader = ^TImageDosHeader; TImageDosHeader = packed record // DOS .EXE header e_magic: Word; // Magic number UnUsed: array [0..57] of Byte; _lfanew: LongInt; // File address of new exe header end; THookedMethodsList = class(TList) private FLock: TRTLCriticalSection; function GetItem(Index: Integer): PHookedData; protected public constructor Create; destructor Destroy; override; procedure Lock; procedure Unlock; procedure Delete(Index: Integer); property Items[Index: Integer]: PHookedData read GetItem; default; end;const TRedirectOpCodesSize = SizeOf(TRedirectOpCodes);var HookedProcedures, DllList: TList; HookedMethodsList: THookedMethodsList;//------------------------------------------------------------------------------{ THookedMethods }constructor THookedMethodsList.Create; begin inherited; InitializeCriticalSection(FLock); end;function THookedMethodsList.GetItem(Index: Integer): PHookedData; begin Result := PHookedData(TList(Self).Items[Index]); end;procedure THookedMethodsList.Lock; begin EnterCriticalSection(FLock); end;procedure THookedMethodsList.Unlock; begin LeaveCriticalSection(FLock); end;procedure THookedMethodsList.Delete(Index: Integer); var Data: PHookedData; Ptr: Pointer; begin Ptr := Items[Index]; Data := PHookedData(Ptr); Dispose(Data); inherited; end;destructor THookedMethodsList.Destroy; var I: Integer; begin Lock; try for I := 0 to HookedMethodsList.Count - 1 do UnhookVirtualMethod(HookedMethodsList[0]^.ClassType, HookedMethodsList[0]^.Index); finally Unlock; end; DeleteCriticalSection(FLock); inherited; end;//------------------------------------------------------------------------------function GetReadableSize(Address, Size: DWord): DWord; const ReadAttributes = [PAGE_READONLY, PAGE_READWRITE, PAGE_WRITECOPY, PAGE_EXECUTE, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_EXECUTE_WRITECOPY]; var MemInfo: TMemoryBasicInformation; Tmp: DWord; begin Result := 0; if (VirtualQuery(Pointer(Address), MemInfo, SizeOf(MemInfo)) = SizeOf(MemInfo)) and (MemInfo.State = MEM_COMMIT) and (MemInfo.Protect in ReadAttributes) then begin Result := (MemInfo.RegionSize - (Address - DWord(MemInfo.BaseAddress))); if (Result < Size) then begin repeat Tmp := GetReadableSize((DWord(MemInfo.BaseAddress) + MemInfo.RegionSize), (Size - Result)); if (Tmp > 0) then Inc(Result, Tmp) else Result := 0; until (Result >= Size) or (Tmp = 0); end; end; end;
2.上接: function IsValidBlockAddr(Address, Size: DWord): Boolean; begin Result := (GetReadableSize(Address, Size) >= Size); end;function ConvertAddress(Addr: DWord): DWord; type TJMPCode = packed record JMPOpCode: Word; JMPPtr: PDWord; MOVOpCode: Word; end; PJMPCode = ^TJMPCode; var JMP: PJMPCode; begin Result := Addr; if (IsValidBlockAddr(Addr, 8)) then begin JMP := PJMPCode(Addr); if (JMP^.JMPOpCode = $25FF) and (IsValidBlockAddr(DWord(JMP^.JMPPtr), 4)) then Result := JMP^.JMPPtr^; end; end;//------------------------------------------------------------------------------function GetVirtualMethod(AClass: TClass; const Index: Integer): Pointer; begin Result := PPointer(Integer(AClass) + (Index * 4))^ end;procedure SetVirtualMethod(AClass: TClass; Index: Integer; Method: Pointer); var PatchAddress: PPointer; OldProtectionCode: DWord; begin PatchAddress := PPointer(Integer(AClass) + (Index * 4)); if (FindHInstance(PatchAddress) = 0) then Exit; // Check for unloaded module... VirtualProtect(PatchAddress, 4, PAGE_EXECUTE_READWRITE, @OldProtectionCode); PatchAddress^ := Method; VirtualProtect(PatchAddress, 4, OldProtectionCode, @OldProtectionCode); FlushInstructionCache(GetCurrentProcess, PatchAddress, 4); end;function HookVirtualMethod(AClass: TClass; Index: Integer; Method: Pointer): Pointer; var HData: PHookedData; n: Integer; begin Result := nil; if (Assigned(HookedMethodsList)) then begin HookedMethodsList.Lock; try Result := GetVirtualMethod(AClass, Index); if (Result = Method) then begin // Just hooked... for n := 0 to (HookedMethodsList.Count - 1) do begin if ((HookedMethodsList[n]^.ClassType = AClass) and (HookedMethodsList[n]^.Index = Index)) then begin Result := HookedMethodsList[n]^.OriginalMethod; Break; end; end; end else begin // First hook... SetVirtualMethod(AClass, Index, Method); New(HData); HData^.ClassType := AClass; HData^.OriginalMethod := Result; HData^.Index := Index; HookedMethodsList.Add(HData); end; finally HookedMethodsList.Unlock; end; end; end;function UnhookVirtualMethod(AClass: TClass; Index: Integer): Boolean; var n: Integer; begin Result := False; if (Assigned(HookedMethodsList)) then begin HookedMethodsList.Lock; try for n := 0 to (HookedMethodsList.Count - 1) do begin if ((HookedMethodsList[n]^.ClassType = AClass) and (HookedMethodsList[n]^.Index = Index)) then begin SetVirtualMethod(AClass, Index, HookedMethodsList[n]^.OriginalMethod); HookedMethodsList.Delete(n); Result := True; Break; end; end; finally HookedMethodsList.Unlock; end; end; end;
可以用一些HOOK,HOOK 系统API readfile,readfileex之类的就行了
3.只能回覆三次,PAS太長了,LZ還是提供一個油箱吧,我將文件發給你,自己動手測試一下試試看,這裡先省略部分PAS代碼,把調用部分貼出來,供你參考function TryHookDllProcedureEx(ImportModules: array of string; ExportModule, ProcName: string; NewProc: Pointer; var CallProc: Pointer; CanFail: Boolean): Boolean; var TmpProc, OldProc: Pointer; HModule: THandle; n: integer; begin Result := False; OldProc := GetProcAddress(GetModuleHandle(PChar(ExportModule)), PChar(ProcName)); for n := low(ImportModules) to high(ImportModules) do begin HModule := GetModuleHandle(PChar(ImportModules[n])); if (HModule <> 0) then begin TmpProc := HookDllProcedure(HModule, ExportModule, OldProc, NewProc, ExportModule + '.' + ProcName, CanFail, False); Result := (Result) or (TmpProc <> nil); end; end; CallProc := OldProc; // WARNING don't move to HERE!!! end;function TryHookProcedureEx(ExportModule, ProcName: string; NewProc: Pointer; var CallProc: Pointer): Boolean; var TmpProc, OldProc: Pointer; begin Result := False; OldProc := GetProcAddress(GetModuleHandle(PChar(ExportModule)), PChar(ProcName)); TmpProc := nil; if Assigned(OldProc) then TmpProc := HookProcedureEx(OldProc, NewProc, ProcName); Result := (Result) or (TmpProc <> nil); CallProc := TmpProc; // WARNING don't move to HERE!!! end;function HookDllProcedureEx(ImportModule, ExportModule, ProcName: string; NewProc: Pointer): Pointer; var OldProc: Pointer; begin OldProc := GetProcAddress(GetModuleHandle(PChar(ExportModule)), PChar(ProcName)); Result := HookDllProcedure(GetModuleHandle(PChar(ImportModule)), ExportModule, OldProc, NewProc, ExportModule + '.' + ProcName, False, False); end;//------------------------------------------------------------------------------procedure Init; begin DllList := TList.Create; HookedMethodsList := THookedMethodsList.Create; HookedProcedures := TList.Create; end;procedure Done; var n: Integer; P: PSaveDLLProc; PHookedBlock: PHookedProcedure; begin for n := 0 to DllList.Count - 1 do begin P := PSaveDLLProc(DllList[n]); HookDLLProcedure(P^.HookModule, P^.ExportModule, P^.NewProc, P^.OldProc, '', True, True); Dispose(P); end; DllList.Free; DllList := nil; HookedMethodsList.Free; HookedMethodsList := nil; for n := HookedProcedures.Count - 1 downto 0 do begin PHookedBlock := HookedProcedures[n]; UnhookProcedure(PHookedBlock^.OriginalProc); end; HookedProcedures.Free; HookedProcedures := nil; end;//------------------------------------------------------------------------------ procedure SafeExec(Proc: TProc; Section: string); var Error: string; begin try Proc; except on Err: TObject do begin if (Err is EIgnoreException) then raise; if (@CriticalError <> nil) then begin CriticalError(Format('%s (Address: %s)', [Section, IntToHex(DWord(@Proc), 8)])); Abort; end else begin if (ExceptObject is Exception) then Error := Exception(ExceptObject).Message else Error := 'General internal error.'; raise Exception.CreateFmt('Critical error at: "%s"'#13#10'Error: "%s".', [Section, Error]); end; end; end; end;//------------------------------------------------------------------------------ initialization SafeExec(Init, 'EHook.Init');finalization SafeExec(Done, 'EHook.Done');end. 下麵是示例程式: 代碼: type //保存原API函數位址 Kernel_WriteFile: function(hFile: Integer; const Buffer; nNumberOfBytesToWrite: Cardinal; var lpNumberOfBytesWritten: Cardinal; lpOverlapped: Pointer): Integer; stdcall; //自訂api函數 function MyWriteFile(hFile: THandle; Buffer:PPChar; nNumberOfBytesToWrite: DWORD; var lpNumberOfBytesWritten: DWORD; lpOverlapped: POverlapped): BOOL; stdcall; var i:DWORD; begin //將所有寫入的資料取反 for i:=LongWord(Buffer) to LongWord(Buffer)+nNumberOfBytesToWrite-1 do begin A:=PByte(i)^; A:=not A; PByte(i)^:=A; end; //調用原來的系統檔 Result:=Kernel_WriteFile(hFile, Buffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped); end; 上面準備好了使用者自訂API,和保存系統API函數指標. 下面介紹用法: 1.對單獨模組進行API Hook 例如,假設我們的程式中包括了Mapx5.ocx, lin.dll和其他一些dll模組, 我們只想Hook程式的MapX5.OCX和 Lin.dll兩個模組的WriteFile這個API函數, 調用函數TryHookDllProcedureEx: 代碼: S:=ExtractFilePath(Application.ExeName);//獲取路徑 // Hooked "WriteFile" Windows API... TryHookDllProcedureEx( [S+'MapX5.OCX', S+'Lin.dll'], //僅改變Mapx5.ocx, lin.dll兩個模組的WriteFile功能 kernel32, 'WriteFile', @HookedWriteFile, @Kernel_WriteFile, True); 2.對所有模組進行API Hook 更簡單,調用函數TryHookProcedureEx 代碼: TryHookProcedureEx( kernel32, 'CreateFileA', @MyCreateFileA, @Kernel_CreateFileA); Unhook某個函數: 更更簡單,調用 UnHookProcedure: 代碼: UnHookProcedure(@Kernel_WriteFile);
本程式來自EurekaLog for Delphi中的EHook.pas,
經過修改後可以單獨運行.
本單元實現了兩種API Hook技術.
一是採用了更改各個模組中函數入口指標,實現各模組調用API時被 Hook.優點是可以選擇性的對某個模組(OCX,DLL..)訪問
某個API時被hook.
二是採用了所有模組都被Hook,通過修改API函數第一條指令為跳轉到使用者API,備份原API內容.用戶可以在自己定義的API上調用原來的API.
本單元還實現了UnHook.
是一個較完整的API Hook 庫程式
有興趣的朋友可以將這個API Hook Lib擴展到對其他進程的API Hook.
本文末尾將介紹兩種Hook技術的使用方法.1.
EHookLib.pas:
代碼:
{************************************************}
{ }
{ EurekaLog v 6.x }
{ Hook Unit - EHook }
{ }
{ Copyright (c) 2001 - 2007 by Fabio Dell'Aria }
{ }
{************************************************}unit EHookLIB;//{$I Exceptions.inc}interfaceuses Windows; type
THandle = Cardinal;
PPointer = ^Pointer;
PShortInt = ^ShortInt;function HookProcedureEx(ProcAddr, NewProc: Pointer; ProcName: string): Pointer;
function UnhookProcedure(ProcAddr: Pointer): Boolean;function HookDllProcedureEx(ImportModule, ExportModule, ProcName: string;
NewProc: Pointer): Pointer;
function TryHookDllProcedureEx(ImportModules: array of string;
ExportModule, ProcName: string; NewProc: Pointer;
var CallProc: Pointer; CanFail: Boolean): Boolean;
function TryHookProcedureEx(ExportModule, ProcName: string; NewProc: Pointer;
var CallProc: Pointer): Boolean;function HookVirtualMethod(AClass: TClass; Index: Integer; Method: Pointer): Pointer;
function UnhookVirtualMethod(AClass: TClass; Index: Integer): Boolean;procedure JumpToMem(Addr, Jump: Pointer);function GetFunctionSize(Addr, MaxSize: DWord): DWord;
function GetAsmSize(Start: Pointer; var Size: Byte): Boolean;var
CriticalError: procedure (const Section: string) = nil;implementationuses Classes, SysUtils;const
EProcNullStr = 'Cannot hook a null procedure ("%s").';
ESharedAreaStr = 'Cannot hook the module "%s" located into the shared-area.';
EHookingErrorStr = 'Cannot hook the procedure "%s".'; SharedMem = $7FFFFFFF; // Don't use major value because Delphi3 don't support it. ModRmMod = $C0; // XX??????
ModRmRM = $07; // ?????XXX OperSizeOver = $66; // Change the operand size from 32 to 16/8 bits.
AddrSizeOver = $67; // Change the address size from 32 to 16/8 bits. OpCodePrefixes: set of Byte =
[$F0, $F2, $F3, $2E, $36, $3E, $26, $64, $65, OperSizeOver, AddrSizeOver]; OpCodeShortJump: set of Byte = [$70..$7F, $E0..$E3, $EB]; // 1 OpCode byte OpCodeReturn: set of Byte = [$C2, $C3..$CA, $CB]; // "Return" first byte OpCodes OpCodeLongJump1Byte: set of Byte = [$E8..$E9]; // 1 OpCode byte OpCodeLongJump2Bytes: set of Byte = [$80..$8F]; // 2 OpCode bytes, 1th = $0F AsmConst: array [0..255] of Byte = ($EE, $EE, $EE, $EE, $F1, $0B, $00, $00,
$0E, $0E, $FE, $FE, $F1, $EB, $00, $FF, $EE, $EE, $EE, $EE, $E1, $EB, $E0,
$E0, $EE, $FE, $FE, $FE, $F1, $FB, $F0, $F0, $EE, $EE, $EE, $EE, $F1, $FB,
$FF, $F0, $EE, $EE, $EE, $EE, $E1, $EB, $EF, $E0, $0E, $0E, $0E, $0E, $01,
$0B, $FF, $F0, $FE, $FE, $FE, $FE, $F1, $FB, $FF, $F0, $E0, $E0, $E0, $E0,
$E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0,
$E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0,
$EE, $EE, $EF, $EF, $EF, $EF, $EB, $EE, $E1, $EE, $F0, $F0, $E0, $E0, $E1,
$E1, $E1, $E1, $E1, $E1, $E1, $01, $F1, $F1, $F1, $F1, $F1, $F1, $E1, $E1,
$BE, $BE, $BE, $BE, $BE, $BE, $BE, $BE, $BE, $BE, $BE, $BE, $BE, $BE, $BE,
$BE, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $ED, $E0, $E0, $E0,
$E0, $E0, $04, $04, $04, $E4, $E0, $E0, $E0, $E0, $01, $0B, $00, $E0, $E0,
$E0, $E0, $E0, $E1, $E1, $E1, $E1, $E1, $E1, $E1, $E1, $FB, $FB, $EB, $EB,
$EB, $EB, $EB, $EB, $EE, $EE, $E2, $E0, $EE, $EE, $EE, $EE, $03, $00, $02,
$00, $00, $01, $00, $00, $FE, $EE, $EE, $EE, $E1, $E1, $F0, $E0, $EE, $EE,
$EE, $EE, $EE, $EE, $EE, $EE, $E1, $E1, $E1, $E1, $E1, $E1, $F1, $E1, $EB,
$EB, $ED, $E1, $E0, $E0, $E0, $E0, $FF, $E0, $EF, $EF, $E0, $E0, $EE, $EE,
$E0, $E0, $E0, $E0, $E0, $E0, $EE, $FE);type
EHookError = class(Exception);
EProcNull = class(EHookError);
EHookingError = class(EHookError);
ESharedArea = class(EHookError); EIgnoreException = class(Exception);
TProc = procedure; TRedirectOpCodes = packed record
JMPOpCode: Byte;
JMPDistance: DWord;
end; TPrefixes = set of Byte; THookedProcedure = record
OriginalProc, HookedBlockPt: Pointer;
HookedBlockSize: DWord;
POriginalAsmPt: Pointer;
POriginalAsmSize: DWord;
end;
PHookedProcedure = ^ THookedProcedure; PSaveDLLProc = ^TSaveDLLProc;
TSaveDLLProc = packed record
HookModule: THandle;
ExportModule: string;
OldProc, NewProc: Pointer;
end; THookedData = packed record
ClassType: TClass;
OriginalMethod: Pointer;
Index: Integer;
end;
PHookedData = ^THookedData; PWin9xDebugThunk = ^TWin9xDebugThunk;
TWin9xDebugThunk = packed record
PUSH: Byte; // PUSH instruction opcode ($68)
Addr: Pointer; // The actual address of the DLL routine
JMP: Byte; // JMP instruction opcode ($E9)
Rel: Integer; // Relative displacement (a Kernel32 address)
end; IMAGE_IMPORT_DESCRIPTOR = packed record
UnUsed: array [0..11] of Byte;
Name: DWord;
FirstThunk: DWord; // RVA to IAT
end;
PImageImportDescriptor = ^IMAGE_IMPORT_DESCRIPTOR; IMAGE_THUNK_DATA = packed record
Function_: DWord; // PDWord
end;
PImageThunkData = ^IMAGE_THUNK_DATA; PImageDosHeader = ^TImageDosHeader;
TImageDosHeader = packed record // DOS .EXE header
e_magic: Word; // Magic number
UnUsed: array [0..57] of Byte;
_lfanew: LongInt; // File address of new exe header
end; THookedMethodsList = class(TList)
private
FLock: TRTLCriticalSection;
function GetItem(Index: Integer): PHookedData;
protected
public
constructor Create;
destructor Destroy; override;
procedure Lock;
procedure Unlock;
procedure Delete(Index: Integer);
property Items[Index: Integer]: PHookedData read GetItem; default;
end;const
TRedirectOpCodesSize = SizeOf(TRedirectOpCodes);var
HookedProcedures, DllList: TList;
HookedMethodsList: THookedMethodsList;//------------------------------------------------------------------------------{ THookedMethods }constructor THookedMethodsList.Create;
begin
inherited;
InitializeCriticalSection(FLock);
end;function THookedMethodsList.GetItem(Index: Integer): PHookedData;
begin
Result := PHookedData(TList(Self).Items[Index]);
end;procedure THookedMethodsList.Lock;
begin
EnterCriticalSection(FLock);
end;procedure THookedMethodsList.Unlock;
begin
LeaveCriticalSection(FLock);
end;procedure THookedMethodsList.Delete(Index: Integer);
var
Data: PHookedData;
Ptr: Pointer;
begin
Ptr := Items[Index];
Data := PHookedData(Ptr);
Dispose(Data);
inherited;
end;destructor THookedMethodsList.Destroy;
var
I: Integer;
begin
Lock;
try
for I := 0 to HookedMethodsList.Count - 1 do
UnhookVirtualMethod(HookedMethodsList[0]^.ClassType, HookedMethodsList[0]^.Index);
finally
Unlock;
end;
DeleteCriticalSection(FLock);
inherited;
end;//------------------------------------------------------------------------------function GetReadableSize(Address, Size: DWord): DWord;
const
ReadAttributes = [PAGE_READONLY, PAGE_READWRITE, PAGE_WRITECOPY, PAGE_EXECUTE,
PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_EXECUTE_WRITECOPY];
var
MemInfo: TMemoryBasicInformation;
Tmp: DWord;
begin
Result := 0;
if (VirtualQuery(Pointer(Address), MemInfo, SizeOf(MemInfo)) = SizeOf(MemInfo)) and
(MemInfo.State = MEM_COMMIT) and (MemInfo.Protect in ReadAttributes) then
begin
Result := (MemInfo.RegionSize - (Address - DWord(MemInfo.BaseAddress)));
if (Result < Size) then
begin
repeat
Tmp := GetReadableSize((DWord(MemInfo.BaseAddress) + MemInfo.RegionSize), (Size - Result));
if (Tmp > 0) then Inc(Result, Tmp)
else Result := 0;
until (Result >= Size) or (Tmp = 0);
end;
end;
end;
function IsValidBlockAddr(Address, Size: DWord): Boolean;
begin
Result := (GetReadableSize(Address, Size) >= Size);
end;function ConvertAddress(Addr: DWord): DWord;
type
TJMPCode = packed record
JMPOpCode: Word;
JMPPtr: PDWord;
MOVOpCode: Word;
end;
PJMPCode = ^TJMPCode;
var
JMP: PJMPCode;
begin
Result := Addr;
if (IsValidBlockAddr(Addr, 8)) then
begin
JMP := PJMPCode(Addr);
if (JMP^.JMPOpCode = $25FF) and (IsValidBlockAddr(DWord(JMP^.JMPPtr), 4)) then
Result := JMP^.JMPPtr^;
end;
end;//------------------------------------------------------------------------------function GetVirtualMethod(AClass: TClass; const Index: Integer): Pointer;
begin
Result := PPointer(Integer(AClass) + (Index * 4))^
end;procedure SetVirtualMethod(AClass: TClass; Index: Integer; Method: Pointer);
var
PatchAddress: PPointer;
OldProtectionCode: DWord;
begin
PatchAddress := PPointer(Integer(AClass) + (Index * 4));
if (FindHInstance(PatchAddress) = 0) then Exit; // Check for unloaded module...
VirtualProtect(PatchAddress, 4, PAGE_EXECUTE_READWRITE, @OldProtectionCode);
PatchAddress^ := Method;
VirtualProtect(PatchAddress, 4, OldProtectionCode, @OldProtectionCode);
FlushInstructionCache(GetCurrentProcess, PatchAddress, 4);
end;function HookVirtualMethod(AClass: TClass; Index: Integer; Method: Pointer): Pointer;
var
HData: PHookedData;
n: Integer;
begin
Result := nil;
if (Assigned(HookedMethodsList)) then
begin
HookedMethodsList.Lock;
try
Result := GetVirtualMethod(AClass, Index);
if (Result = Method) then
begin // Just hooked...
for n := 0 to (HookedMethodsList.Count - 1) do
begin
if ((HookedMethodsList[n]^.ClassType = AClass) and
(HookedMethodsList[n]^.Index = Index)) then
begin
Result := HookedMethodsList[n]^.OriginalMethod;
Break;
end;
end;
end
else
begin // First hook...
SetVirtualMethod(AClass, Index, Method);
New(HData);
HData^.ClassType := AClass;
HData^.OriginalMethod := Result;
HData^.Index := Index;
HookedMethodsList.Add(HData);
end;
finally
HookedMethodsList.Unlock;
end;
end;
end;function UnhookVirtualMethod(AClass: TClass; Index: Integer): Boolean;
var
n: Integer;
begin
Result := False;
if (Assigned(HookedMethodsList)) then
begin
HookedMethodsList.Lock;
try
for n := 0 to (HookedMethodsList.Count - 1) do
begin
if ((HookedMethodsList[n]^.ClassType = AClass) and
(HookedMethodsList[n]^.Index = Index)) then
begin
SetVirtualMethod(AClass, Index, HookedMethodsList[n]^.OriginalMethod);
HookedMethodsList.Delete(n);
Result := True;
Break;
end;
end;
finally
HookedMethodsList.Unlock;
end;
end;
end;
ExportModule, ProcName: string; NewProc: Pointer;
var CallProc: Pointer; CanFail: Boolean): Boolean;
var
TmpProc, OldProc: Pointer;
HModule: THandle;
n: integer;
begin
Result := False;
OldProc := GetProcAddress(GetModuleHandle(PChar(ExportModule)), PChar(ProcName)); for n := low(ImportModules) to high(ImportModules) do
begin
HModule := GetModuleHandle(PChar(ImportModules[n]));
if (HModule <> 0) then
begin
TmpProc := HookDllProcedure(HModule, ExportModule, OldProc, NewProc,
ExportModule + '.' + ProcName, CanFail, False);
Result := (Result) or (TmpProc <> nil);
end;
end; CallProc := OldProc; // WARNING don't move to HERE!!!
end;function TryHookProcedureEx(ExportModule, ProcName: string; NewProc: Pointer;
var CallProc: Pointer): Boolean;
var
TmpProc, OldProc: Pointer;
begin
Result := False;
OldProc := GetProcAddress(GetModuleHandle(PChar(ExportModule)), PChar(ProcName)); TmpProc := nil;
if Assigned(OldProc) then TmpProc := HookProcedureEx(OldProc, NewProc, ProcName); Result := (Result) or (TmpProc <> nil);
CallProc := TmpProc; // WARNING don't move to HERE!!!
end;function HookDllProcedureEx(ImportModule, ExportModule, ProcName: string;
NewProc: Pointer): Pointer;
var
OldProc: Pointer;
begin
OldProc := GetProcAddress(GetModuleHandle(PChar(ExportModule)), PChar(ProcName)); Result := HookDllProcedure(GetModuleHandle(PChar(ImportModule)), ExportModule,
OldProc, NewProc, ExportModule + '.' + ProcName, False, False);
end;//------------------------------------------------------------------------------procedure Init;
begin
DllList := TList.Create;
HookedMethodsList := THookedMethodsList.Create;
HookedProcedures := TList.Create;
end;procedure Done;
var
n: Integer;
P: PSaveDLLProc;
PHookedBlock: PHookedProcedure;
begin
for n := 0 to DllList.Count - 1 do
begin
P := PSaveDLLProc(DllList[n]);
HookDLLProcedure(P^.HookModule, P^.ExportModule, P^.NewProc, P^.OldProc, '', True, True);
Dispose(P);
end;
DllList.Free;
DllList := nil;
HookedMethodsList.Free;
HookedMethodsList := nil;
for n := HookedProcedures.Count - 1 downto 0 do
begin
PHookedBlock := HookedProcedures[n];
UnhookProcedure(PHookedBlock^.OriginalProc);
end;
HookedProcedures.Free;
HookedProcedures := nil;
end;//------------------------------------------------------------------------------
procedure SafeExec(Proc: TProc; Section: string);
var
Error: string;
begin
try
Proc;
except
on Err: TObject do
begin
if (Err is EIgnoreException) then raise; if (@CriticalError <> nil) then
begin
CriticalError(Format('%s (Address: %s)', [Section, IntToHex(DWord(@Proc), 8)]));
Abort;
end
else
begin
if (ExceptObject is Exception) then Error := Exception(ExceptObject).Message
else Error := 'General internal error.';
raise Exception.CreateFmt('Critical error at: "%s"'#13#10'Error: "%s".', [Section, Error]);
end;
end;
end;
end;//------------------------------------------------------------------------------
initialization
SafeExec(Init, 'EHook.Init');finalization
SafeExec(Done, 'EHook.Done');end.
下麵是示例程式:
代碼:
type
//保存原API函數位址
Kernel_WriteFile: function(hFile: Integer; const Buffer; nNumberOfBytesToWrite: Cardinal;
var lpNumberOfBytesWritten: Cardinal; lpOverlapped: Pointer): Integer; stdcall;
//自訂api函數
function MyWriteFile(hFile: THandle; Buffer:PPChar; nNumberOfBytesToWrite: DWORD;
var lpNumberOfBytesWritten: DWORD; lpOverlapped: POverlapped): BOOL; stdcall;
var
i:DWORD;
begin
//將所有寫入的資料取反
for i:=LongWord(Buffer) to LongWord(Buffer)+nNumberOfBytesToWrite-1 do
begin
A:=PByte(i)^;
A:=not A;
PByte(i)^:=A;
end;
//調用原來的系統檔
Result:=Kernel_WriteFile(hFile, Buffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped);
end;
上面準備好了使用者自訂API,和保存系統API函數指標.
下面介紹用法:
1.對單獨模組進行API Hook
例如,假設我們的程式中包括了Mapx5.ocx, lin.dll和其他一些dll模組,
我們只想Hook程式的MapX5.OCX和 Lin.dll兩個模組的WriteFile這個API函數,
調用函數TryHookDllProcedureEx:
代碼:
S:=ExtractFilePath(Application.ExeName);//獲取路徑
// Hooked "WriteFile" Windows API...
TryHookDllProcedureEx(
[S+'MapX5.OCX', S+'Lin.dll'], //僅改變Mapx5.ocx, lin.dll兩個模組的WriteFile功能
kernel32, 'WriteFile',
@HookedWriteFile, @Kernel_WriteFile, True);
2.對所有模組進行API Hook
更簡單,調用函數TryHookProcedureEx
代碼:
TryHookProcedureEx(
kernel32, 'CreateFileA',
@MyCreateFileA, @Kernel_CreateFileA);
Unhook某個函數:
更更簡單,調用 UnHookProcedure:
代碼:
UnHookProcedure(@Kernel_WriteFile);