当前位置:   article > 正文

【C语言】二十二步了解函数栈帧(压栈、传参、返回、弹栈)

压栈
阅读本文需要掌握的知识
熟练使用——c语言函数

进入正题前

首先我们为什么要学习函数的栈帧?

简单回答就是:增加内功

学习函数调用的底层代码,了解函数如何传参、如何返回
这样对于函数的使用有很大的帮助 对于日后出现的问题提会有所了解
列如:
函数在传参时,将在函数内部创建变量a,
然后将a的地址传了回去,
当函数test调用完 后会被销毁
这时传回去的a的地址被p接收会就造成野指针,使用野指针会造成非法访问等问题
如不了解函数栈帧 ,理解起来可能有一些麻烦 展示代码如下

int* test()
{
	int a = 10;
	int *pa = &a;
	return pa;
}
int main()
{
	int* p = test();//此时p指向了一个不存在 或者不属于当前函数的地址 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

当此时用p指针做一些其他操作,会出现问题,所以了解函数栈帧很重要

前期准备:


函数栈帧大致分为以下几步:简单了解不至于被劝退。
1.main函数在栈区创建,并且传参
2.被调用的函数在栈区创建,计算值,并带回返回值
3.以上操作都是在栈区中完成,所以有了压栈、弹栈


我们将围绕以下代码展开函数栈帧的讲解
此代码通过Add函数实现a+b  将返回值带给c的过程
#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("%d\n", c);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
函数栈帧的创建和销毁所解决的问题

最后我会回答这些问题,记不住没关系
局部变量是怎么创建的?
为什么局部变量的值是随机值?
函数是怎么传参的?
传参的顺序是怎样的?
形参和实参是什么关系?
函数调用是怎么做的?
函数调用结束后是怎么返回的?

此次过程所需软件

我所用到的软件是 Visual Studio 2019
使用VS2013也可以,可能略有差异

软件准备
将代码写在VS2019中,按F10开始调试 这里是引用
并点开反汇编在这里插入图片描述

或者F10 后,空白处右键鼠标转到反汇编在这里插入图片描述

打开内存 和监视这里是引用
像这样排列以便观察

并且内存监视 列改为4 因为我们写的代码是int类型 int是4字节这里是引用

准备完成 ,解读反汇编代码,你还要知道这些!

在反汇编内 ,你看到如下代码
挺长 但是不用害怕
下面代码暂时你看看就行,看不懂没关系,下面我一句一句解释 你很快就会明白

int main()
{
002918B0  push        ebp  
002918B1  mov         ebp,esp  
002918B3  sub         esp,0E4h  
002918B9  push        ebx  
002918BA  push        esi  
002918BB  push        edi  
002918BC  lea         edi,[ebp-24h]  
002918BF  mov         ecx,9  
002918C4  mov         eax,0CCCCCCCCh  
002918C9  rep stos    dword ptr es:[edi]  
002918CB  mov         ecx,29C003h  
002918D0  call        0029131B  
	int a = 10;
002918D5  mov         dword ptr [ebp-8],0Ah  
	int b = 20;
002918DC  mov         dword ptr [ebp-14h],14h  
	int c = 0;
002918E3  mov         dword ptr [ebp-20h],0  
	c = Add(a, b);
002918EA  mov         eax,dword ptr [ebp-14h]  
002918ED  push        eax  
002918EE  mov         ecx,dword ptr [ebp-8]  
002918F1  push        ecx  
002918F2  call        002910B4  
002918F7  add         esp,8  
002918FA  mov         dword ptr [ebp-20h],eax  
	printf("%d\n", c);
002918FD  mov         eax,dword ptr [ebp-20h]  
00291900  push        eax  
00291901  push        297B30h  
00291906  call        002910D2  
0029190B  add         esp,8  
	return 0;
0029190E  xor         eax,eax  
}
00291910  pop         edi  
00291911  pop         esi  
00291912  pop         ebx  
00291913  add         esp,0E4h  
00291919  cmp         ebp,esp  
0029191B  call        00291244  
00291920  mov         esp,ebp  
00291922  pop         ebp  
00291923  ret  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

看完上面的代码 ,如果你还没被劝退,那么继续跟我往下看吧!

先介绍一下 上述代码的一些英文有些寄存器暂时记不住没关系,用到的时候我会加以说明
eax - 寄存器 不会被销毁 集成到cpu上的
ebx
ecx
edx

esp - 栈顶指针 记住
ebp - 栈底指针 记住
这两个寄存器中放的是地址
这两个地址是用来维护函数栈帧的

这里了解eax esp ebp 就可以啦
记不住也没关系,后面都会讲解

首先要了解 main()函数执行的时候也是被一个叫__tmainCRTStartup函数调用的

所以先给__tmainCRTStartup在栈区开辟空间
这是编译器底层的调用 暂时不做探讨 知道就可以

查看方法如下 在监视中添加esp、ebp查看地址请添加图片描述
esp、ebp是两个维护函数空间的指针
上面为低地址 ,下面为高地址
    栈顶指针 esp 0x008ff99c
    栈底指针 ebp 0x008ff9b8
    
    首先分配一块空间给__tmainCRTStartup
    
    栈区:
    |       |           低地址
    |       |
    |       |
    |       |
    |       |\ esp 0x008ff99c 	栈顶
    |       | \  
    |       |   __tmainCRTStartup
    |       | /
    |       |/ ebp 0x008ff9b8	栈底
    |       |           高地址
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

从以上代码可以看出 栈区 先使用高地址,再使用低地址
栈顶指针 esp 0x008ff99c(十六进制) —— 9,435,548 (十进制)
栈底指针 ebp 0x008ff9b8(十六进制) —— 9,435,576 (十进制)
8ff99c - 8ff9b8 计算得出 栈区为__tmainCRTStartup 开辟了28字节的空间


解读反汇编语句开始


一、为main函数开辟空间

1.002918B0 push ebp
按下F11进行逐语句操作,后面所有操作都是F11完成

push意为压栈 此行代码意为 将ebp压栈
黄色箭头指向第二条语句 说明第一条语句执行完了
此时的esp ebp 是维护__tmainCRTStartup函数的

代码展示

请添加图片描述

图解展示

    push 压栈 ebp 
        把ebp的地址压栈到栈顶 esp自动指向最上面的地址
        又因为栈先使用高地址 后使用低地址 
        所以esp 的地址减小4 esp = 0x008ff998
        
    |       |           低地址
    |       |
    |       |
    |  ebp  |   esp 0x008ff998
    |       |\  ↑↑↑向上移动 4字节     ~~0x008ff99c~~  
    |       | \  
    |       |   __tmainCRTStartup
    |       | /
    |       |/ ebp 0x008ff9b8
    |       |           高地址    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
2. 002918B1 mov ebp,esp

mov可以理解为赋值,此代码意为:ebp = esp
此时是esp ebp向上移动的过程 要去维护main函数

请添加图片描述

    把esp的值赋给ebp
        此时ebp指向 esp指向的位置
        ebp = 0x008ff998
        
    |       |   
    |       |        
    |       |
    |       |        
    |       |           低地址
    |       |
    |       |
    |  ebp  |   ebp 0x008ff998   esp 0x008ff998
    |       |\  
    |       | \  
    |       |   __tmainCRTStartup
    |       | /
    |       |/ 
    |       |           高地址  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
3. 002918B3 sub esp,0E4h

sub的意思是 减去
esp0E4h ,esp指针就指向了低地址
此操作将esp(栈顶指针) 向上移动,为main函数开辟空间并维护

请添加图片描述

    002918B3  sub         esp,0E4h  
    esp 减去 0E4h   0E4h == 228
        esp = 0x008ff8b4
      
                        低地址
    |       |\   esp 0x008ff8b4
    |       | \       
    |       |  \ 
    |       |   main     
    |       |  /         
    |       | /
    |       |/
    |  ebp  |   ebp 0x008ff998 
    |       |\  
    |       | \  
    |       |   __tmainCRTStartup
    |       | /
    |       |/ 
    |       |           高地址    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

main函数的空间请添加图片描述

二、压栈

002918B9 push ebx
002918BA push esi
002918BB push edi

esp每次压栈后会自动指向最低地址处
EBX 是"基地址"(base)寄存器, 在内存寻址时存放基地址。
ESI/EDI分别叫做"源/目标索引寄存器"
暂时不理解没关系,直接看三

                           低地址
   |  edi      |   esp  0x008ff8a8
   |  esi      |0x008ff8ac
   |  ebx      |0x008ff8b0 
   | 			|\   ↑   0x008ff8b4
   | 			|  \   
   | 			|    \
   | 			|     main
   | 			|    /
   | 			|   /   
   | 			|  /         
   | 			| /
   |           |/
   |  ebp      |   ebp 0x008ff998 
   |           |\  
   |           | \  
   |           |   __tmainCRTStartup
   |           | /
   |           |/ 
   |           |           高地址 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

请添加图片描述请添加图片描述
请添加图片描述

三、初始化main函数

002918BC lea edi,[ebp-24h]
002918BF mov ecx,9
002918C4 mov eax,0CCCCCCCCh
002918C9 rep stos dword ptr es:[edi]

edi存储ebp减24h的位置,24hmain函数空间的大小
ecx存储9
eax存储0CCCCCCCCh
ebpedi存储的位置的ecx(9)个位置都 初始化为eax(0CCCCCCCCh)
讲人话就是 :将main空间的内容初始化为cc cc cc cc

                            低地址
    |  edi      |   esp  0x008ff8a8
    |  esi      |0x008ff8ac
    |  ebx      |0x008ff8b0 
    |cc cc cc cc|\   ↑   0x008ff8b4
    |cc cc cc cc|  \   
    |cc cc cc cc|    \
    |cc cc cc cc|     main
    |cc cc cc cc|    /
    |cc cc cc cc|   /   
    |cc cc cc cc|  /         
    |cc cc cc cc| /
    |cc cc cc cc|/
    |  ebp      |   ebp 0x008ff998 
    |           |\  
    |           | \  
    |           |   __tmainCRTStartup
    |           | /
    |           |/ 
    |           |           高地址 
    
    002918BC  lea         edi,[ebp-24h] 
        [ebp-24h] 加载到 edi
    002918BF  mov         ecx,99放到ecx里
    002918C4  mov         eax,0CCCCCCCCh
        eax赋值为0CCCCCCCCh
    002918C9  rep stos    dword ptr es:[edi]  
        从edi的位置开始向下的eax这么多个 dword(4byte) 全部改成eax的值
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

请添加图片描述

四、在main中创建变量a并存储

int a = 10;
002918D5 mov dword ptr [ebp-8],0Ah

a变量的值存到main开辟的空间中, 位置是([ebp-8])的位置
内存中的 |0a 00 00 00| 中的a十六进制代表10 是a的数值的意思,并不是变量a

                                低地址
    |  edi      |   esp  0x008ff8a8
    |  esi      |   
    |  ebx      |   
    |cc cc cc cc|\ 
    |cc cc cc cc|  \
    |cc cc cc cc|    \
    |cc cc cc cc|     main    
    |cc cc cc cc|    /
    |cc cc cc cc|   /    
    |cc cc cc cc|  /         
    |0a 00 00 00| / ebp-8 0x008FF990
    |cc cc cc cc|/
    |  ebp      |   ebp 0x008ff998 
    |           |\  
    |           | \  
    |           |   __tmainCRTStartup
    |           | /
    |           |/ 
    |           |           高地址    
    
        int a = 10;
    002918D5  mov         dword ptr [ebp-8],0Ah
        ebp - 8 的位置放 一个10进去   0Ah == 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

请添加图片描述

五、在main中创建变量b并存储

int b = 20;
002918DC mov dword ptr [ebp-14h],14h

b变量的值存到main开辟的空间中, 位置是([ebp-14h])的位置
内存中的 |14 00 00 00| 中的14十六进制代表20

                                    低地址
    |  edi      |   esp  0x008ff8a8
    |  esi      |   
    |  ebx      |
    |cc cc cc cc|\
    |cc cc cc cc|  \
    |cc cc cc cc|    \   
    |cc cc cc cc|     main     
    |14 00 00 00|  b /   ebp-14h 0x008FF984
    |cc cc cc cc|   /     
    |cc cc cc cc|  /         
    |0a 00 00 00| /  a    ebp-8 0x008FF990
    |cc cc cc cc|/
    |  ebp      |   ebp 0x008ff998 
    |           |\  
    |           | \  
    |           |   __tmainCRTStartup
    |           | /
    |           |/ 
    |           |           高地址   
    
            int b = 20;
    002918DC  mov         dword ptr [ebp-14h],14h  
        把b放在 ebp-20的位置   14h == 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

请添加图片描述

六、在main中创建变量c并存储

int c = 0;
002918E3 mov dword ptr [ebp-20h],0

c变量的值存到main开辟的空间中, 位置是([ebp-20h])的位置

                                低地址
    |  edi      |   esp  0x008ff8a8
    |  esi      |   
    |  ebx      |
    |cc cc cc cc|\
    |00 00 00 00|  \ c    ebp-20h 0x008FF978
    |cc cc cc cc|    \ 
    |cc cc cc cc|     main   
    |14 00 00 00|    /b    ebp-14h 0x008FF984
    |cc cc cc cc|   /     
    |cc cc cc cc|  /         
    |0a 00 00 00| /  a    ebp-8 0x008FF990
    |cc cc cc cc|/
    |  ebp      |   ebp 0x008ff998 
    |           |\  
    |           | \  
    |           |   __tmainCRTStartup
    |           | /
    |           |/ 
    |           |           高地址 
    
        	int c = 0;
    002918E3  mov         dword ptr [ebp-20h],0 
        将ebp-20h的位置存放c
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

请添加图片描述


此时:完成了main函数 的开辟, int a = 10; int b = 20; int c = 0; 的创建。
接下来:是将ab的值做临时拷贝, 为Add函数开辟空间。


七、函数传参:将b压栈

c = Add(a, b);
002918EA mov eax,dword ptr [ebp-14h]
002918ED push eax

[ebp-14h]位置上的值(b)存储到eax
eax进行压栈
.
为调用Add函数做准备 因为传参是a b 所以将a b 先压栈到栈区 以便后面调用
EAX寄存器也称为累加器
push压栈操作

                            低地址
    |           |
    |14 00 00 00|  b esp  0x008ff8a4                 
    |  edi      |         0x008ff8a8
    |  esi      |   
    |  ebx      |
    |cc cc cc cc|\
    |00 00 00 00|  \ c    ebp-20h 0x008FF978
    |cc cc cc cc|    \ 
    |cc cc cc cc|     main   
    |14 00 00 00|    /b    ebp-14h 0x008FF984
    |cc cc cc cc|   /     
    |cc cc cc cc|  /         
    |0a 00 00 00| /  a    ebp-8 0x008FF990
    |cc cc cc cc|/
    |  ebp      |   ebp 0x008ff998 
    |           |\  
    |           |   __tmainCRTStartup
    |           |/ 
    |           |           高地址 
    
            c = Add(a, b);
    002918EA  mov         eax,dword ptr [ebp-14h]  
    002918ED  push        eax  
    将[ebp-14h]的值放在eax寄存器上   再将eax压栈
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

请添加图片描述

八、函数传参:将a压栈

002918EE mov ecx,dword ptr [ebp-8]
002918F1 push ecx

[ebp-8](a)的值存在ecx
ecx进行压栈
ECX 是计数器(counter), 是重复(REP)前缀指令和LOOP指令的内定计数器。

                            低地址
    |0a 00 00 00|  ecx(a) esp  0x008ff8a0
    |14 00 00 00|  eax(b)      0x008ff8a4                
    |  edi      |              0x008ff8a8
    |  esi      |   
    |  ebx      |
    |cc cc cc cc|\
    |00 00 00 00|  \ c    ebp-20h 0x008FF978
    |cc cc cc cc|    \ 
    |cc cc cc cc|     main   
    |14 00 00 00|    /b    ebp-14h 0x008FF984
    |cc cc cc cc|   /     
    |cc cc cc cc|  /         
    |0a 00 00 00| /  a    ebp-8 0x008FF990
    |cc cc cc cc|/
    |  ebp      |   ebp 0x008ff998 
    |           |\  
    |           |   __tmainCRTStartup
    |           |/ 
    |           |           高地址 
    
    002918EE  mov         ecx,dword ptr [ebp-8]  
    002918F1  push        ecx 
    将ebp-8(a) 的值存放在ecx  将ecx压栈                    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

请添加图片描述

九、压栈call的下一条指令的地址

002918F2 call 002910B4
002918F7 add esp,8

压栈call指令 的下一条指令的地址 为了返回时继续执行代码
因为此语句执行完 会进入Add函数
首先记住你call下一条指令的地址

这里002918F7一会,会被存储到途中箭头所指向的位置请添加图片描述
此时压栈成功 跳到函数里 可以看到002918F7以小端的形式存储到内存中
小端存储:将低位字节存放在低地址处 请添加图片描述

    002918F2  call        002910B4  
    002918F7  add         esp,8  
        call执行后 下一条指令的地址会被压栈
        
                                    低地址
    |f7 18 29 00|  call的下一条指令  esp  0x008FF89C
    |0a 00 00 00|  ecx(a)                 0x008ff8a0
    |14 00 00 00|  eax(b)                 0x008ff8a4    
    |  edi      |                         0x008ff8a8
    |  esi      |   
    |  ebx      |
    |cc cc cc cc|\
    |00 00 00 00|  \ c    ebp-20h 0x008FF978
    |cc cc cc cc|    \ 
    |cc cc cc cc|     main   
    |14 00 00 00|    /b    ebp-14h 0x008FF984
    |cc cc cc cc|   /     
    |cc cc cc cc|  /         
    |0a 00 00 00| /  a    ebp-8 0x008FF990
    |cc cc cc cc|/
    |  ebp      |   ebp 0x008ff998 
    |           |\  
    |           |   __tmainCRTStartup
    |           |/ 
    |           |           高地址 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25


此时的反汇编代码:是VS2019的底层逻辑跳转的意思 跳转到地址00291770的位置
继续按下F11会跳转都以下界面

(借用一下,下面语句的图,此时你看到了Add函数 ,就可以继续阅读反汇编指令了)请添加图片描述



十、进入函数内部

此时你又看到了一大堆 反汇编代码

好家伙 差点劝退有没有
到这里我们已经进行的45%
以下代码 看看就好,不用理解 也很简单 跟我一起看下去吧 ヾ(◍°∇°◍)ノ゙

    int Add(int x, int y)
    {
    00291770  push        ebp  
    00291771  mov         ebp,esp  
    00291773  sub         esp,0CCh  
    00291779  push        ebx  
    0029177A  push        esi  
    0029177B  push        edi  
    0029177C  lea         edi,[ebp-0Ch]  
    0029177F  mov         ecx,3  
    00291784  mov         eax,0CCCCCCCCh  
    00291789  rep stos    dword ptr es:[edi]  
    0029178B  mov         ecx,29C003h  
    00291790  call        0029131B  
    	int z = 0;
    00291795  mov         dword ptr [ebp-8],0  
    	z = x + y;
    0029179C  mov         eax,dword ptr [ebp+8]  
    0029179F  add         eax,dword ptr [ebp+0Ch]  
    002917A2  mov         dword ptr [ebp-8],eax  
    	return z;
    002917A5  mov         eax,dword ptr [ebp-8]  
    }
    002917A8  pop         edi  
    002917A9  pop         esi  
    002917AA  pop         ebx  
    002917AB  add         esp,0CCh  
    002917B1  cmp         ebp,esp  
    002917B3  call        00291244  
    002917B8  mov         esp,ebp  
    002917BA  pop         ebp  
    002917BB  ret 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

十一、压栈ebp的地址

00291770 push ebp

ebp 是栈底指针 维护main函数 现在要维护Add函数 所以要向上移动
此代码的意思是:将ebp的地址进行压栈
可以看到内存中|98 f9 8f 00|就是ebp的地址

                                        低地址
    |98 f9 8f 00|  ebp(main)        esp  0x008ff898
    |f7 18 29 00|  call的下一条指令       0x008FF89C
    |0a 00 00 00|  ecx(a)                 0x008ff8a0
    |14 00 00 00|  eax(b)                 0x008ff8a4    
    |  edi      |                         0x008ff8a8
    |  esi      |   
    |  ebx      |
    |cc cc cc cc|\
    |00 00 00 00|  \ c    ebp-20h 0x008FF978
    |cc cc cc cc|    \ 
    |cc cc cc cc|     main   
    |14 00 00 00|    /b    ebp-14h 0x008FF984
    |cc cc cc cc|   /     
    |cc cc cc cc|  /         
    |0a 00 00 00| /  a    ebp-8 0x008FF990
    |cc cc cc cc|/
    |  ebp      |   ebp 0x008ff998 
    |           |\  
    |           |   __tmainCRTStartup
    |           |/ 
    |           |           高地址 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

请添加图片描述

十二、让ebp(栈底指针)指向esp(栈顶指针)的位置

00291771 mov ebp,esp

此语句的意思是:ebp = esp
mov(汇编指令)把一个字节、字或双字的操作数从源位置传送到目的位置,源操作数的内容不变。

    00291771  mov         ebp,esp
        把esp的值赋给ebp
        让ebp指向现在的esp
    
                                            低地址
    |98 f9 8f 00|  main-ebp     ebp  esp  0x008ff898
    |f7 18 29 00|  call的下一条指令       0x008FF89C
    |0a 00 00 00|  ecx(a)                 0x008ff8a0
    |14 00 00 00|  eax(b)                 0x008ff8a4    
    |  edi      |                         0x008ff8a8
    |  esi      |   
    |  ebx      |
    |cc cc cc cc|\
    |00 00 00 00|  \ c    ebp-20h 0x008FF978
    |cc cc cc cc|    \ 
    |cc cc cc cc|     main   
    |14 00 00 00|    /b    ebp-14h 0x008FF984
    |cc cc cc cc|   /     
    |cc cc cc cc|  /         
    |0a 00 00 00| /  a    ebp-8 0x008FF990
    |cc cc cc cc|/
    |  ebp      |   *ebp* 0x008ff998 
    |           |\  
    |           |   __tmainCRTStartup
    |           |/ 
    |           |           高地址
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

请添加图片描述

十三、为Add函数开辟空间

00291773 sub esp,0CCh

esp减去0cch个数值 此操作将esp向上(低地址)移动 , 并开辟出 维护空间

    00291773  sub         esp,0CCh
        将esp - 0CCH
        esp向低地址移动 为Add函数开辟空间
                             低地址
    |           |
    |           |
    |           |
    |           |\   esp  0x008ff7cc
    |           | \
    |           |  \
    |           |   \
    |           |    \
    |           |     \
    |           |      Add
    |           |     /
    |           |    /
    |           |   /
    |           |  /
    |           | /
    |           |/                      
    |98 f9 8f 00|  main-ebp         ebp  0x008ff898
    |f7 18 29 00|  call的下一条指令       0x008FF89C
    |0a 00 00 00|  ecx(a)                 0x008ff8a0
    |14 00 00 00|  eax(b)                 0x008ff8a4    
    |  edi      |                         0x008ff8a8
    |  esi      |   
    |  ebx      |
    |cc cc cc cc|\
    |00 00 00 00|  \ c    ebp-20h 0x008FF978
    |cc cc cc cc|    \ 
    |cc cc cc cc|     main   
    |14 00 00 00|    /b    ebp-14h 0x008FF984
    |cc cc cc cc|   /     
    |cc cc cc cc|  /         
    |0a 00 00 00| /  a    ebp-8 0x008FF990
    |cc cc cc cc|/
    |  ebp      |   *ebp* 0x008ff998 
    |           |\  
    |           |   __tmainCRTStartup
    |           |/ 
    |           |           高地址
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

忘记截图啦 跟上面main函数开辟空间是一样的

十四、压栈

00291779 push ebx
0029177A push esi
0029177B push edi

记不记得上面见过这些寄存器 所以操作是一样的
暂时不用理解这些寄存器
esp 自动指向最低地址处(栈顶)

                                 低地址
    |98 f9 8f 00| edi    esp 0x008ff7c0
    |23 10 29 00| esi
    |00 70 75 00| ebx
    |           |\           0x008ff7cc
    |           | \
    |           |  \
    |           |   \
    |           |    \
    |           |     \
    |           |      Add
    |           |     /
    |           |    /
    |           |   /
    |           |  /
    |           | /
    |           |/                      
    |98 f9 8f 00|  main-ebp         ebp  0x008ff898
    |f7 18 29 00|  call的下一条指令       0x008FF89C
    |0a 00 00 00|  ecx(a)                 0x008ff8a0
    |14 00 00 00|  eax(b)                 0x008ff8a4    
    |  edi      |                         0x008ff8a8
    |  esi      |   
    |  ebx      |
    |cc cc cc cc|\
    |00 00 00 00|  \ c    ebp-20h 0x008FF978
    |cc cc cc cc|    \ 
    |cc cc cc cc|     main   
    |14 00 00 00|    /b    ebp-14h 0x008FF984
    |cc cc cc cc|   /     
    |cc cc cc cc|  /         
    |0a 00 00 00| /  a    ebp-8 0x008FF990
    |cc cc cc cc|/
    |  ebp      |   *ebp* 0x008ff998 
    |           |\  
    |           |   __tmainCRTStartup
    |           |/ 
    |           |           高地址
    
    00291779  push        ebx  
    0029177A  push        esi  
    0029177B  push        edi
    压栈 ebx  esi  edi
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

请添加图片描述

十五、初始化Add函数

0029177C lea edi,[ebp-0Ch]
0029177F mov ecx,3
00291784 mov eax,0CCCCCCCCh
00291789 rep stos dword ptr es:[edi]

很熟悉有没有 如果你没记住 没关系我们再讲一遍
.
[ebp-0Ch]这么些空间放在edi
3存放在ecx
0CCCCCCCCh 存放在eax
ebp(栈底指针)向下ecx(3)个位置初始化成eax(cc cc cc cc)的值
.
说人话就是 将Add函数3个空间初始化为cc cc cc cc
前三句是存储 ,最后一局执行

请添加图片描述

                                     低地址
    |98 f9 8f 00| edi    esp 0x008ff7c0
    |23 10 29 00| esi
    |00 70 75 00| ebx
    |           |\           0x008ff7cc
    |           | \
    |           |  \
    |           |   \
    |           |    \
    |           |     \
    |           |      Add
    |           |     /
    |           |    /
    |           |   /
    |cc cc cc cc|  /
    |cc cc cc cc| /
    |cc cc cc cc|/                      
    |98 f9 8f 00|  main-ebp         ebp  0x008ff898
    |f7 18 29 00|  call的下一条指令       0x008FF89C
    |0a 00 00 00|  ecx(a)                 0x008ff8a0
    |14 00 00 00|  eax(b)                 0x008ff8a4    
    |  edi      |                         0x008ff8a8
    |  esi      |   
    |  ebx      |
    |cc cc cc cc|\
    |00 00 00 00|  \ c    ebp-20h 0x008FF978
    |cc cc cc cc|    \ 
    |cc cc cc cc|     main   
    |14 00 00 00|    /b    ebp-14h 0x008FF984
    |cc cc cc cc|   /     
    |cc cc cc cc|  /         
    |0a 00 00 00| /  a    ebp-8 0x008FF990
    |cc cc cc cc|/
    |  ebp      |   *ebp* 0x008ff998 
    |           |\  
    |           |   __tmainCRTStartup
    |           |/ 
    |           |           高地址  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38


回顾一下Add函数内部的代码

int Add(int x, int y)
{
	int z = 0;
	z = x + y;
	return z;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

十六、函数内创建z并赋值0

int z = 0;
00291795 mov dword ptr [ebp-8],0

0放在[ebp-8]的位置上
此时vs2019可能跳转了 暂时不用管 继续F11 跳回去

                                         低地址
    |98 f9 8f 00| edi    esp 0x008ff7c0
    |23 10 29 00| esi
    |00 70 75 00| ebx
    |           |\           0x008ff7cc
    |           | \
    |           |  \
    |           |   \
    |           |    \
    |           |     \
    |           |      Add
    |           |     /
    |           |    /
    |           |   /
    |cc cc cc cc|  /
    |00 00 00 00| / z (ebp-80x008FF890
    |cc cc cc cc|/                      
    |98 f9 8f 00|  main-ebp         ebp  0x008ff898
    |f7 18 29 00|  call的下一条指令       0x008FF89C
    |0a 00 00 00|  ecx(a`) ebp+8          0x008ff8a0
    |14 00 00 00|  eax(b`) ebp+c          0x008ff8a4    
    |  edi      |                         0x008ff8a8
    |  esi      |   
    |  ebx      |
    |cc cc cc cc|\
    |00 00 00 00|  \ c    ebp-20h 0x008FF978
    |cc cc cc cc|    \ 
    |cc cc cc cc|     main   
    |14 00 00 00|    /b    ebp-14h 0x008FF984
    |cc cc cc cc|   /     
    |cc cc cc cc|  /         
    |0a 00 00 00| /  a    ebp-8 0x008FF990
    |cc cc cc cc|/
    |  ebp      |   *ebp* 0x008ff998 
    |           |\  
    |           |   __tmainCRTStartup
    |           |/ 
    |           |           高地址
    
    int z = 0;
    00291795  mov         dword ptr [ebp-8],0 
        创建z变量赋值为0
        此时vs2019可能跳转了 暂时不用管 继续F11 跳回去
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

请添加图片描述

十七、函数内计算z = a+b

z = x + y;
0029179C mov eax,dword ptr [ebp+8]
0029179F add eax,dword ptr [ebp+0Ch]
002917A2 mov dword ptr [ebp-8],eax

读取ebp+8的值赋给eax eax = 10
ebp+0ch的值累加到eax eax = 10+20
eax的值 赋值给ebp-8的位置 z = 30
.
1e十六进制转换为十进制等于30

                                             低地址
    |98 f9 8f 00| edi    esp 0x008ff7c0
    |23 10 29 00| esi
    |00 70 75 00| ebx
    |           |\           0x008ff7cc
    |           | \
    |           |  \
    |           |   \
    |           |    \
    |           |     \
    |           |      Add
    |           |     /
    |           |    /
    |           |   /
    |cc cc cc cc|  /
    |1e 00 00 00| / z (ebp-80x008FF890
    |cc cc cc cc|/                      
    |98 f9 8f 00|  main-ebp         ebp  0x008ff898
    |f7 18 29 00|  call的下一条指令       0x008FF89C
    |0a 00 00 00|  ecx(a`) ebp+8          0x008ff8a0
    |14 00 00 00|  eax(b`) ebp+c          0x008ff8a4    
    |  edi      |                         0x008ff8a8
    |  esi      |   
    |  ebx      |
    |cc cc cc cc|\
    |00 00 00 00|  \ c    ebp-20h 0x008FF978
    |cc cc cc cc|    \ 
    |cc cc cc cc|     main   
    |14 00 00 00|    /b    ebp-14h 0x008FF984
    |cc cc cc cc|   /     
    |cc cc cc cc|  /         
    |0a 00 00 00| /  a    ebp-8 0x008FF990
    |cc cc cc cc|/
    |  ebp      |   *ebp* 0x008ff998 
    |           |\  
    |           |   __tmainCRTStartup
    |           |/ 
    |           |           高地址
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

请添加图片描述

十八、将返z的值储到寄存器、然后弹栈

return z;
002917A5 mov eax,dword ptr [ebp-8]

[ebp-8]位置上的值 存储到eax
eax是寄存器 ,寄存器不在栈中, 所以弹栈的时候不会销毁 能顺利将值带回去
.
这里我们将弹栈一起运行完成
esp自动向高地址移动

科普

栈区使用 ,先压栈 ,当用完的时候就会弹栈
弹栈后弹出的位置将不属于现在的程序
但是值时保留的,原则不能访
当这块空间没被再次使用时
但是越界访问的时候也能访问到这里的值
这让我想起了数据恢复技术

    	return z;
    002917A5  mov         eax,dword ptr [ebp-8]
        函数即将返回 销毁 为了保存z
        将z的值 保存到寄存器eax中
    002917A8  pop         edi  
    002917A9  pop         esi  
    002917AA  pop         ebx 
        弹出 esi  esi ebx
    
    --|98 f9 8f 00| edi     0x008ff7c0
    --|23 10 29 00| esi
    --|00 70 75 00| ebx
    |           |\     esp   0x008ff7cc
    |           | \
    |           |  \
    |           |   \
    |           |    \
    |           |     \
    |           |      Add
    |           |     /
    |           |    /
    |           |   /
    |cc cc cc cc|  /
    |1e 00 00 00| / z (ebp-80x008FF890
    |cc cc cc cc|/                      
    |98 f9 8f 00|  main-ebp         ebp  0x008ff898
    |f7 18 29 00|  call的下一条指令       0x008FF89C
    |0a 00 00 00|  ecx(a`) ebp+8          0x008ff8a0
    |14 00 00 00|  eax(b`) ebp+c          0x008ff8a4    
    |  edi      |                         0x008ff8a8
    |  esi      |   
    |  ebx      |
    |cc cc cc cc|\
    |00 00 00 00|  \ c    ebp-20h 0x008FF978
    |cc cc cc cc|    \ 
    |cc cc cc cc|     main   
    |14 00 00 00|    /b    ebp-14h 0x008FF984
    |cc cc cc cc|   /     
    |cc cc cc cc|  /         
    |0a 00 00 00| /  a    ebp-8 0x008FF990
    |cc cc cc cc|/
    |  ebp      |   *ebp* 0x008ff998 
    |           |\  
    |           |   __tmainCRTStartup
    |           |/ 
    |           |           高地址
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

1请添加图片描述
2
请添加图片描述

马上就要完成啦!坚持住 O(∩_∩)O哈哈~(已完成86%)

十九、弹栈、销毁Add函数

002917AB add esp,0CCh
002917B1 cmp ebp,esp
002917B3 call 00291244

esp加上0cch 此时esp指向栈底(ebp)的位置
esp指向的位置里的值 赋值给ebp 可以看到|98 f9 8f 00|是维护main函数栈底的位置,这下你明白了为什么要存储这个位置了吗
第三句暂时先不看

002917B8 mov esp,ebp

ebp赋值给esp 也就是让esp维护ebp的位置,ebp已经指向了main函数的空间栈底

    002917AB  add         esp,0CCh  
    002917B1  cmp         ebp,esp  
    002917B3  call        00291244 
         
    002917B8  mov         esp,ebp 
        将ebp赋值给esp
        
    |           |\        0x008ff7cc
    |           | \
    |           |  \
    |           |   \
    |           |    \
    |           |     \
    |           |      Add
    |           |     /
    |           |    /
    |           |   /
    |cc cc cc cc|  /
    |1e 00 00 00| / z (ebp-80x008FF890
    |cc cc cc cc|/                      
    |98 f9 8f 00|  main-ebp     esp   ebp  0x008ff898
    |f7 18 29 00|  call的下一条指令       0x008FF89C
    |0a 00 00 00|  ecx(a`) ebp+8          0x008ff8a0
    |14 00 00 00|  eax(b`) ebp+c          0x008ff8a4    
    |  edi      |                         0x008ff8a8
    |  esi      |   
    |  ebx      |
    |cc cc cc cc|\
    |00 00 00 00|  \ c    ebp-20h 0x008FF978
    |cc cc cc cc|    \ 
    |cc cc cc cc|     main   
    |14 00 00 00|    /b    ebp-14h 0x008FF984
    |cc cc cc cc|   /     
    |cc cc cc cc|  /         
    |0a 00 00 00| /  a    ebp-8 0x008FF990
    |cc cc cc cc|/
    |  ebp      |   *ebp* 0x008ff998 
    |           |\  
    |           |   __tmainCRTStartup
    |           |/ 
    |           |           高地址
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

请添加图片描述

002917BA pop ebp

ebp的位置上弹栈,并跳转到ebp中存储的地址中去(main函数栈底)

    002917BA  pop         ebp
        将ebp弹出 将ebp指向的函数 main-ebp 赋值给ebp
    
    --|98 f9 8f 00|  main-ebp               0x008ff898
    |f7 18 29 00|  call的下一条指令  esp  0x008FF89C
    |0a 00 00 00|  ecx(a`) ebp+8          0x008ff8a0
    |14 00 00 00|  eax(b`) ebp+c          0x008ff8a4    
    |  edi      |                         0x008ff8a8
    |  esi      |   
    |  ebx      |
    |cc cc cc cc|\
    |00 00 00 00|  \ c    ebp-20h 0x008FF978
    |cc cc cc cc|    \ 
    |cc cc cc cc|     main   
    |14 00 00 00|    /b    ebp-14h 0x008FF984
    |cc cc cc cc|   /     
    |cc cc cc cc|  /         
    |0a 00 00 00| /  a    ebp-8 0x008FF990
    |cc cc cc cc|/
    |  ebp      |                     ebp 0x008ff998 
    |           |\  
    |           |   __tmainCRTStartup
    |           |/ 
    |           |           高地址
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

请添加图片描述

二十、ret返回

002917BB ret

跳出返回 到 call 的下一条指令
还记得之前存储的call的下一条指令吗 ,此时弹栈就会找到call的下一条指令

    002917BB  ret 
        跳出返回 到 call 的下一条指令
    
    --|f7 18 29 00|  call的下一条指令       0x008FF89C
    |0a 00 00 00|  ecx(a`) ebp+8    esp   0x008ff8a0
    |14 00 00 00|  eax(b`) ebp+c          0x008ff8a4    
    |  edi      |                         0x008ff8a8
    |  esi      |   
    |  ebx      |
    |cc cc cc cc|\
    |00 00 00 00|  \ c    ebp-20h 0x008FF978
    |cc cc cc cc|    \ 
    |cc cc cc cc|     main   
    |14 00 00 00|    /b    ebp-14h 0x008FF984
    |cc cc cc cc|   /     
    |cc cc cc cc|  /         
    |0a 00 00 00| /  a    ebp-8 0x008FF990
    |cc cc cc cc|/
    |  ebp      |                     ebp 0x008ff998 
    |           |\  
    |           |   __tmainCRTStartup
    |           |/ 
    |           |           高地址
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

请添加图片描述

二十一、弹栈a、b变量的临时拷贝

002918F7 add esp,8

此举意为:esp+8 esp向高地址走
也就是把 ab还给操作系统了,不再维护

    002918F7  add         esp,8
        esp + 8 也就是把 a` b`还给操作系统了 
        
    --|0a 00 00 00|  ecx(a`) ebp+8          0x008ff8a0
    --|14 00 00 00|  eax(b`) ebp+c          0x008ff8a4   
    |  edi      |                 esp    0x008ff8a8
    |  esi      |   
    |  ebx      |
    |cc cc cc cc|\
    |00 00 00 00|  \ c    ebp-20h 0x008FF978
    |cc cc cc cc|    \ 
    |cc cc cc cc|     main   
    |14 00 00 00|    /b    ebp-14h 0x008FF984
    |cc cc cc cc|   /     
    |cc cc cc cc|  /         
    |0a 00 00 00| /  a    ebp-8 0x008FF990
    |cc cc cc cc|/
    |  ebp      |                     ebp 0x008ff998 
    |           |\  
    |           |   __tmainCRTStartup
    |           |/ 
    |           |           高地址
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

请添加图片描述

二十二、将函数返回值带回

002918FA mov dword ptr [ebp-20h],eax

eax的值赋值给 [ebp-20h]( c )中
eax 是函数返回时 z存放进去的30

    002918FA  mov         dword ptr [ebp-20h],eax  
        将eax的值赋值给 [ebp-20h](c)中
        eax 是函数返回时 z存放进去的30 
    
    |  edi      |                 esp    0x008ff8a8
    |  esi      |   
    |  ebx      |
    |cc cc cc cc|\
    |1e 00 00 00|  \ c    ebp-20h 0x008FF978
    |cc cc cc cc|    \ 
    |cc cc cc cc|     main   
    |14 00 00 00|    /b    ebp-14h 0x008FF984
    |cc cc cc cc|   /     
    |cc cc cc cc|  /         
    |0a 00 00 00| /  a    ebp-8 0x008FF990
    |cc cc cc cc|/
    |  ebp      |                     ebp 0x008ff998 
    |           |\  
    |           |   __tmainCRTStartup
    |           |/ 
    |           |           高地址
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

请添加图片描述


到此函数的栈帧就讲完了,梳理一下!

1.esp、ebp 维护__tmainCRTStartup()

2.栈区为main函数开辟空间,并初始化、创建a、b

3.拷贝a、b进行压栈(也就是传参——值传递)

4.Add函数在栈区开辟空间,并计算z值存到寄存器eax中

5.因为寄存器不会被销毁,弹栈后将eax赋值给c,以此带回函数的返回值



到此 后面的代码和Add返回的过程是一样的所以不做过多解释了!


局部变量是怎么创建的?
答:在栈中main函数的空间创建。
为什么局部变量的值是随机值?
答:因为初始化的时候全部初始化为cc cc cc cc 就是随机值。
函数是怎么传参的?
答:传值调用——是在栈区中临时拷贝一份参数,所以改变形参不会影响实参。
传参的顺序是怎样的?
答:Add(a,b); 先压栈右面的参数b。
形参和实参是什么关系?
答:形参是实参的一份临时拷贝。
函数调用是怎么做的?
答:调用的时候进行压栈,返回的时候进行弹栈销毁。
函数调用结束后是怎么返回的?
答:函数调用完,将返回值存在eax寄存器中,然后弹栈,用eax将值带回去。

此时函数的创建、传参、返回 都已经完成 函数的栈帧也就完成 main函数的返回 和Add相等 小伙伴们自己试一试吧!

如果你认真看完 有哪里不懂 可以评论私信加企鹅
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/569542
推荐阅读
相关标签
  

闽ICP备14008679号