码迷,mamicode.com
首页 > Windows程序 > 详细

WIndows 64 栈帧结构

时间:2016-08-07 15:27:12      阅读:948      评论:0      收藏:0      [点我收藏+]

标签:

小标题

1.x64下反汇编

2.调试64位4参数中寄存器的值

3.调试64位中5参数及多次调用函数栈时栈区结构

4.64位中少于4参数情况 

 

1.x64下反汇编

   64位下函数的调用约定全部用FASTCALL,就是前4个参数依次用rcx,rdx,r8,r9传递,多余的参数从右至左压参。

x86下栈帧是比较清楚的,但是x64位下栈帧的资料好少。然后调了下熟悉栈帧的结构。

int Add(int a,int b,int c,int d);

int _tmain(int argc, _TCHAR* argv[])
{
	int a = 0;
	Add(1,2,3,4);
	return 0;
}

int Add(int a,int b,int c,int d)
{
	int xx = a+b+c+d;
	int yy = a+b-c-d;
	int zz = -a-b+c+d;
	return xx;
}

  使用Vs2010 ,64位下调试,打开寄存器窗口,Alt+8 反汇编

      可以看到首先将1,2,3,4放在寄存器中,然后调用call指令,call指令可以分解为将下一条指令压参,然后jmp到函数地址

      在执行push指令的时候,RSP-8

000000013F931049  mov         r9d,4  
000000013F93104F  mov         r8d,3  
000000013F931055  mov         edx,2  
000000013F93105A  mov         ecx,1  
000000013F93105F  call        Add (13F931005h)   //指令为 push rip    ;RSP-8   
                                                 //       jmp Add

  Add函数的反汇编代码如下:

     可以看到前4句将寄存器中传递的参数赋值给rsp+8h,rsp+10h,rsp+18h,rsp+20h

     如果多余4个参数,其余的依次放入rsp+28h 之后

     然后保存前栈帧栈底,开辟栈区保存局部变量,由于是三个变量12字节,对齐内存是16字节,sub rsp,10h

     初始化栈区,rep stos指令

int Add(int a,int b,int c,int d)
{
000000013F251080  mov         dword ptr [rsp+20h],r9d  
000000013F251085  mov         dword ptr [rsp+18h],r8d  
000000013F25108A  mov         dword ptr [rsp+10h],edx  
000000013F25108E  mov         dword ptr [rsp+8],ecx  
000000013F251092  push        rdi                //保存前栈底   RSP-8
000000013F251093  sub         rsp,10h            //开辟栈区 16字节 RSP-10h
000000013F251097  mov         rdi,rsp            //新栈帧栈底rdi=rsp
000000013F25109A  mov         ecx,4              //循环次数
000000013F25109F  mov         eax,0CCCCCCCCh  
000000013F2510A4  rep stos    dword ptr [rdi]    //将rdi开始赋值eax中的值,循环4次
000000013F2510A6  mov         ecx,dword ptr [rsp+20h]  //此处是第一个参数a  
	int xx = a+b+c+d;
000000013F2510AA  mov         eax,dword ptr [b]  
000000013F2510AE  mov         ecx,dword ptr [a]  
000000013F2510B2  add         ecx,eax  
000000013F2510B4  mov         eax,ecx  
000000013F2510B6  add         eax,dword ptr [c]  
000000013F2510BA  add         eax,dword ptr [d]  
000000013F2510BE  mov         dword ptr [rsp],eax  //rsp 保存 xx
	int yy = a+b-c-d;
000000013F2510C1  mov         eax,dword ptr [b]  
000000013F2510C5  mov         ecx,dword ptr [a]  
000000013F2510C9  add         ecx,eax  
000000013F2510CB  mov         eax,ecx  
000000013F2510CD  sub         eax,dword ptr [c]  
000000013F2510D1  sub         eax,dword ptr [d]  
000000013F2510D5  mov         dword ptr [yy],eax   //rsp+4 保存yy
	int zz = -a-b+c+d;
000000013F2510D9  mov         eax,dword ptr [a]  
000000013F2510DD  neg         eax  
000000013F2510DF  sub         eax,dword ptr [b]  
000000013F2510E3  add         eax,dword ptr [c]  
000000013F2510E7  add         eax,dword ptr [d]  
000000013F2510EB  mov         dword ptr [zz],eax  //rsp+8 保存
	return xx;
000000013F2510EF  mov         eax,dword ptr [rsp]  //将返回值保存在eax寄存器中
}
000000013F2510F2  add         rsp,10h    //恢复开辟的栈区
000000013F2510F6  pop         rdi        //恢复前栈帧的栈底
000000013F2510F7  ret                    //pop rip  将之前保存的call下一条指令弹出给rip , 继续执行 
                                         //RSP - 8  等于调用call之前的值 

 

2.调试64位4参数中寄存器的值

    下面是调试中的一些寄存器的值

 

技术分享

此时的RSP和RDI都是_tmain函数的栈顶和栈底,alt+8反汇编

 

技术分享

 

 技术分享

此时RSP经过 call 中的push rip 减去8,push edi 减去8,sub rsp,10h 一共减去20h

rdi赋值为rsp,为当前Add的栈底

 

技术分享

 

 rdi经过rep stos 指令将eax中值初始化到rdi中,共4*4字节,rdi初始化之后加10h

此时我们看内存中的情况如上图

栈帧情况如下

技术分享

 

 

 

 

 3.调试64位中5参数及多次调用函数栈时栈区结构

      我们在试试5参数的函数调用情况,同时我们知道函数会把4个寄存器中的值赋值到栈上面的区域,要开辟4*8=20h的区域,在调试的时候没有发现对于rsp的操作

于是猜测是在上一个函数中已经开辟好了额外的空间存储参数的数据。

int _tmain(int argc, _TCHAR* argv[])
{
	int a = 0;
	Sub(1,2,3,4,5);
	return 0;
}

int Add(int a,int b,int c,int d)
{
	int xx = a+b+c+d;
	int yy = a+b-c-d;
	int zz = -a-b+c+d;
	return xx;
}
	 
int Sub(int a,int b,int c,int d,int e)
{
	int xx = a+b+e+d;
	int yy = a+b-c-d;
	int zz = -a-b+c+d;
	Add(b,c,d,e);
	return xx;
}

  

我们使用Sub()5个参数的函数,同时在Sub函数中调用Add函数

	Sub(1,2,3,4,5);
000000013F4F2EF9  mov         dword ptr [rsp+20h],5   //当前rsp + 20 就是存储4个参数之后的位置  
000000013F4F2F01  mov         r9d,4  
000000013F4F2F07  mov         r8d,3  
000000013F4F2F0D  mov         edx,2  
000000013F4F2F12  mov         ecx,1  
000000013F4F2F17  call        Sub (13F4F100Fh)  


int Sub(int a,int b,int c,int d,int e)
{
000000013FC32FB0  mov         dword ptr [rsp+20h],r9d  //当前的rsp 比之前操作的 -8 ,所以这个位置是 第五个参数的前面一个
000000013FC32FB5  mov         dword ptr [rsp+18h],r8d  
000000013FC32FBA  mov         dword ptr [rsp+10h],edx  
000000013FC32FBE  mov         dword ptr [rsp+8],ecx  
000000013FC32FC2  push        rdi  
000000013FC32FC3  sub         rsp,30h     //这个地方是调用Add函数  额外分配的20h    存储4个参数
000000013FC32FC7  mov         rdi,rsp  
000000013FC32FCA  mov         ecx,0Ch  
000000013FC32FCF  mov         eax,0CCCCCCCCh  
000000013FC32FD4  rep stos    dword ptr [rdi]  
000000013FC32FD6  mov         ecx,dword ptr [rsp+40h]  

  

 

我在函数中调用了上面的Add()函数,结果rsp - 30 开辟栈空间,这是为了调用Add的时候 多开的4*8 保存4个寄存器中的值在栈中。

其中局部变量保存在 rsp + 20h 的地方,依次保存xx,yy,zz和没用到的4字节

技术分享

 

在Add函数的栈区,我们果然看到了Sub开辟栈区的20h地址空间,存储了传递给寄存器的参数

技术分享

 

4.64位中少于4参数情况 

  还有就是传递少于4个参数的情况,也会开辟  4*8的区域   

  没有传递参数也会开辟   4*8区域。

 

WIndows 64 栈帧结构

标签:

原文地址:http://www.cnblogs.com/aliflycoris/p/5746143.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!