【软件安全】Chapter2_1 堆栈基础

本文详细阐述了编程中的内存区域划分,包括栈区与堆区的对比,堆块的分配与释放机制,以及函数调用过程中的栈帧管理和寄存器的作用。重点介绍了32位CPU的寄存器结构和标志寄存器的功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、内存区域。

1. 编制程序的四个步骤

编辑,编译,链接,运行

编译:检查语法并对单个文件产生可执行程序(可执行文件内包含二进制机器代码)

链接:将多个可执行文件进行关联(函数调用信息),增加启动程序等

2. 什么是Bug

 一种表现为用户部分操作,例如输入,导致程序无法正常执行

3. 内存区域的划分

一个程序可能被分配到不同的内存区域执行。

 

处理器从代码区取出指令与操作数,送入算术逻辑单元;

开辟动态内存时,从堆区分配一块合适的区域返回给代码区的代码使用;

PS.动态:所需内存大小在程序设计时不能预先确定或内存过大无法在栈区分配。

调用发生时,函数调用信息关系等信息保存在栈区。

4. 堆区与栈区的对比

栈区堆区
存储函数运行时的局部变量、数组等程序运行时动态分配的内存
系统自动预留或回收内存空间需要程序员用专有函数进行申请(new)
低地址扩展,底高顶低,先进后出高地址扩展
连续不连续用链表来存储的空闲内存地址
内存地址减小方向延伸内存地址增大方向延伸
声明局部变量int b,系统自动开辟空间p = (char*) malloc(10)
速度快,但无法控制速度慢,易产生内存碎片,但方便

5.堆的内存组织

(1)堆块

空闲态堆块:链入空链表,由系统管理。Flink与Blink分别指向前后空闲块的地址。

占有态堆块:返回由程序员定义的堆块指针,从而对内存进行读写、释放,由程序员管理。

指向堆块的指针或句柄指向的是块身的首地址

大小:包括块首在内。e.g. 若申请32字节,则实际分配40字节。

单位:8字节(不足则按8字节分配)

(2)堆表

占有态堆块被使用其的程序索引,堆表只索引所有空闲态的堆块。

空表索引:大小128的指针数组,数组的每一项包括两个指针,用于标识一条空表。

Free[n] (n>0)标识堆中所有大小为8n字节的空闲堆块。

Free[0]标识零号空表,链入所有大于等于1028字节、小于512KB的堆块,升序排列。

(3)堆块的分配(以空表为例)

· 普通空表:

首先寻找对应大小的最优空闲块进行分配;

若失败,从稍大的块中按请求的大小割出一块进行次优分配,剩余部分重新标注块首链入空表。(找零)

· 零号空表:

分配时先查最后一个最大的,若能满足,再正向搜索最小能满足需求的空闲块。

(4)堆块的释放(以空表为例)

占用态->空闲态,链入相应表尾

(5)堆块的合并(以空表为例)

分配和释放可能引发堆块合并。两空闲堆块相邻时被合并。

流程:卸下,合并,修改块首,链入新表(合并时还有一种操作叫做内存紧缩)

二、函数调用。

1. 基本理解

(1)可执行文件中,代码是以函数为单元存储的。

(2)函数调用时借助系统栈完成函数状态的保存与恢复。

(3)函数被调用时,系统栈会为该函数开辟新的栈帧,并将其压入栈中。每个栈帧对应一个未运行完的函数,保存了该函数返回地址和局部变量

(4)当函数返回时,系统栈会弹出该函数对应的栈帧

2. 步骤

(1)参数入栈:先后顺序从右向左压入系统栈!!!

(2)返回地址入栈:将当前代码区调用指令的下一条指令地址压入栈中。

(3)代码区跳转:处理器从当前代码区跳转到被调用函数的入口处

(4)栈帧调整保存当前栈帧值将当前栈帧切换到新栈帧。(EBP入栈,ESP赋值给EBP,更新栈帧底部。)

(注意,在上图中,“局部变量”在“返回地址”上方,但这不意味着在函数调用时参数入栈在返回地址入栈之后!函数调用步骤中的参数入栈指的是不在这个函数中定义的变量,而非函数中的局部变量,可以参考Chapter4缓冲区溢出漏洞中提及的windows控制台程序的主函数参数对应的反汇编代码。)

三、寄存器。

1. 基本概念

(1)寄存器是中央处理器CPU的组成部分。(32位CPU指的就是寄存器大小4个字节

(2)寄存器是有限存贮容量的高速存贮部件,可用于暂存指令、数据和地址

(3)CPU本身只负责运算,自带一级缓存和二级缓存(视为读写较快的内存)。内存负责储存数据,但最常用、最频繁读写的数据都会放在寄存器中(如循环变量)。CPU优先读写寄存器,再由寄存器与内存交换数据。

(4)每个函数独占自己的栈帧空间,当前运行的函数的栈帧空间总在栈顶

(5)函数栈帧中一般包括:局部变量,栈帧状态值,函数返回地址

栈帧状态值:保存前栈帧的(顶部和)底部,用于本帧弹出后恢复上一个栈帧。(前栈帧的顶部即为当前栈帧的底部,所以实际不要存)

函数返回地址:保存函数调用前的指令位置。

2. 寄存器

(1)指针寄存器
ESP栈指针寄存器存放指向系统栈最上面一个栈帧的栈顶的指针extended stack pointer
EBP基址指针寄存器存放指向系统栈最上面一个栈帧的底部的指针extended base pointer

栈帧调整方法:(1)EBP入栈;(2)将ESP赋值给EBP,更新栈帧底部。

(先保存当前栈帧值,再将当前栈帧切换到新栈帧)

注意:不可分割成8位寄存器。作为通用寄存器,可存储算术逻辑运算的操作数和运算结果。

(2)指令指针寄存器
EIP指令指针寄存器存放指向下一条等待执行的指令地址的指针extended instruction pointer

此外:

IR指令寄存器存放当前从主存储器读出的正在执行的一条指令Instruction Register

当执行一条指令时,先把它从内存取到数据寄存器(DR,Data Register)中,然后再传送至IR。指令划分为操作码和地址码字段,由二进制数字组成。

在计算机工作的时候,CPU会从IP中获得关于指令的相关内存地址,然后按照正确的方式取出指令,并将指令放置到原来的指令寄存器中。

(3)数据寄存器
EAX累加器用于乘、除等使用频率高的操作,存放函数返回值Accumulator
EBX基地址寄存器可作为存储器指针使用,用来访问存储器Base Register
ECX计数寄存器在循环和字符串操作时,要用它来控制循环次数;在位操作中,当移多位时,要用CL来指明移位的位数。Count Register
EDX数据寄存器在进行乘、除运算时,可作为默认操作数参与运算,也可用于存放I/O的端口地址Data Register

32位CPU有4个32位通用寄存器EAX、EBX、ECX和EDX,具有可分可合的特性!

低16位寄存器分别为AX、BX、CX和DX,对低16位数据的存取,不会影响高16位的数据。

4个16位寄存器又可分割成8个独立的8位寄存器AH-AL、BH-BL、CH-CL、DH-DL,每个寄存器都有自己的名称,可独立存取。

(4)变址寄存器
ESI在内存操作指令中作为“源地址指针”使用
EDI在内存操作指令中作为“目的地址指针”使用

变址寄存器主要用来存放操作数的地址,用于堆栈操作和变址运算中计算操作数的有效地址。

往往有ESI、EDI存在的时候,就意味着数据的复制和移动。

32位CPU有2个32位通用寄存器ESI和EDI。其低16位对应先前CPU中的SI和DI,对低16位数据的存取,不影响高16位的数据。

(5)段寄存器
CS代码段寄存器代码段的段值
DS数据段寄存器数据段的段值
SS堆栈段寄存器堆栈段的段值
FS/ ES/ GS附加段寄存器附加数据段的段值

段寄存器是根据内存分段的管理模式而设置的。

内存单元的物理地址标准形式为段:偏移量

(可以认为一个段是一本书的某一页,偏移量是一页的某一行)

融合变址寄存器:在很多字符串操作指令中,DS:ESI指向源串,ES:EDI指向目标串。

(6)标志寄存器
ZF/ Z-Flag零标志可以设成0或者1
OF/O-Flag溢出标志反映有符号数加减运算是否溢出。如果运算结果超过了有符号数的表示范围,则OF置1,否则置0。
CF/C-Flag进位标志用于反映运算是否产生进位或借位。如果运算结果的最高位产生一个进位或借位,则CF置1,否则置0。

标志寄存器在32位操作系统中大小是32位的,也就是说,它可以存32个标志。

OF、CF主要和运算有关,ZF除了和运算有关还和一些状态有关。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值