- UID
- 75402
注册时间2014-5-2
阅读权限30
最后登录1970-1-1
龙战于野
TA的每日心情 | 开心 2015-8-2 16:07 |
---|
签到天数: 2 天 [LV.1]初来乍到
|
本帖最后由 F8LEFT 于 2014-12-23 12:21 编辑
可能这个标题没有多大的新意,实际内容也没有多少。不过我还是把我研究到的东西说一下好了,也可以当做是扫盲,就看看也是不错的。 一些基础的东西小Q老师已经说过了,请参考帖子:https://www.chinapyg.com/thread-74565-1-1.html。
我再补充一下其他的东西。
测试软件 VS2012
在x64上面,函数调用更加类似于 FastCall的调用模式,它首先会使用寄存器 rcx,rdx,r8,r9(整形参数) 或者 xmm0,xmm1,xmm2,xmm3 (浮点型参数)来传递参数,最后才是使用堆栈来进行传递。
那么问题来了,如果函数的头4个参数是 包含 整形 与 浮点型 的,比如 double Test(LONGLONG i1, LONGLONG i2, double a3, double a4),那么是否使用的是 rcx,rdx,xmm0,xmm1这4个寄存器呢?在VC2012上进行相关的测试:
double Test(LONGLONG i1, LONGLONG i2, double a3, double a4)
{
return i1 + i2 + a3 + a4;
}
调用Test的代码:
- 000000013F2B104D | F2 0F 10 1D 9B 58 00 00 | movsd xmm3,qword ptr ds:[<__real@4011851eb851eb85>] | ;a4 = 4.4
- 000000013F2B1055 | F2 0F 10 15 83 58 00 00 | movsd xmm2,qword ptr ds:[<__real@40091eb851eb851f>] | ;a3 = 3.3
- 000000013F2B105D | BA 02 00 00 00 | mov edx,2 | ;i2 = 2
- 000000013F2B1062 | B9 01 00 00 00 | mov ecx,1 | ;i1 = 1
- 000000013F2B1067 | E8 A3 FF FF FF | call <x64text.@ILT+10(?Test@@YAN_J0NN@Z)> | ;call Test
复制代码 call Test中对参数的处理:
- 000000013F2B1130 | F2 0F 11 5C 24 20 | movsd qword ptr ss:[rsp+20],xmm3 | ;a4 入栈
- 000000013F2B1136 | F2 0F 11 54 24 18 | movsd qword ptr ss:[rsp+18],xmm2 | ;a3 入栈
- 000000013F2B113C | 48 89 54 24 10 | mov qword ptr ss:[rsp+10],rdx | ;i2 入栈
- 000000013F2B1141 | 48 89 4C 24 08 | mov qword ptr ss:[rsp+8],rcx | ;i1 入栈
复制代码 可以看到,实际使用的寄存器为 rcx,rdx,xmm2,xmm3,后面的参数都是使用堆栈。其他情况都是类似的。所以,实际传参情况是这样的。
第1个参数使用 rcx(整形参数) 或 xmm0 (浮点型参数)
第2个参数使用 rdx(整形参数) 或 xmm1 (浮点型参数)
第3个参数使用 r8 (整形参数) 或 xmm2 (浮点型参数)
第4个参数使用 r9 (整形参数) 或 xmm3 (浮点型参数)
第5个参数使用堆栈
第6个参数使用堆栈
第7个........
上面我说过了,X64的调用非常的像fastcall方式的。那么对于堆栈平衡问题是如何处理的呢?我们知道 cdcel是在call外通过 add rsp, 0x??,来平衡的, WINAPI 是在call内通过 ret 0x?? 来平衡的。好的,那么我们再测试一下。double WINAPI Test(LONGLONG i1, LONGLONG i2, double a3, double a4, double a5)
{
return i1 + i2 + a3 + a4;
}
汇编代码:
- 000000013F8D1045 | 48 83 EC 40 | sub rsp,40 |;新建堆栈空间
- ...........
- 000000013F8D105D | F2 0F 10 05 9B 58 00 00 | movsd xmm0,qword ptr ds:[<__real@4016000000000000>] | ;a5 = 5.5
- 000000013F8D1065 | F2 0F 11 44 24 20 | movsd qword ptr ss:[rsp+20],xmm0 | ;a5 入栈
- 000000013F8D106B | F2 0F 10 1D 7D 58 00 00 | movsd xmm3,qword ptr ds:[<__real@401199999999999a>] | ;a4 = 4.4
- 000000013F8D1073 | F2 0F 10 15 65 58 00 00 | movsd xmm2,qword ptr ds:[<__real@400a666666666666>] | ;a3 = 3.3
- 000000013F8D107B | BA 02 00 00 00 | mov edx,2 | ;i2 = 2
- 000000013F8D1080 | B9 01 00 00 00 | mov ecx,1 | ;i1 = 1
- 000000013F8D1085 | E8 7B FF FF FF | call <x64text.@ILT+0(?Test@@YAN_J0NNN@Z)> | ;call Test
- 000000013F8D108A | F2 0F 2C C0 | cvttsd2si eax,xmm0 | ;eax:BaseThreadInitThunk
- .........
- 000000013F8D10AD | 48 83 C4 40 | add rsp,40 | ;释放堆栈空间
复制代码 程序不再是每经过一个call,堆栈就变化一次了。而是在每一个call开头,先计算出所需要的堆栈总空间,然后一次新建全部的空间,到最后才全部释放。在调用到其他call的时候就不会特别的再次新建堆栈空间来存放参数,而是直接利用前面新建的空间来进行,入栈操作也从 Push XXXXXXXX改为了 movsd ss:[rsp + ptr], XMM?,或者 mov ss:[rsp + ptr], r?x。因为堆栈不会再变了,所以你不需要非常郁闷的去看到rsp的变化了。
那么call里面又是如何调用参数的呢:
未处理参数时堆栈数据
- 000000000025F6A8 : 000000013F43108A return to XXXXXXXXXXXXXXXX
- 000000000025F6B0 : CCCCCCCCCCCCCCCC
- 000000000025F6B8 : CCCCCCCCCCCCCCCC
- 000000000025F6C0 : CCCCCCCCCCCCCCCC
- 000000000025F6C8 : CCCCCCCCCCCCCCCC
- 000000000025F6D0 : 4016000000000000
复制代码 处理的相关代码:
- 000000013F431150 | F2 0F 11 5C 24 20 | movsd qword ptr ss:[rsp+20],xmm3 | ;a4 入栈
- 000000013F431156 | F2 0F 11 54 24 18 | movsd qword ptr ss:[rsp+18],xmm2 | ;a3 入栈
- 000000013F43115C | 48 89 54 24 10 | mov qword ptr ss:[rsp+10],rdx | ;i2 入栈
- 000000013F431161 | 48 89 4C 24 08 | mov qword ptr ss:[rsp+8],rcx | ;i1 入栈
复制代码 处理后堆栈的数据:
- 000000000025F6A8 : 000000013F43108A return to XXXXXXXXXXXXXXXX
- 000000000025F6B0 : 0000000000000001
- 000000000025F6B8 : 0000000000000002
- 000000000025F6C0 : 400A666666666666
- 000000000025F6C8 : 401199999999999A
- 000000000025F6D0 : 4016000000000000
复制代码 rsp根本没有改变,只是将 rcx,rdx,xmm2,xmm3的值入堆栈而已!!!而且是通过mov的方式。而a5因为call前已经处理了所以call中就没有再弄了。
call里面通过mov 把参数放到 寄存器 ,call外面通过mov 把参数放到堆栈。实际上与x86也是非常相似的,不过是少了堆栈的大小变化而已。
所以,我想你应该可以很容易的知道call返回的方式了。
这虽然是WINAPI,但也不再是 ret 0x??的形式了哈哈。因为入栈的时候没有了堆栈的变化了,退出时自然也不需要手动平衡了。
就只那么多了,这几天以来的成果,如果有错误的话欢迎大家来指出O(∩_∩)O哈!
|
评分
-
查看全部评分
|