ret指令与call指令的深入理解
本篇原创作者: Rj45
背景
1、代码
2、编译
gcc Rj45.c -o Rj45 -m32
3、反编译
objdump -d Rj45 -M intel
0804840b <main>:
804840b: 8d 4c 24 04 lea ecx,[esp+0x4]
804840f: 83 e4 f0 and esp,0xfffffff0
8048412: ff 71 fc push DWORD PTR [ecx-0x4]
//从上述汇编代码可以看到,在底层中,main函数并不如我们接触的编程语言一样,为程序运行时第一个执行函数,还有函数在main函数前执行。
8048415: 55 push ebp
8048416: 89 e5 mov ebp,esp
8048418: 51 push ecx
8048419: 83 ec 04 sub esp,0x4
804841c: 83 ec 0c sub esp,0xc
804841f: 68 c0 84 04 08 push 0x80484c0//压入参数`hello,Rj45`所存储的地址
8048424: e8 b7 fe ff ff call 80482e0 <puts@plt>//调用puts函数进行打印
8048429: 83 c4 10 add esp,0x10//进行栈帧平衡
804842c: b8 00 00 00 00 mov eax,0x0
8048431: 8b 4d fc mov ecx,DWORD PTR [ebp-0x4]
8048434: c9 leave
8048435: 8d 61 fc lea esp,[ecx-0x4]
8048438: c3 ret //puts函数返回
8048439: 66 90 xchg ax,ax
804843b: 66 90 xchg ax,ax
804843d: 66 90 xchg ax,ax
804843f: 90 nop
4、问题 在反汇编的过程中,有两个比较关键的地方:call
和 ret
。
在《汇编语言(第3版)》中对这两个指令的概述为:
call和ret指令都是转移指令,它们都修改IP,或同时修改CS和IP。它们经常被共同用来实现子程序的设计,也即是调用和返回。
其中ret指令用栈中的数据,修改IP的内容,实现近转移。而call指令将IP或者CS和IP压入栈中,实现转移。还有retf指令,用栈中的数据,修改CS和IP的内容,实现远转移。
那么,如何理解这个概述,以及如何理解从概述延申出来的概念性的知识
什么是转移指令?
1、概念 转移指令是可以控制CPU执行内存中某处代码的指令,或者说,转移指令是可以修改IP,或同时修改CS和IP的指令。
根据转移行为可分为段内转移和段间转移,其中 只修改IP的叫段内转移;同时修改CS和IP的,叫段间转移
。
由于转移指令对IP或CS和IP的修改范围的不同,又分为短转移、近转移和远转移。
2、分类
无条件转移指令,如jmp
条件转移指令,如jz
循环指令,如loop
过程
中断
什么是CS和IP?
CS是代码段寄存器、IP是指令指针寄存器。
(8086CPU有四个段寄存器,CS、DS、SS、ES,当CPU要访问内存是由这四个段寄存器提供内存单元的段地址。)
这两个寄存器是8086CPU中最关键的寄存器,它们指示了CPU当前要读取指令的地址。
CS和IP如何指示CPU读取指令的地址?
8086机器中,任意时刻,CPU将CS:IP指向的内容当作指令执行。
意思是,任意时刻,设CS中的内容是M,IP中的内容为N,8086CPU将从内存M*16+N单元开始,读取一条指令并执行。
解释:CPU当前的状态是CS内数据为2000H,IP内为0000H
内存20000到20009H单元存放着可执行的机器码
1、CPU将从内存2000H*16+0000H出读取指令执行,其中将CS和IP内容送入地址加法器,地址加法器完成后,得到 物理地址=段地址*16+偏移地址
。
2、地址加法器将物理地址送人输入输出控制电路,输入输出控制电路将物理地址20000H送上地址总线。
3、从内存20000H单元开始存放的机器指令 B82301即mov ax,0123H
通过数据总线被送入CPU。
4、输入输出控制电路将机器指令送入指令缓冲器,接着到执行控制器。执行控制器执行指令后,AX内的内容为0123H。
5、读取一条指令后,IP中的值会自动增加,以使CPU可以读取下一个指令。因当前读取的指令 B82301
的长度为3,故IP的值加3,CS:IP指向内存单元2000:0003。
6、以此依次执行。
什么是段地址和偏移地址?
什么是段?段的划分来自于CPU,而不是内存,内存没有分段。
由于8086CPU的内存的物理地址=段地址*16+偏移地址,故使得在管理内存的时候借助了分段的概念,将若干个地址连续的内存单元看作一个段。
什么是段地址和偏移地址?这是8086CPU对内存读写图
CPU的两个相关部件提供两个16位地址、段地址和偏移地址。
段地址和偏移地址通过内部总线送到地址加法器。
地址加法器通过 物理地址=段地址*16+偏移地址
,合成20位物理地址。
输入输出电路将物理地址送上地址总线。故段地址和偏移地址也即CS内地址数据和IP内地址数据。
ret指令与call指令实现了什么功能?
1、ret指令用栈中的数据,修改IP的内容,返回代码段的第一条指令。相当于
pop IP
2、call指令将IP或者CS和IP压入栈中,实现转移。相当于
push IP
//push IP和push CS
jmp [address]
3、retf指令,用栈中的数据,修改CS和IP的内容,返回代码段的第一条指令。相当于
pop IP
pop CS
以ret和call实现的子程序源程序的框架
assume cs:code
code segment
main: :
:
call sub1//调用子程序sub1
:
mov ax,4c00h
int 21h
sub1: :
:
call sub2//调用子程序sub2
:
ret//子程序返回
sub2: ://子程序sub2开始
:
ret//子程序返回
code ends
end main
解释:一个写有一定功能的程序段叫子程序。在子程序需要被执行的时候用call指令去调用。当子程序执行完后,由于call指令后面的指令的地址已经存储在栈中( push操作
), 使得在子程序后面再使用ret指令时候,栈中的数据会设置IP内的值, IP内的值的设置实现了CPU继续执行call指令后面的代码指令,达到程序继续执行的目的。
参考
《汇编语言(第3版)》
- 我身边的一些数据库事故 (r5笔记第52天)
- 一个清理脚本的改进思路(r5笔记第51天)
- 【专业技术】Python爬虫:抓取手机APP的传输数据
- 海量数据迁移之传输表空间(一) (r5笔记第71天)
- 一条sql语句的改进探索(r5笔记第70天)
- 【专业技术】Node.js 究竟是什么?
- Github 项目推荐 | 用 Pytorch 实现的 WaveNet-Vocoder
- 重启数据库的一场闹剧(r5笔记第68天)
- 【C语言系列】基础语法案例分析(初级篇)
- 一次ORA-00600问题的排查和分析(r5笔记第64、65天)
- SpringMVC入门就这么简单
- pl/sql中的forall简单测试(r5笔记第63天)
- SpringMVC【开发Controller】详解
- 巧用外部表备份历史数据(r5笔记第62天)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法