寄存器
-
寄存器
eax;ebx;ecx;edx
特别的 esp;ebp
-
esp,ebp这两个寄存器是用来存放地址,来维护函数开辟的栈帧
-
函数每一次的调用都会在栈区开辟新的空间
调用逻辑
整体调用的逻辑
这里以一个简单程序为例子
#include<stdio.h>
int Add(int x, int y)
{
int z = 0;
z = x + y;
return z;
}
int main()
{
int a = 10;
int b = 20;
int c = 0;
c = Add(a, b);
printf("%dn", c);
return 0;
}
找到调用 堆栈能够清晰的看出整个程序函数调用的逻辑


不难发现整个程序的运行
_tmainCRTStartup()调用了mainCRTStartup()
mainCRTStartup()调用了main()
main()调用Add
main()函数的栈帧
首先在文章的开头我说过esp,ebp这两个寄存器是存放地址的用来维护函数开辟的栈帧的,那调用main函数的之前其实_tmainCRTStartup()和mainCRTStartup()的空间其实应该已经开辟好了,并且esp,ebp也是维mainCRTStartup()创建的空间


push ebp
push为压栈pop为出栈,在栈区数据的创建都是从上往下的,同样的删除数据也是从最顶上的先删除。
每次push的时候我们的esp就会自动跳转到顶上

那事实上是不是这样呢?我们可以通过监视来查看
esp的地址0x0077FEBC
ebp的地址0x0077FF08

当我们进行push操作的时候发现,在栈顶上开辟了新的空间(4字节)并且将esp的地址0x0077FEBC(4字节)存放了进去,同时esp变量现在的0x0077FEB8(栈区是先先使用高地址再使用低地址)所以此时esp的位置应该往上
esp的地址0x0077FEB8
ebp的地址0x0077FF08

move ebp,esp
move ebp,esp意思为将esp赋值给ebp

esp=0x0077FEB8
ebp=0x0077FEB8
这里其实也看出了ebp从原来的0x0077FF08变成了和esp一样的0x0077FEB8

sub esp,0E4H
esp的值减去0e4h
esp=0x0077FDD4
ebp=0x0077FEB8
因为栈区的地址是先用高地址再用低地址,所以地址越小就越靠经顶部。
esp+0e4h=0x0077FEB8
也就是说esp,ebp现在维护着0e4h个空间,这个新开辟出的空间就是为main函数开辟的栈帧


push ebx /esi/ edi
这里三次push操作和最开始的一样
push ebx
esp=0x0077FDD0
ebp=0x0077FEB8
ebx=0x008E2000

esp由原来的0x0077FDD4变成0x0077FDD0,比原先多了4字节的空间且多的空间存放的是ebx的值

同理push esi
esp=0x0077FDCC
ebp=0x0077FEB8
esi=0x0067110E

同理push edi
esp=0x0077FDC8
ebp=0x0077FEB8
edi=0x0067110E


lea edi,[ebp-0E4h]
lea为加载有效地址其实就是ebp-0e4h的这个地址存放到寄存器edi中
特别的ebp-0e4h的地址其实就main函数栈顶地址

就是说现在的edi=0x0077FDD4 指向的其实是图中绿色esp的位置
mov ecx,39h

mov eax,0CCCCCCCCh

rep stos dword ptr es:[edi]
从edi里地址的位置开始向下的ecx(39h)次这么多双子的数据都赋值eax的值



main函数中变量的初始化
mov dword ptr [ebp-8],0Ah
将0Ah就是10放到ebp-8的位置


mov dword ptr [ebp-14h],14h


mov dword ptr [ebp-20h],0


调用add函数栈帧变化
mov eax,dword ptr [ebp-14h]
将ebp-14h中的值赋值给eax
也就是说将b的值20赋值给了eax

push eax
同样进行压栈操作在栈顶将eax的值放入并且esp维护栈顶


mov ecx,dword ptr [ebp-8]

同样的道理这两部就是创建空间将a的值创建进去

call
call指令其实是将一下条指令的地址进行压栈


进入Add函数

首先还是push ebp这里不在解释直接截图了


mov ebp,esp

sub esp,0CCh


三次压栈不过多赘述

lea edi,[ebp-0CCh]
ebp-0CCh其实是粉红色esp的位置

将edi里的地址向下ecx次(33h)的双字赋值0CCCCCCCCh



mov dword ptr [ebp-8],0


mov eax,dword ptr [ebp+8]
add eax,dword ptr [ebp+0Ch]
ebp+8其实就是变量a传的参数
ebp+12其实就是变量b传的参数
将a的值放入eax ,再将b的值加到eax也就是10+20=30


mov dword ptr [ebp-8],eax
eax存放的是30,将30放到ebp-8的位置就是z的位置


这里当出了函数,变量就应该销毁所有要把返回值再次放入寄存器中

pop edi

发现esp的值变大也就是说栈区在变小 edi这块空间消失了

pop esi和pop ebx同理


mov esp,ebp


这里其实就看出为add函数开辟的栈帧正在销毁
pop ebx



到这个时候esp ebp由回到了main函数了
call汇编指令结束

call汇编指令结束,此时esp指向得空间存放的是call下面一条指令的地址



add esp,8



mov dword ptr [ebp-20h],eax



剩下的栈帧的销毁其实都是一样的就不多赘述了
最后
以上就是傻傻皮卡丘最近收集整理的关于又菜又爱玩的函数栈帧的创建与销毁寄存器调用逻辑调用add函数栈帧变化的全部内容,更多相关又菜又爱玩内容请搜索靠谱客的其他文章。
发表评论 取消回复