浅谈main函数的栈帧

要理解什仫是栈帧首先就要理解什仫是栈?

那仫什仫是栈呢?在数据结构中有一种结构叫栈,它的定义为:仅在表尾进行插入和删除的操作

我们允许插入和删除的一端称为栈顶(esp),另一端则为栈底(ebp),所以栈又被称为后进先出的线性表(LIFO).而且我们知道在内存中空间的分配是从高地址向低地址增长的;

好了说了这仫多的栈那仫什仫是栈帧呢?其实说白了栈帧实际上就是用来记录函数调用过程的信息,是编译器用来实现函数调用过程的的一种数据结构.下图是我对栈帧分布的一点理解,以下都是在VC++6.0版本下测试的:

下面就让我们来看一道关于main函数栈帧的代码:

#include<iostream>
using namespace std;

int print()
{
	cout<<"i love you"<<endl;
	exit(1);
}

int Add(int a,int b)
{
	int z=0;
	*((int *)&z+2)=(int)print();
	z=a+b;
	return z;
}

int main()
{
	int a=10;
	int b=20;
	int ret=Add(a,b);
	cout<<ret<<endl;
	return 0;
}

这段代码的结果是什仫呢?

是不是感觉很惊讶?这也是程序员们表白的一种方式啦... 就不知道妹子能不能看的懂了...

通过观察发现这段代码有两个cout语句,为什仫会输出有字符i love you的语句呢?加了*((int *)&z+2)=(int)print()这条语句后会不会对代码的逻辑有影响呢?答案是肯定的.

要解决这几个问题就要分析求两个数的和的代码在汇编中是怎仫存储的也就是函数栈帧的调用过程,下面让我们先来打开反汇编来揭开main函数栈帧的神秘面纱吧...

刚进入反汇编就出现了如下的汇编语句?

这几条语句实现之后的结果在内存中为下图:

我们观察后之后的几条语句也就是lea之后的发现它的作用就是循环13h次给ebp初始化,不信你调开内存(Alt+6)观察ebp确实循环13h次被初始化为随机值。

下面就是所有的函数的压栈语句:

1.给a,b分配空间进行压栈的过程

2.执行Add函数的压栈过程,并记录call指令的下一条指令的地址

3.进入Add函数的内部,执行Add函数的栈帧

此时函数的压栈过程就结束了,下图是我个人对函数栈帧的一点理解:

调用完成后就是栈帧的释放过程了,以下在汇编语句中的释放命令:

在函数栈帧的释放过程中我们只以释放Add函数的栈帧为例,释放main函数的栈帧过程类似:

1).pop(出栈)掉edi,esi,ebx,此时esp指向原来的esp7的位置

2).把esp移动到ebp的位置,此时esp指向的是main函数的ebp位置(此时相当于已经弹出了Add函数的栈帧)

3).pop ebp也就是将弹出的ebp放入到ebp,此时我们发现pop的是main函数的ebp所以此时ebp就指向main函数的ebp,也就是ebp2,这也告诉我们为什仫要记录main函数的ebp的原因了

4).esp指向的是call指令的下一条指令的位置

看到这里我们应该就能理解为什仫文章开始的代码会输出print()函数的内容而不是输出main()函数的内容了吧...

记录call指令的下一条指令的地址就是为了能从Add()函数返回到main()函数,如果我们修改了这个地址使得Add()函数不能返回到main()函数而去执行另外一个函数print()函数,当然是不会输出main()函数的内容啦,因为当修改了call指令的下一条指令的地址后Add()函数就不能回到main()函数了又怎仫能输出main()函数的内容呢?

而语句*((int *)&z+2) 就是取出了call下一条指令的地址,通过上图发现z和这个地址只差八个字节,如果取出z的地址再加2也就是取出了这个call指令的下一条指令的地址,对它进行解引用使得这个地址指向的是print()函数,不能回到main()函数.

在这里函数的栈帧就分析结束了,如果有什仫理解的不全面或者需要改进的地方希望读者致信邮箱[email protected],Thanks

时间: 2024-11-07 06:40:54

浅谈main函数的栈帧的相关文章

浅谈Java中的栈和堆

人们常说堆栈堆栈,堆和栈是内存中两处不一样的地方,什么样的数据存在栈,又是什么样的数据存在堆中? 这里浅谈Java中的栈和堆 首先,将结论写在前面,后面再用例子加以验证. Java的栈中存储以下类型数据,栈对应的英文单词是Stack 基本类型 引用类型变量 方法 Java的堆中存储以下类型数据,堆对应的英文单词是Heap 实例对象 在函数中定义的一些基本类型的变量(8种)和对象的引用变量都是在函数的栈Stack内存中分配.当在一段代码块中定义一个变量时,java就在栈中为这个变量分配内存空间,当

Linux - 函数的栈帧

栈帧(stack frame),机器用栈来传递过程参数,存储返回信息,保存寄存器用于以后恢复,以及本地存储.为单个过程(函数调用)分配的那部分栈称为栈帧.栈帧其实是两个指针寄存器, 寄存器%ebp为帧指针,而寄存器%esp为栈指针,当程序运行时,栈指针可以移动(大多数的信息的访问都是通过帧指针的).总之简单一句话,栈帧的主要作用是用来控制和保存一个过程的 所有信息的.栈帧结构如下所示: 下面,我们通过一个简单的程序来了解栈帧: 简单的函数分析,如下图: 该函数的栈帧情况: 当*p=bug,修改栈

Linux进程的栈和进程中函数的栈帧

主要理解了Linux进程的栈和函数的栈帧的区别和联系 上图中刻画的是一个Linux进程的结构图,其中本文想要关注的栈如上图所示是栈底在0xc0000000地址递减的一块进程内存区域 Linux系统中为每个进程分配的实际大小未做深究 Linux进程的栈的整体认知就是这么多,即 1)栈底在0xc0000000(并不需要栈底指针,函数的栈帧才需要栈底指针) 2)栈的增长方向,地址递减方向 3)栈顶指针esp 4)大小未知(不是本文关注的重点) 5)里面存放的内容是什么呢? 另一个概念是上文中提到的函数

浅谈intval()函数用法

1 <?php 2 $sql=mysql_query("select count(*) as total from tb_leaveword ",$conn); 3 $infos=mysql_fetch_array($sql); 4 $total=$infos['total']; //获取总留言条数 5 if($total==0){ //如果总留言条数为0,则给出提示 6 echo "<div align=center>对不起,暂无留言!</div&g

浅谈javascript函数劫持

http://www.xfocus.net/articles/200712/963.html 浅谈javascript函数劫持 文章提交:hkluoluo (luoluonet_at_hotmail.com) by luoluo on 2007-11-30 luoluonet_at_yahoo.cn http://www.ph4nt0m.org 一.概述 javascript函数劫持,也就是老外提到的javascript hijacking技术.最早还是和剑心同学讨论问题时偶然看到的一段代码,大

【python】浅谈enumerate 函数

enumerate 函数用于遍历序列中的元素以及它们的坐标: >>> for i,j in enumerate(('a','b','c')):  print i,j 0 a 1 b 2 c >>> for i,j in enumerate([1,2,3]):  print i,j 0 1 1 2 2 3 >>> for i,j in enumerate({'a':1,'b':2}):  print i,j 0 a 1 b >>> fo

开发技术--浅谈Python函数

开发|浅谈Python函数 函数在实际使用中有很多不一样的小九九,我将从最基础的函数内容,延伸出函数的高级用法.此文非科普片~~ 前言 目前所有的文章思想格式都是:知识+情感. 知识:对于所有的知识点的描述.力求不含任何的自我感情色彩. 情感:用我自己的方式,解读知识点.力求通俗易懂,完美透析知识. 正文 首先介绍函数是什么,接着走进函数,并且发现函数的高级使用方法,最后列出常用的Python的内置函数. 函数是什么? 1.函数,在代码执行的是不执行,只有在调用函数的时候才会执行. 2.函数使用

浅谈PHP数据结构之栈

今天開始进阶自己的PHP,首先一切的编程语言都须要修炼自己的"内功",何为程序猿的"内功",我想大概就是数据结构和算法了吧 .毕竟是灵魂,是普通程序猿到高级程序猿的进阶. 不多说.直接说主题--"栈". 什么是栈,所谓栈就是遵循"后进先出"的原则. 先进栈的最后出栈. 用PHP实现栈无需考虑栈溢出的情况,相对来说比較easy实现,例如以下是经过学习和參考后的代码. <?php class Stack{ private $

libsvm代码阅读(2):svm.cpp浅谈和函数指针(转)

svm.cpp浅谈 svm.cpp总共有3159行代码,实现了svm算法的核心功能,里面总共有Cache.Kernel.ONE_CLASS_Q.QMatrix.Solver.Solver_NU.SVC_Q.SVR_Q 8个类(如下图1所示),而它们之间的继承和组合关系如图2.图3所示.在这些类中Cache.Kernel.Solver是核心类,对整个算法起支撑作用.在以后的博文中我们将对这3个核心类做重点注解分析,另外还将对svm.cpp中的svm_train函数做一个注解分析. 图1 图2 图3