栈和栈帧

堆栈(stack)又称为堆叠,是计算机科学里最重要且最基础的数据结构之一,它按照FILO(First In Last Out,后进先出)的原则存储数据。

栈的相关概念:

  1. 栈顶栈底:允许元素插入与删除的一端称为栈顶,另一端称为栈底
  2. 压栈:栈的插入操作,叫做进栈,也称压栈、入栈。
  3. 弹栈:栈的删除操作,也叫做出栈。

下面是栈的示意图,从图中可以清楚的看到,不管是插入数据还是删除数据,都是在栈顶进行的,还有就是FILO原则,可以看到,如果你想取出B的值,那么你必须先要将B的上面的C取出,要取出C的值,就得取出C上面的值,以此类推。
栈的示意图

从技术上说,就是CPU寄存器里的某个指针所指向的一片内存区域。这里所说的“某个指针”通常位于x86/x64平台的ESP寄存器/RSP寄存器,以及ARM平台的SP寄存器

操作栈的最常见的指令时PUSH(压栈)和POP(弹栈)。PUSH指令会对ESP/RSP/SP寄存器的值进行减法运算,使之减去4(32位)或8(64位),然后将操作数写到上述寄存器里的指针所指向的内存中

POP指令是PUSH指令的逆操作:它先从栈指针指向的内存中读取数据,用以备用(通常是写到其他寄存器里),然后再将栈指针的数值加上4或8.

下图演示了x86平台下的push指令和pop指令,指令push Z,首先ESP的值-4,然后将Z的值写入新的ESP所指的内存中,指令pop eax,先将Z的值存入EAX寄存器,然后进行ESP+4。指令POP EBX,首先将栈顶元素存入EBX,然后ESP+4
在这里插入图片描述
下面通过一个例子说明,首先是push eax,此时eax的值为0x115fcc0ESP的值为0x115fc68,栈顶的值为0x75936359

在这里插入图片描述
push eax执行完之后结果如下图,此时ESP的值为0x115fc64(为原来ESP的值-4,注意多数栈是逆增长的,也就是向低地址增长),栈顶的值为0x115fcc0(EAX的值)
在这里插入图片描述pop ebx的指令执行完时,此时ebx的值为0x115fcc0(从栈顶弹出来的),ESP的值为0x115fc68上一步ESP的值+4),此时之前的数据0x115fcc0依然在内存中(地址为0x115fc64的地方),只不过这个值不再是栈的一部分了,因为ESP指向的是栈顶。
在这里插入图片描述### 栈在进程中的作用如下:

  1. 暂时保存函数内的局部变量。
  2. 调用函数时传递参数。
  3. 保存函数返回的地址。

栈帧

栈帧也叫过程活动记录,是编译器用来实现过程/函数调用的一种数据结构。简言之,栈帧就是利用EBP(栈帧指针,请注意不是ESP)寄存器访问局部变量、参数、函数返回地址等的手段。

;栈帧结构
PUSH EBP			;函数开始(使用EBP前先把已有值保存到栈中)
MOV EBP, ESP		;保存当前ESPEBP...					;函数体
					;无论ESP值如何变化,EBP都保持不变,可以安全访问函数的局部变量、参数
					
MOV ESP, EBP		;将函数的起始地址返回到ESPPOP EBP				;函数返回前弹出保存在栈中的值
RETN				;函数终止

每一次函数的调用,都会在调用栈(call stack)上维护一个独立的栈帧(stack frame)。每个独立的栈帧一般包括:

  • 函数的返回地址和参数
  • 临时变量:包括函数的非静态局部变量以及编译器自动生成的其他临时变量
  • 函数调用的上下文

栈是从高地址向低地址延伸,一个函数的栈帧用EBP和ESP这两个寄存器来划定范围。EBP指向当前栈帧的底部,ESP始终指向栈帧的顶部。

EBP寄存器又被称为帧指针(Frame Pointer)

ESP寄存器又被称为栈指针(Stack Pointer)

一个很常见的活动记录示例如图所示
活动记录图

关于函数调用的讲解,可以参考另一篇文章(https://blog.csdn.net/Casuall/article/details/88783277),也是pwnable.kr的第三道题的WP,一个简单的栈溢出的题。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值