我正在尝试将一些C钩子放入其他人在asm中编写的代码中。我不太了解x86 asm,我想确保我安全地这样做。
到目前为止,我有这么多:
EXTERN _C_func
CALL _C_func
这似乎有效,但我确信那是危险的。至少它可能会破坏EAX,甚至可能更多。
所以我认为我需要知道的是:C会破坏哪些寄存器以及如何确保正确保存寄存器?还有什么我可以做的,以确保我可以安全地将钩子插入任意位置?
我正在使用Visual Studio作为C函数,如果这有帮助的话。 谢谢。
更新:
我查了几个人建议的“偏执狂”方法,它看起来像这样:
pushfd
pushad
call _C_func
popad
popfd
AD =(A)ll通用寄存器
FD =(F)滞后
(我不确定'd'代表什么,但它意味着32位寄存器而不是16位。)
但最有效的方法在Kragen的答案中有说明。 (假设您不需要保留标志。如果这样做,请添加FD指令。)
另外,请注意在C函数中分配大量堆栈变量,因为它可能会超出asm子例程的堆栈空间。
我认为这样做,谢谢你帮助大家!
答案 0 :(得分:3)
如果调用约定是cdecl并且C_funct的签名只是:
void C_func(void);
然后这是完全“安全”,但是寄存器EAX,ECX和EDX“可以在函数内部使用”,因此可能会被覆盖。
为防止这种情况发生,您可以保存您关注的寄存器,然后将其恢复:
push eax
push ecx
push edx
call _C_func
pop edx
pop ecx
pop eax
按照惯例,被叫方不应修改寄存器EBX,ESI,EDI和EBP。
我相信被调用者可以修改标志 - 如果你关心保留标志的值那么你应该保存它们。
请参阅x86 calling conventions上的维基百科页面。
答案 1 :(得分:1)
您需要了解调用约定。有关调用约定的讨论,请参阅此article。
如果你对寄存器很偏执,你可以在调用函数之前将所有寄存器都压入堆栈(除了那些带有返回信息的寄存器外,函数不应该在没有清理的情况下返回)。
答案 2 :(得分:0)
这取决于您使用的是x86还是x86_64平台...每个调用约定,以及调用者保存和被调用者保存寄存器(甚至是可用的寄存器集)都有所不同。
话虽这么说,在调用C函数之前,需要将调用者保存寄存器压入堆栈。如果您正在调用C函数,则无需担心Callee-save寄存器,但如果从C函数调用汇编例程,则需要注意。
对于x86 32位平台,调用者保存寄存器为:
EAX
EDX
ECX
还要记住,堆栈指针寄存器ESP
和EBP
也将使用cdecl调用约定进行更改,但是C函数将在调用后恢复这些值,假设您有没有完成C函数不希望你对这些寄存器做的事情。
被调用者保存寄存器是:
EBX
ESI
EDI
最后,C函数的返回值在EAX
寄存器中,并且在调用函数之前将C函数参数压入堆栈。完成的顺序将取决于调用约定,但对于cdecl,它将是从右到左的顺序,这意味着在调用函数之前,C函数的最左边的参数被最后推送到堆栈上
如果您决定在调用C函数之前简单地将所有通用寄存器保存在堆栈中,然后在C函数之后将它们从堆栈中弹回,则可以使用{{1在x86上执行此操作}和PUSHAD
指令。但是这些指令在x86_64上不可用。另请注意,如果您在POPAD
中有返回值,则在致电EAX
之前,您需要做一些事情(比如将其保存在内存中)。