我试图在使用微软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位编译器的默认调用约定,其中>它被称为寄存器。
取自: http://en.wikipedia.org/wiki/X86_calling_conventions
这就是问题,borland fastcall和microsoft __fastcall是不同的。
所以我认为我需要在Hook函数中使用一段汇编代码来对齐寄存器或其他东西,但我还是无法解决这个问题。
任何帮助都将不胜感激。
EDIT1: David Heffernan答案部分工作。 (它只能在showmessage之前使用)
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.
答案 0 :(得分:3)
C ++函数正在ECX
和EDX
传递参数。所以你只需将它们复制到Delphi期望的寄存器:EAX
和EDX
。由于第二个参数已经在正确的寄存器中,所以您需要的是:
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并且不打算把它放回去。
答案 1 :(得分:-1)
另一种尝试是更改此功能签名,如下所示
From:procedure onTextMessageWrapper(Modo:Integer; Msg:PAnsiChar);寄存器; To:procedure onTextMessageWrapper;寄存器;