3

我正在尝试在使用 microsoft visual studio 2010 编译的应用程序中挂钩一个函数,并从 delphi 2010 dll 启用 __fastcall,但我不熟练地弄清楚如何绕过以下问题:

C++ 函数是:

     void __fastcall function(int arg1; char* arg2);

我正在尝试类似的事情(使用uallHook):

    var FTextMessage : procedure(Modo: integer; Msg: pchar); register;
    procedure onTextMessage(Modo: integer; Msg: pchar); register;
    begin
      ShowMessage(inttostr(Modo) + ' - ' + string(Msg));
      FTextMessage(Modo, Msg);
    end;
    begin
    HookCode(Ptr($574210), @onTextMessage, @FTextMessage);
    end.

这会导致调试/崩溃。

所以,我发现:

“Microsoft 或 GCC[4] __fastcall[5] 约定(又名 __msfastcall)传递适合 ECX 和 EDX 的前两个参数(从左到右评估)。剩余的参数从右到左推入堆栈。”

Borland fastcall 从左到右评估参数,它通过 EAX、EDX、ECX 传递三个参数。剩余的参数被压入堆栈,也是从左到右。 [6] 它是 Embarcadero Delphi 的 32 位编译器的默认调用约定,其中 >it 被称为寄存器。

取自: http ://en.wikipedia.org/wiki/X86_calling_conventions

这就是问题所在,borland fastcall 和microsoft __fastcall 是不同的。

所以我想我需要在 Hook 函数中使用一段汇编代码来对齐寄存器或其他东西,但我还不能解决这个问题。

任何帮助,将不胜感激。

Edit1:David Heffernan 回答部分有效。(它只在显示消息之前有效)

     var FTextMessage : procedure(Modo: integer;Msg: PAnsiChar); register;

     procedure onTextMessage(Modo: integer;Msg: PAnsiChar); register;
     begin
       ShowMessage(inttostr(Modo) + ' - ' + string(Msg));
       asm
         MOV ECX,Modo
         MOV EDX,Msg
         JMP FTextMessage
       end;  // I got a crash trying or without trying to restore the registers before executing the JMP FTextMessage
     //  FTextMessage(Modo,Msg); // < Also this method doesnt work either, if I call the Original function from here (without executing asm code above) i got a crash too. (this is what i was meaning with "to correct the registers")
     end;

     procedure onTextMessageWrapper(Modo: Integer;Msg: PAnsiChar); register;
     asm
       MOV EDX,ECX  // < Moving ECX to EDX, I'm able to Access Modo and Msg correctly in onTextMessage procedure.
       JMP onTextMessage
     end;

     begin
       HookCode(Ptr($574210), @onTextMessageWrapper, @FTextMessage);
     end.
4

2 回答 2

3

C++ 函数在ECX和中传递参数EDX。因此,您只需要将它们复制到 Delphi 期望的寄存器中:EAXEDX分别。由于第二个参数已经在正确的寄存器中,所以您需要的是:

procedure onTextMessage(Modo: Integer; Msg: PAnsiChar); register;
begin
  ...
end;

procedure onTextMessageWrapper(Modo: Integer; Msg: PAnsiChar); register;
asm
  MOV EAX,ECX
  JMP onTextMessage
end;

我已经在两个函数中完成了这项工作,这样我就可以编写一个纯asm函数来进行寄存器交换。我们希望纯粹asm避免所有的序言。所以,onTextMessageWrapper是你的钩子函数。

另请注意,由于您使用的是 Unicode Delphi,因此您需要PAnsiChar匹配char*.

我所知道的调用约定的最佳参考是Agner Fog。你会在那里找到所有血淋淋的细节。重要的一点是,在 Windows x86 上,EAX、ECX 和 EDX 寄存器是易失性或暂存寄存器。这意味着被调用者可以修改这些寄存器而无需恢复它们。这就是为什么我在上面修改 EAX 并且不费心把它放回去的原因。

于 2012-12-04T13:01:48.963 回答
-1

另一种尝试是更改此函数签名,如下所示

来自:procedure onTextMessageWrapper(Modo: Integer; Msg: PAnsiChar); 登记; To: 过程 onTextMessageWrapper; 登记;

于 2012-12-04T21:06:54.957 回答