2012-01-23 2 views
17

Я хочу исправить рутинный вызов, чтобы иметь возможность обрабатывать его сам с некоторыми изменениями. Я пишу загрузчик ресурсов. Я хочу, чтобы патч LoadResourceModule Delphi и InitInheritedComponent подпрограммы с моей. Я проверил PatchAPI-вызов в блоке MadExcept.pas, но не смог понять, могу ли я использовать его для моего проекта.Патч обычный вызов в delphi

Я хочу что-то вроде

мой ехе во время выполнения вызовов -> LoadResourceModule -> Переход -> MyCustomResourceModule ...

Все указатели на это было бы очень полезно.

+3

Это называется 'detour' проверить этот вопрос [Как изменить реализацию (объезд) от внешне заявленной функции] (http://stackoverflow.com/questions/6905287/ как-to-change-the-implementation-detour-of-an-external-declaration-function) – RRUZ

+0

Я просто думал об этом же сегодня - поэтому использование этой техники позволило бы, например, добавить код в потоке компонентов (из DFM к применению)? Так, например, у меня могло бы быть центральное место для регистрации используемых классов компонентов или обеспечения качества («не используйте классы BDE!» Или эту старую версию компонента X!)? – mjn

+2

@mjn Существуют другие точки расширения, которые позволяют сделать это более легко. Например, 'TReader.OnFindComponentClass'. Код исправления всегда должен быть последним, когда ничего другого не может выполнить. –

ответ

16

Я использую следующий код:

procedure PatchCode(Address: Pointer; const NewCode; Size: Integer); 
var 
    OldProtect: DWORD; 
begin 
    if VirtualProtect(Address, Size, PAGE_EXECUTE_READWRITE, OldProtect) then 
    begin 
    Move(NewCode, Address^, Size); 
    FlushInstructionCache(GetCurrentProcess, Address, Size); 
    VirtualProtect(Address, Size, OldProtect, @OldProtect); 
    end; 
end; 

type 
    PInstruction = ^TInstruction; 
    TInstruction = packed record 
    Opcode: Byte; 
    Offset: Integer; 
    end; 

procedure RedirectProcedure(OldAddress, NewAddress: Pointer); 
var 
    NewCode: TInstruction; 
begin 
    NewCode.Opcode := $E9;//jump relative 
    NewCode.Offset := NativeInt(NewAddress)-NativeInt(OldAddress)-SizeOf(NewCode); 
    PatchCode(OldAddress, NewCode, SizeOf(NewCode)); 
end; 

Вы бы реализовать свой крюк/патч/объезд по телефону RedirectProcedure:

RedirectProcedure(@LoadResourceModule, @MyLoadResourceModule); 

Это будет работать на 32-битном коде. Он также будет работать для 64-битного кода при условии, что как старые, так и новые функции находятся в одном и том же исполняемом модуле. В противном случае расстояние перехода может превышать диапазон 32-битного целого.

Мне было бы очень интересно, если бы кто-нибудь мог предоставить альтернативу, которая работала для 64-битного адресного пространства, независимо от того, насколько далеко друг от друга были два адреса.

+6

Может быть хорошей идеей либо * переустановить * перенаправление, либо убедиться, что при закрытии приложения не будет разрыва кода - может быть сделан перенаправленный вызов (например, RTL или другой блок, загруженный до блока перенаправления), и прыгать к некоторому неинициализированному коду. –

+0

@Arnaud Это может быть правдой. Во всем моем использовании этого я перенаправляю до того, как будут сделаны какие-либо вызовы, или это обычная процедура без побочных эффектов, и поэтому отладка не имеет значения. –

+0

@DavidHeffernan. Только для размышления, как я могу вызвать старую процедуру, если я хочу получить значение по умолчанию и затем работать над этим значением. Поскольку в приведенном выше коде мы перезаписываем адрес старой процедуры, чтобы перейти к новой процедуре. Что-то вроде MyLoadResourceModule внутренне использует LoadResourceModule и делает что-то дополнительное .... –

7

Для этого уже существует Delphi detours library.

The Delphi Detours это библиотека позволяет подключать функции Дельфи и Windows API .Это обеспечивает простой способ для вставки и удалить крючок.

Особенности:

  • Поддержка x86 и x64 архитектуры.
  • Разрешить вызов оригинальной функции через функцию Trampoline.
  • Поддержка Multi Крюк.
  • COM/Интерфейсы/поддержка win32api.
  • Поддержка COM vtable patching.
  • Полностью потокобезопасный код для соединения и отсоединения.
  • Поддержка метода привязки объектов.
  • Поддерживается Delphi 7/2005-2010/XE-XE7.
  • Поддержка Lazarus/FPC.
  • Поддерживается 64-разрядный адрес.
  • В библиотеке не используется внешняя библиотека.
  • Библиотека может вставлять и удалять крючок в любое время.
  • Библиотека содержит библиотеку InstDecode, которая позволяет вам декодировать инструкции asm (x86 & x64).
3

Я изменил код Дэвида Хеффернэн для 64-битной поддержки и косвенного перехода к методам в BPL. С некоторой помощью: http://chee-yang.blogspot.com.tr/2008/11/hack-into-delphi-class.html

type 
    PAbsoluteIndirectJmp = ^TAbsoluteIndirectJmp; 
    TAbsoluteIndirectJmp = packed record 
    OpCode: Word; // $FF25(Jmp, FF /4) 
    Addr: DWORD; // 32-bit address 
        // in 32-bit mode: it is a direct jmp address to target method 
        // in 64-bit mode: it is a relative pointer to a 64-bit address used to jmp to target method 
    end; 

    PInstruction = ^TInstruction; 
    TInstruction = packed record 
    Opcode: Byte; 
    Offset: Integer; 
    end; 


function GetActualAddr(Proc: Pointer): Pointer; 
begin 
    Result := Proc; 
    if Result <> nil then 
    if PAbsoluteIndirectJmp(Result)^.OpCode = $25FF then // we need to understand if it is proc entry or a jmp following an address 
{$ifdef CPUX64} 
     Result := PPointer(NativeInt(Result) + PAbsoluteIndirectJmp(Result)^.Addr + SizeOf(TAbsoluteIndirectJmp))^; 
     // in 64-bit mode target address is a 64-bit address (jmp qword ptr [32-bit relative address] FF 25 XX XX XX XX) 
     // The address is in a loaction pointed by (Addr + Current EIP = XX XX XX XX + EIP) 
     // We also need to add (instruction + operand) size (SizeOf(TAbsoluteIndirectJmp)) to calculate relative address 
     // XX XX XX XX + Current EIP + SizeOf(TAbsoluteIndirectJmp) 
{$else} 
     Result := PPointer(PAbsoluteIndirectJmp(Result)^.Addr)^; 
     // in 32-bit it is a direct address to method 
{$endif} 
end; 

procedure PatchCode(Address: Pointer; const NewCode; Size: Integer); 
var 
    OldProtect: DWORD; 
begin 
    if VirtualProtect(Address, Size, PAGE_EXECUTE_READWRITE, OldProtect) then //FM: remove the write protect on Code Segment 
    begin 
    Move(NewCode, Address^, Size); 
    FlushInstructionCache(GetCurrentProcess, Address, Size); 
    VirtualProtect(Address, Size, OldProtect, @OldProtect); // restore write protection 
    end; 
end; 

procedure RedirectProcedure(OldAddress, NewAddress: Pointer); 
var 
    NewCode: TInstruction; 
begin 
    OldAddress := GetActualAddr(OldAddress); 

    NewCode.Opcode := $E9;//jump relative 
    NewCode.Offset := NativeInt(NewAddress) - NativeInt(OldAddress) - SizeOf(NewCode); 

    PatchCode(OldAddress, NewCode, SizeOf(NewCode)); 
end; 

 Смежные вопросы

  • Нет связанных вопросов^_^