Cómo cambiar la implementación (desvío) de una función declarada externamente

Tengo una función de terceros

function DataCompare(const S1, S2: string; APartial: Boolean): Boolean; begin ... end; 

Se usa en otra unidad de terceros.

Deseo reemplazar el cuerpo de la función en tiempo de ejecución con otra implementación nueva.

es posible? Supongo que habrá necesidad de algún truco (ala VirtualMemoryUnprotect). Una solución no ensamblable es muy bienvenida.

Sí, puede hacerlo utilizando las funciones ReadProcessMemory y WriteProcessMemory para parchear el código del proceso actual. Básicamente, se obtiene la dirección del procedimiento o función para parchar y luego se inserta una instrucción Jump en la dirección del nuevo procedimiento.

Verifique este código

 Uses uThirdParty; //this is the unit where the original DataCompare function is declarated type //strctures to hold the address and instructions to patch TJumpOfs = Integer; PPointer = ^Pointer; PXRedirCode = ^TXRedirCode; TXRedirCode = packed record Jump: Byte; Offset: TJumpOfs; end; PAbsoluteIndirectJmp = ^TAbsoluteIndirectJmp; TAbsoluteIndirectJmp = packed record OpCode: Word; Addr: PPointer; end; var DataCompareBackup: TXRedirCode; //Store the original address of the function to patch //this is the implementation of the new function function DataCompareHack(const S1, S2: string; APartial: Boolean): Boolean; begin //here write your own code end; //get the address of a procedure or method of a function function GetActualAddr(Proc: Pointer): Pointer; begin if Proc <> nil then begin if (Win32Platform = VER_PLATFORM_WIN32_NT) and (PAbsoluteIndirectJmp(Proc).OpCode = $25FF) then Result := PAbsoluteIndirectJmp(Proc).Addr^ else Result := Proc; end else Result := nil; end; //patch the original function or procedure procedure HookProc(Proc, Dest: Pointer; var BackupCode: TXRedirCode); var n: {$IFDEF VER230}NativeUInt{$ELSE}DWORD{$ENDIF}; Code: TXRedirCode; begin Proc := GetActualAddr(Proc); Assert(Proc <> nil); //store the address of the original procedure to patch if ReadProcessMemory(GetCurrentProcess, Proc, @BackupCode, SizeOf(BackupCode), n) then begin Code.Jump := $E9; Code.Offset := PAnsiChar(Dest) - PAnsiChar(Proc) - SizeOf(Code); //replace the target procedure address with the new one. WriteProcessMemory(GetCurrentProcess, Proc, @Code, SizeOf(Code), n); end; end; //restre the original address of the hooked function or procedure procedure UnhookProc(Proc: Pointer; var BackupCode: TXRedirCode); var n: {$IFDEF VER230}NativeUInt{$ELSE}Cardinal{$ENDIF}; begin if (BackupCode.Jump <> 0) and (Proc <> nil) then begin Proc := GetActualAddr(Proc); Assert(Proc <> nil); WriteProcessMemory(GetCurrentProcess, Proc, @BackupCode, SizeOf(BackupCode), n); BackupCode.Jump := 0; end; end; //Patch the original procedure or function procedure HookDataCompare; begin //look how is passed the address of the original procedure (including the unit name) HookProc(@uThirdParty.DataCompare, @DataCompareHack, DataCompareBackup); end; //restore the address of the original procedure or function procedure UnHookDataCompare; begin UnhookProc(@uThirdParty.DataCompare, DataCompareBackup); end; initialization HookDataCompare; finalization UnHookDataCompare; end. 

Ahora, cada vez que ejecuta su aplicación y se realiza una llamada a la función DataCompare se ejecutará la instrucción de salto (a la nueva dirección), lo que provocará que se DataCompareHack función DataCompareHack .

Creo que JCL tiene algunos utils para este tipo de cosas … No lo he usado yo mismo, pero he tenido una mirada rápida y los siguientes elementos parecen prometedores:

 jclSysUtils.WriteProtectedMemory() jclPeImage.TJclPeMapImgHooks.ReplaceImport() 

Creo que jclHookExcept.JclHookExceptions() demuestra cómo usarlos.