windows编程--x64调用约定

摘要:
windows32位程序包括stdcall,thiscall,fastcall,cdecl,clrcall,vectorcall,nakedcall等调用方式,x64位程序默认使用新的fastcall调用方式。结构体和union如果大小是1,2,4,8字节,用整数的传参方式;否则通过指针传递。调用函数为前四个参数在调用栈上保留相应的空间,称作shadowspace或spillslot。即使被调用方没有或小于4个参数,调用函数仍然保留那么多的栈空间,这有助于在某些特殊情况下简化调用约定。由调用函数负责清理调用栈。RBX,RBP,RDI,RSI,R12,R14,R14,andR15寄存器则必须在使用时进行保护。

windows32位程序包括stdcall,thiscall,fastcall,cdecl,clrcall,vectorcall,nakedcall等调用方式,x64位程序默认使用新的fastcall调用方式。

这种调用方式得益于x64平台寄存器数量的增加。 

x64 fastcall调用约定

空间大于8字节的参数用参照传递,不能把一个参数分割到多个寄存器中进行传递;前四个整型或指针类型参数从左到右由RCX,RDX,R8,R9依次传递,前四个浮点类型参数从左到右由XMM0,XMM1,XMM2,XMM3依次传递。第五个和其后的参数通过栈传递,参数从右到左一次入栈。

__m128类型, 数组和字符串不能通过值传递,只能通过指针。

结构体和union如果大小是1,2,4,8字节,用整数的传参方式;否则通过指针传递。

小于等于64位的整型或指针类型返回值由RAX传递。
浮点返回值由XMM0传递。

更大的返回值(比如结构体),由调用方在栈上分配空间,并由RCX持有该空间的指针并传递给被调用函数,因
此整型参数使用的寄存器依次右移一格,实际只可以利用3个寄存器,其余参数入栈。函数调用结束后,RAX返
回该空间的指针(就是rcx的值)。

Varargs(可变参数)也通过上述方式进行传参。

调用函数为前四个参数在调用栈上保留相应的空间,称作shadowspace或spillslot。即使被调用方没有或小
于4个参数,调用函数仍然保留那么多的栈空间,这有助于在某些特殊情况下简化调用约定。
由调用函数负责清理调用栈。
除RCX,RDX,R8,R9以外,RAX、R10、R11、XMM4和XMM5也是易变化的(volatile)寄存器。


RBX,RBP,RDI,RSI,R12,R14,R14,andR15寄存器则必须在使用时进行保护。
在寄存器中,所有参数都是右对齐的。小于64位的参数并不进行高位零扩展,也就是高位是无法预测的垃圾数据.

参数传递示例

1 int func1(int a, int b, int c, int d, int e, doublef) {
2     return 9;
3 }
4 
5 int func2(int a, int b, int c, int d, int e, double f,intg) {
6     return 90;
7 }
8 
9 intmain() {
10     int r = func1(1, 2, 3, 4, 5, 6.6);
11     int r2 = func2(1, 2, 3, 4, 5, 6.6,7);
12     return 0;
13 }

反汇编:

1 int r2 = func2(1, 2, 3, 4, 5, 6.6,7);
2 00007FF65F263D0E  mov         dword ptr [rsp+30h],7  
3 00007FF65F263D16  movsd       xmm0,mmword ptr [__real@401a666666666666 (07FF65F269BB0h)]  
4 00007FF65F263D1E  movsd       mmword ptr [rsp+28h],xmm0  
5 00007FF65F263D24  mov         dword ptr [rsp+20h],5  
6 00007FF65F263D2C  mov         r9d,4  
7 00007FF65F263D32  mov         r8d,3  
8 00007FF65F263D38  mov         edx,2  
9 00007FF65F263D3D  mov         ecx,1  
10 00007FF65F263D42  call        func2 (07FF65F261375h)  
11 00007FF65F263D47  mov         dword ptr [r2],eax  
12     return 0;
13 00007FF65F263D4A  xor         eax,eax  

返回结构体示例:

1 S1 func3(int a, int b, int c, intd) {
2     S1 s{6,7,8,9,11,12};
3     returns;
4 }
5 intmain() {
6     int r = func1(1, 2, 3, 4, 5, 6.6);
7     int r2 = func2(1, 2, 3, 4, 5, 6.6,7);
8     S1 r3 = func3(1, 2, 3, 4);
9     return 0;
10 }

反汇编

1 S1 func3(int a, int b, int c, intd) {
2 00007FF68A271750  mov         dword ptr [rsp+20h],r9d  
3 00007FF68A271755  mov         dword ptr [rsp+18h],r8d  
4 00007FF68A27175A  mov         dword ptr [rsp+10h],edx  
5 00007FF68A27175E  mov         qword ptr [rsp+8],rcx   //这里rcx存放着调用函数提前分配好的用于存放返回的结构体的指针,由于rcx用来存放返回值地址,第一个参数改由edx传递,后面的参数一次后延。
6 00007FF68A271763  push        rbp  
7 00007FF68A271764  push        rsi  
8 00007FF68A271765  push        rdi  
9 00007FF68A271766  sub         rsp,110h  
10 00007FF68A27176D  lea         rbp,[rsp+20h]  
11 00007FF68A271772  mov         rdi,rsp  
12 00007FF68A271775  mov         ecx,44h  
13 00007FF68A27177A  mov         eax,0CCCCCCCCh  
14 00007FF68A27177F  rep stos    dword ptr [rdi]  
15 00007FF68A271781  mov         rcx,qword ptr [rsp+138h]  
16 00007FF68A271789  lea         rcx,[__AD28E9E4_源@cpp (07FF68A280000h)]  
17 00007FF68A271790  call        __CheckForDebuggerJustMyCode (07FF68A271082h)  
18     S1 s{6,7,8,9,11,12};
19 00007FF68A271795  mov         dword ptr [s],6  
20 00007FF68A27179C  mov         dword ptr [rbp+0Ch],7  
21 00007FF68A2717A3  mov         dword ptr [rbp+10h],8  
22 00007FF68A2717AA  mov         dword ptr [rbp+14h],9  
23 00007FF68A2717B1  mov         dword ptr [rbp+18h],0Bh  
24 00007FF68A2717B8  mov         dword ptr [rbp+1Ch],0Ch  
25     returns;
26 00007FF68A2717BF  lea         rax,[s]  
27 00007FF68A2717C3  mov         rdi,qword ptr [rbp+110h]  //rbp+110h里存放的是从rcx里传进来的调用函数分配的用户存放返回值的指针
28 00007FF68A2717CA  mov         rsi,rax  
29 00007FF68A2717CD  mov         ecx,18h  
30 00007FF68A2717D2  rep movs    byte ptr [rdi],byteptr [rsi]  //上面几行是将s结构体的数据复制到调用函数分配好的空间里(rcx传进来的)
31 00007FF68A2717D4  mov         rax,qword ptr [rbp+110h]  //将返回值指针赋值给rax
32 }

免责声明:文章转载自《windows编程--x64调用约定》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇防止sql注入的方法亿级用户的新浪微博平台架构下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Delphi指针的定义和取值

效果图如下: 要点: 1.指针的2中定义方法 PInteger 和 ^Integer 2.取地址符号 @ 和 Addr函数 3.取内容符号 ^ ,比如MyPointInt1^则是取MyPointInt1指针所指向的内容了。 program MyPoint; //指针详解 {$APPTYPE CONSOLE} usesSysUtils,windows,G...

cjson库的使用以及源码阅读

cjson是一个用c语言开发的json解析库,免费开源只有一个c文件和一个h文件。json和xml功能相似,可以用来传输数据,存储数据以及表达程序当前的状态。 1、下载cjson的源码         https://github.com/DaveGamble/cJSON 2、阅读readme文件可以大概的了解一下cjson的介绍以及使用方法,我尝试着把...

数组和指针——都是“退化”惹的祸

1. 什么是数组类型?下面是C99中原话:An array type describes a contiguously allocated nonempty set of objects with aparticular member object type, called the element type.36) Array types are char...

内存管理[3]堆

VirtualAlloc 分配的内存是以 4K 为最小单位、连续的内存地址(但映射到真实的内存时它不一定是连续的), 前面说了, 它不适合分配小内存(譬如只有几个字节的变量); 局部的变量在 "栈" 中有程序自动管理, 那么那些全局的小变量怎么办呢? 这就要用到 "堆".这样看来, VirtualAlloc 分配的内存既不是 "栈" 也不是 "堆"; Vi...

文件读写和文件指针的移动

read 函数 -#include <unistd.h> -ssize_t read(int fd, void *buf, size_t count); 从fd 所指的文件中读取count 个字节到buf 中。返回实际读取到的字节数,有错误发生则返回-1。读取文件时,文件读写指针会会随着读取到的字节数移动。 write 函数 -...

PE注入

1 // PE注入.cpp : 定义控制台应用程序的入口点。 2 // 3 4 #include "stdafx.h" 5 6 #include <windows.h> 7 8 #include <tlhelp32.h> 9 10 #include <process.h>...