GenCodeHook, Version 1.5 (alt)
Hinweis: dies ist nicht die neueste Version!
Datei: GenCodeHook.pas
{ GenCodeHook.pas Delphi unit containing functions to do a generic code hook by replacing the code location to be patched by a jump to the new location. Additionally, a new code fragment is created that allows to call the old function. Version 1.5 - Always find the most current version at http://flocke.vssd.de/prog/code/pascal/codehook/ Copyright (C) 2005, 2006 Volker Siebert <flocke@vssd.de> All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. } unit GenCodeHook; interface uses Windows, SysUtils; { Creates a generic code hook by patching "Proc" to execute "NewProc" instead. Before patching, "OldProc" receives a pointer to a function that can be used to call the old code (though not at the same location). } function CreateGenericCodeHook(Proc, NewProc: pointer; var OldProc: pointer): boolean; { Restore a patched location and free the memory } procedure RemoveGenericCodeHook(var OldProc: pointer); { Function that patches protected memory } function PatchMemory(Where: pointer; const What; Size: integer; PadSize: integer = 0; PadByte: byte = $90): boolean; implementation uses CodeLen, CodeMem; {$DEFINE ABSOLUTE_ADDRESSES} {.$DEFINE ABS_JUMP_INDIRECT} type TNearJmp = packed record Insn: byte; // $E9 = jmp disp32 Disp: integer; end; TAbsJmp = packed record {$IFDEF ABS_JUMP_INDIRECT} Insn1: byte; // $FF Insn2: byte; // $25 (%00-100-101) = jmp dword ptr [offs] Addr: pointer; {$ELSE} Insn1: byte; // $68 = push imm32 Addr: pointer; Insn2: byte; // $C3 = ret {$ENDIF} end; const SIGNATURE = $68636746; // 'Fgch' {$IFDEF ABSOLUTE_ADDRESSES} PATCH_SIZE = SizeOf(TAbsJmp); {$ELSE} PATCH_SIZE = SizeOf(TNearJmp); {$ENDIF} BACKUP_SIZE = MAX_IA32_INSTRUCTION_LENGTH + PATCH_SIZE; type TGenCodeHookHeader = record BupLen: integer; Backup: array [0 .. BACKUP_SIZE - 1] of byte; Location: pointer; {$IFDEF ABS_JUMP_INDIRECT} JumpHere: pointer; {$ENDIF} Signature: cardinal; end; PGenCodeHookStruct = ^TGenCodeHookStruct; TGenCodeHookStruct = record Header: TGenCodeHookHeader; Code: array [0 .. SizeOf(TNearJmp) - 1] of byte; // dynamisch end; { Pointer increment } function pinc(Ptr: pointer; incr: integer = 1): pointer; begin Result := pointer(integer(Ptr) + incr); end; { Function that patches protected memory } function PatchMemory(Where: pointer; const What; Size: integer; PadSize: integer = 0; PadByte: byte = $90): boolean; var OldProtect: cardinal; begin if Size <= 0 then Result := true else begin Result := VirtualProtect(Where, Size, PAGE_EXECUTE_READWRITE, OldProtect); if Result then try if Size = 4 then PCardinal(Where)^ := PCardinal(@What)^ else if Size = 2 then PWord(Where)^ := PWord(@What)^ else if Size = 1 then PByte(Where)^ := PByte(@What)^ else begin PByte(Where)^ := $CC; // int3 Move(pinc(@What)^, pinc(Where)^, Size - 1); PByte(Where)^ := PByte(@What)^; end; if Size < PadSize then begin FillChar(pinc(Where, Size)^, PadSize - Size, PadByte); Size := PadSize; end; FlushInstructionCache(GetCurrentProcess(), Where, Size); finally VirtualProtect(Where, Size, OldProtect, OldProtect); end; end; end; { Calculate the displacement from Source to Target (Intel IA-32 use pc-relative displacements for jumps and calls) } function CalcDisplacement(Source, Target: pointer): integer; begin Result := integer(Target) - (integer(Source) + SizeOf(integer)); end; { Creates a generic code hook by patching "Proc" to execute "NewProc" instead. Before patching, "OldProc" receives a pointer to a function that can be used to call the old code (though not at the same location). } function CreateGenericCodeHook(Proc, NewProc: pointer; var OldProc: pointer): boolean; var mbi: TMemoryBasicInformation; len, bup: integer; cop: PGenCodeHookStruct; nj: TNearJmp; {$IFDEF ABSOLUTE_ADDRESSES} aj: TAbsJmp; {$ENDIF} map: TCodeMap; begin Result := false; OldProc := nil; // PSDK quote: "Windows Me/98/95: You cannot use VirtualProtect on // any memory region located in the shared virtual address space // (from 0x80000000 through 0xBFFFFFFF)." // // -> Hooking will fail! // Get size of memory region if VirtualQuery(Proc, mbi, SizeOf(mbi)) < SizeOf(mbi) then exit; // Analyze CPU code and find amount of memory to copy len := integer(mbi.RegionSize) - (integer(Proc) - integer(mbi.BaseAddress)); map := AnalyzeCpuInstructionSequence(Proc, len); len := LengthOfCpuInstructionSequence(Proc, Map, PATCH_SIZE); {$IFDEF ABSOLUTE_ADDRESSES} if len = 0 then len := LengthOfCpuInstructionSequence(Proc, Map, SizeOf(TNearJmp)); {$ENDIF} if len = 0 then exit; // Calculate the backup size (the remaining // bytes will be filled with NOPs later) bup := 0; while bup < PATCH_SIZE do begin if (Map[bup] and AISQ_SIZE_MASK) = 0 then inc(bup) else inc(bup, Map[bup] and AISQ_SIZE_MASK); end; // Get new memory cop := AllocCodeMem(SizeOf(TGenCodeHookStruct) + len); try // Initialize the header cop^.Header.Location := Proc; cop^.Header.Signature := SIGNATURE; {$IFDEF ABS_JUMP_INDIRECT} cop^.Header.JumpHere := NewProc; {$ENDIF} // Make backup copy cop^.Header.BupLen := bup; Move(Proc^, cop^.Header.Backup[0], bup); // Copy instructions (and do relocations) CopyCpuInstructionSequence(Proc, len, Map, @cop^.Code[0]); // place jump to old procedure at the end nj.Insn := $E9; // call disp32 nj.Disp := CalcDisplacement(@cop^.Code[len + 1], pinc(Proc, len)); Move(nj, cop^.Code[len], SizeOf(TNearJmp)); // Pointer to old procedure OldProc := @cop^.Code[0]; // Finally, patch the target memory {$IFDEF ABSOLUTE_ADDRESSES} if len = SizeOf(TNearJmp) then begin //nj.Insn := $E9; // call disp32 nj.Disp := CalcDisplacement(pinc(Proc), NewProc); Result := PatchMemory(Proc, nj, SizeOf(TNearJmp), bup); end else begin {$IFDEF ABS_JUMP_INDIRECT} aj.Insn1 := $FF; // jmp dword ptr [offs] aj.Insn2 := $25; // ... aj.Addr := @cop^.Header.JumpHere; {$ELSE} aj.Insn1 := $68; // push imm32 aj.Addr := NewProc; aj.Insn2 := $C3; // ret {$ENDIF} Result := PatchMemory(Proc, aj, SizeOf(TAbsJmp), bup); end; {$ELSE} //nj.Insn := $E9; nj.Disp := CalcDisplacement(pinc(Proc), NewProc); Result := PatchMemory(Proc, nj, SizeOf(TNearJmp), bup); {$ENDIF} if not Result then begin OldProc := nil; FreeCodeMem(cop); end; except OldProc := nil; FreeCodeMem(cop); raise; end; end; { Restore a patched location and free the memory } procedure RemoveGenericCodeHook(var OldProc: pointer); var cop: PGenCodeHookStruct; begin if OldProc = nil then exit; cop := pinc(OldProc, -SizeOf(TGenCodeHookHeader)); if (cop^.Header.Signature <> SIGNATURE) then exit; PatchMemory(cop^.Header.Location, cop^.Header.Backup[0], cop^.Header.BupLen); OldProc := cop^.Header.Location; cop^.Header.Signature := 0; FreeCodeMem(cop); end; end. |