
AI-摘要
PrideLzh GPT
AI初始化中...
介绍自己 🙈
生成本文简介 👋
推荐相关文章 📖
前往主页 🏠
前往爱发电购买
5-控制转移与堆栈
pridelizihao一、指令指针寄存器和简单控制转移
(1)指令指针寄存器
指令指针寄存器EIP:
- IA-32系列CPU有一个32位的指令指针寄存器EIP,它始终指向当前处理的指令。
- 它是早先8086CPU指令指针寄存器IP的扩展
- 由CS和EIP确定所取指令的存储单元地址。段寄存器CS给出当前段代码段的段号,指令指 针寄存器EIP给出偏移。即
CS:EIP
- 如果代码段起始地址为0,则EIP给出的偏移直接决定所取指令的存储单元地址
- 实方式下,段的最大范围是64k,EIP中高16位必须为0,只有低16位的IP起作用
顺序执行指令的过程
CPU执行代码(程序)就是一条接一条地执行机器指令。可以把CPU执行指令的过程看做一条处理指令的流水线,通过以下两个步骤实现的指令的顺序执行。- 从存储器取指令
- 根据指令长度,自动调整指令指针寄存器EIP的值,使其指向下一条指令
这些工作是CPU自动完成的,只需要把我们编写的汇编程序存入代码段,就可以自动顺序执行了。
控制转移指令
控制转移指令,它通过直接改变EIP寄存器的内容,实现指令执行过程中的跳转转移
:非自动顺序调整EIP内容控制转移指令
:专门用于改变EIP内容的指令- 各种控制转移指令用于根据不同的情形改变EIP内容,从而实现转移,包括:
- 条件转移指令
- 无条件转移指令
- 循环指令
- 函数调用及返回指令
(2)常用条件转移指令
- 格式:
Jcc LABEL
- 操作:
jcc
代表各种条件转移指令的缩写(助记符),当条件满足时,转到标号LABEL
处执行;否则顺序执行 - 注意:就好像小于和不大于等于是一码事,同一条指令也可能有多个助记符,见下表
(3)比较指令和数值大小比较
- 比较指令
名称 | CMP(比较指令) |
---|---|
格式 | CMP DEST,SRC |
动作 | 根据DEST-SRC的差 影响标志寄存器中各状态标志,但不结果作为结果的差值送目的寄存器 |
合法值 | SRC:通用寄存器、存储单元、立即数 |
DEST:通用寄存器、存储单元 | |
注意 | DEST 和 SRC 必须尺寸一致 |
除了不把差值结果送DEST外,其他和SUB指令完全一致 |
1 | CMP EDX, -2 ;把EDX与-2比较 |
- 比较数值的大小
- 一般使用比较指令CMP。
- 根据零标志ZF判断是否相等(
JN/JE
)- 如果都是无符号数,可根据进位CF判断大小(
JNB/JAE/JC
) - 如果都是有符号数,同时根据符号标志SF和溢出标志OF判断(
JL/JNGE
)
- 如果都是无符号数,可根据进位CF判断大小(
- IA-32同时提供两套以数值大小为条件的条件转移指令,分别使用无符号数之间比较和有符号数之间比较。二者判断标志不同
- 有符号数间称:大于(G),等于(E),小于(L)
- 无符号数间称:高于(A),等于(E),低于(B)
示例:
1 | //假设ECX和EDX存储两个数,现在要把较大的存在ECX中,较小的存在EDX中 |
示例:
1 | //用汇编实现下面C函数转的功能: |
(4)简单无条件转移指令
名称 | CMP(比较指令) |
---|---|
格式 | JMP LABEL |
动作 | 指令控制无条件转移到LABEL处 |
注意 | 是段内转移,没有任何前提一定发生转移,类似C中的goto |
通常用在if-else分支用,if分支结束后跳过else分支 |
示例:
1 | //原始C代码(_fastcall表示用寄存器传参数) |
二、堆栈和堆栈操作
(1)堆栈
程序的运行和堆栈有密切关系
- cpu运行期间需要堆栈保存某些关键信息
- 程序自身用堆栈保存一些临时数据
堆栈
堆栈
:一段内存区域,对他的访问限于一端进行。存储于堆栈段,段寄存器为SS栈底
:堆栈中地址较大的一端栈顶
:堆栈中地址较小的一端
堆栈的操作
- 后进先出原则,所有存取在栈顶进行(存入数据的地址越来越小,堆栈生长方向为从高地址到低地址)
进栈/压栈操作
:存入数据出栈/弹出操作
:取出数据
堆栈相关寄存器
寄存器 | 存储内容 |
---|---|
SS(堆栈段寄存器) | 当前堆栈段号,指示堆栈所在内存区域的位置 |
ESP(堆栈指针寄存器) | 栈顶的偏移,SS:ESP永远指向栈顶,CPU自动控制 |
EBP(堆栈数据寄存器) | 栈内数据的偏移,SS:EBP指向栈中一个数据 (习惯指向函数帧栈底),手动控制 |
堆栈平衡
堆栈平衡
:在函数调用前后esp和ebp的值应当相同- 为何要做堆栈平衡:esp和ebp寄存器在子函数调用时是非常重要的(见下方说明),其值在调用过程中会发生改变。一个程序中可能有很多函数,有时还会有嵌套调用的情况,但CPU只有esp和ebp两个寄存器,怎么处理大量的函数呢?intel的策略是同一时刻只处理该时刻执行的函数,也就是说esp和ebp的值在不断刷新。如果一个函数执行后没有恢复esp和ebp指针,就会影响它前后及嵌套的函数,使它们操作堆栈时地址错乱。
堆栈的用途
- 保护寄存器、保护现场
- 保存返回地址
- 传递参数
- 安排局部变量或临时变量
- 反转一组数据
一张很清晰的图片
(2)堆栈操作指令
- 进栈指令
名称 | PUSH(进栈指令) |
---|---|
格式 | PUSH SRC |
动作 | 把源操作数SRC压入堆栈,并调整esp指向栈顶 |
合法值 | SRC:32/16位通用Reg或段Reg;双字/字存储单元;立即数 |
注意 | 双字入栈:ESP-4,然后把双字送到ESP所指的数据单元 |
字入栈:ESP-2,然后把字送到ESP所指的数据单元 | |
至少进栈一个字 | |
示例: |
1 | PUSH EAX //把EAX的内容压入堆栈 |
- 出栈指令
名称 | POP(出栈指令) |
---|---|
格式 | POP DEST |
动作 | 从栈顶弹出一个双字/字到DEST,并调整esp指向栈顶 |
合法值 | DEST:32/16位通用Reg或段Reg;双字/字存储单元。但是不能是立即数或代码段寄存器CS |
注意 | 双字出栈:先从ESP所指存储单元弹出一个双字数据送DEST,然后ESP+=4 |
字出栈:先从ESP所指存储单元弹出一个字数据送DEST,然后ESP+=2 | |
至少出栈一个字 | |
DEST不能是立即数或代码段寄存器CS | |
示例: |
1 | POP ESI //从堆栈弹出一个双字到ESI |
1 |
|
- 通用寄存器全进出栈指令
- 有时需要把多个通用Reg压入栈,以保护值。为了提高效率,从80186开始提供了通用寄存器全进出栈指令
(1)16位通用Reg
名称 | PUSHA(16位通用寄存器全进栈指令) |
---|---|
格式 | PUSHA |
动作 | 将8个16位通用寄存器的内容压入堆栈,压入顺序:AX、CX、DX、BX、SP、BP、SI、DI |
名称 | POPA(16位通用寄存器全出栈指令) |
---|---|
格式 | POPA |
动作 | 从堆栈弹出内容,以PUSHA相反的顺序送通用寄存器 |
(2)32位通用Reg
名称 | PUSHAD(32位通用寄存器全进栈指令) |
---|---|
格式 | PUSHAD |
动作 | 将8个16位通用寄存器的内容压入堆栈,压入顺序:EAX、ECX、EDX、EBX、ESP、EBP、ESI、EDI |
名称 | POPAD(32位通用寄存器全出栈指令) |
---|---|
格式 | POPAD |
动作 | 从堆栈弹出内容,以PUSHA相反的顺序送通用寄存器 |
示例:
1 | //演示PUSHAD指令的执行效果,还演示另一种访问堆栈区域存储单元的方法 |
评论
匿名评论隐私政策
TwikooValine
✅ 你无需删除空行,直接评论以获取最佳展示效果