编写Microsoft Fastcall 64位汇编函数



我在通过shellcode编写64位快速调用函数时遇到了一些问题,问题是它似乎与其他函数在它之前和之后调用混淆,尽管工作,经过几次我传递的参数最终由于某种原因成为垃圾,而不是我传递给它的东西,这使我认为我可能误解了这种调用惯例的工作方式。

例如,我在调用printf之前调用打印我使用的2个参数,下面是第一次运行后对它们发生的情况:
printf("RAX: %pnR10: %pnn",mem2,mem);
function((U64)mem2,(U64)mem,1);

输出:

RAX: 0000000000597D00
R10: 0000000000597B80
RAX: 0000000000190000
R10: 0000000000597D00

* RAX的第二个值实际上是分配shellcode的地址,我不知道它是如何结束的。

注:我用stdcall调用约定在32位中编写了相同的函数,它工作得很好,所以它的主要部分,除了使用的寄存器外保持不变,工作正常。

所以文档指定了前3个参数将分别放入RCX, RDX和R8中,并且RAX, R10, R11等将在函数中使用,因此这就是我存储要使用的参数的地方(它们在函数的持续时间内被修改)。

下面是函数原型:
typedef void(__fastcall*f_proto)(unsigned long long a, unsigned long long b, unsigned long long c);

是这样的:

PUSH RBP
MOV RBP, RSP
MOV RAX, RCX
MOV R10, RDX
MOV R11, R8
...
MOV RSP, RBP
POP RBP
RETN 0x18

64位windows只使用一种调用约定,称为'Microsoft x64调用约定'(如果包括__vectorcall,则为两种)。维基百科分别讨论了32位调用约定,然后讨论了单个64位微软调用约定。我在这里有一个调用约定的图表。调用约定的MDSN页面正在讨论x86调用约定,然后如果您正在寻找x64调用约定,它会引导您到一个链接,并且您需要注意调用后的主空间和rsp对齐。

维基百科页面:

Microsoft__fastcall约定(又名__msfastcall)传递适合ECX和EDX的前两个参数(从左到右计算)。剩余的参数从右向左压入堆栈。当编译器为IA64或AMD64编译时,它忽略__fastcall关键字,而使用一个64位调用约定。

__stdcallMSDN页面上:

在ARM和x64处理器上,__stdcall被编译器接受并忽略

__cdeclMSDN页面上:

在ARM和x64处理器上,__cdecl被接受,但通常被编译器忽略。根据ARM和x64的惯例,参数在可能的情况下在寄存器中传递,随后的参数在堆栈上传递。在x64代码中,使用__cdecl来覆盖/Gv编译器选项,并使用默认的x64调用约定。

只有__vectorcall在x64上做一些不同的事情

最新更新