不同特权级间代码段的跳转{ 门 + 跳转(jmp + call) + 返回(ret) }
【0】写在前面
- 0.1)我们讲 CPU的保护机制,它是可靠的多任务运行环境所必须的;
- 0.2) CPU保护机制:分为段级保护 + 页级保护;
- 0.2.1)段级保护分为:段限长 limit 检查、段类型 type 检查、特权级检查、等等……
- 0.3)我们着重讲 段特权级检查(bit 6~5): 特权级检查又分为:访问数据段时的特权级检查 + 代码段之间转移控制时的特权级检查;(这里,我们着重讲 代码段间转移控制时的特权级检查)
- 0.4)版权声明:本文部分内容图片总结于李炯的“Linux内核完全剖析“;
【1】进入代码段间转移控制时的特权级检查(引入门的概念)
1.0)使用jmp 或 call 指令可以实现以下4 种转移:
- 1)目标操作数包含目标代码段的段选择子;
- 2)目标操作数指向一个包含目标代码段选择子的调用门描述符;
- 3)目标操作数指向一个包含目标代码段选择子的TSS;
- 3)目标操作数指向一个任务门, 这个任务门指向一个包含目标代码段选择子的TSS;
1.1)直接调用或跳转到代码段
(Attention): JMP、CALl 和 RET指令的近转移只是在当前代码段中执行程序控制转移,因此不会执行特权级检查;JMP、CALL和 RET 指令的远转移 会把 控制转移到 另一个代码段中,因此处理器会进行特权级检查;
- 1.1.1)当不通过调用门把程序控制权转移到另一个代码段时,处理器会验证4种特权级和类型信息:
- (验证?如何验证? 拿 CPL和 选择子中的RPL 去和目的段描述符中的DPL 进行比较,验证)
- 1. 当前特权级 CPL;
- 2. 含有被调用过程的目的代码段描述符中的描述符特权级 DPL;
- 3. 目的代码段的段选择子中的请求特权级 RPL;
- 4. 目的代码段描述符中的一致性标记 C, 它确定了一个 代码段是非一致代码段(C=0)还是 一致代码段(C=1);
1.1.2)如何验证?-Validation
- V1)当访问非一致代码段(C=0):CPL 必须等于 目标代码段的DPL, 同时要求 RPL 小于 DPL (即RPL的特权级大于DPL),才能使得转移成功;
当非一致代码段的段选择子 被加载进 CS寄存器中, 特权级字段不会改变,即它仍然是 调用者的CPL; V2)当访问一致代码段时(C=1): 调用者的CPL 在数值上大于或等于目的代码段的 DPL (即DPL 的特权级要比 CPL大), 对于一致代码段的访问,处理器忽略对 RPL 的检查;
当程序控制被转移到一个 一致代码段中, CPL并不改变, 即使目的代码段的DPL在数值上小于CPL;由于 CPL 没有改变, 因此堆栈也不会发生切换;最后, 大多数代码段都是非一致代码段, 对于非一致代码段, 程序的控制权只能转移到 具有 相同特权级的代码段中, 除非转移时通过一个调用门进行的;
最后(这就是调用门存在的意义所在了,即调用门可以实现不同特权级间的非一致代码段的跳转)
1.2)门描述符
1.2.1)为了对具有不同特权级的代码段提供受控的访问,处理器提供了称为门描述符的特殊描述符集,有4种门描述符:
- 1. 调用门(call gate), 类型TYPE=12: 用于在不同特权级间实现受控的程序控制转移;(访问调用门要求CPL和RPL,必须小于或等于调用门的DPL)(干货)
- 2. 陷阱门(trap gate),类型TYPE=15:用于调用异常和中断的处理程序;
- 3. 中断门(interrupt gate),类型TYPE=14:用于调用异常和中断的处理程序;
- 4. 任务门(task gate),类型TYPE=5:用于任务切换;
1.2.2)调用门的功能:
- 1. 指定要访问的代码段;
- 2. 在指定代码段中定义程序的一个入口点;
- 3. 指定访问过程的调用者需要具备的特权级;
- 4. 若会发生堆栈切换, 它会指定在堆栈间需要复制的可选参数的个数;
5. 指明调用门描述符是否有效;
; 门的定义,紧跟在描述符定义之后
; 门 目标选择子,偏移,DCount, 属性
LABEL_CALL_GATE_TEST: Gate SelectorCodeDest, 0, 0, DA_386CGate+DA_DPL0 ;(8Ch + 00h)
1.3)通过调用门访问代码段
1.3.1) 当处理器访问调用门时, 它会使用调用门中的段选择符来定位目的代码段的段描述符。然后, CPU 会把代码段描述符的基地址与调用门中的偏移地址进行组合,形成代码段中指定程序入口点的线性地址(这里生成了线性地址,注意要和分页机制联系起来,如果开启分页机制的话)
SelectorCallGateTest equ LABEL_CALL_GATE_TEST - LABEL_GDT
LABEL_CALL_GATE_TEST: GateSelectorCodeDest, 0, 0, DA_386CGate+DA_DPL0 ;(8Ch + 00h)
SelectorCodeDest equ LABEL_DESC_CODE_DEST - LABEL_GDT
1.3.2)通过调用门进行程序控制转移时, CPU会对4种 不同特权级进行检查;
- 1. 当前特权级 CPL;(CS 和 SS 中的 bit1~0)
- 2. 调用门选择子中的请求特权级RPL;(选择子数据结构 bit1~0)
- 3. 调用门描述符中的描述符特权级 DPL;(段描述符数据结构,低4字节 bit6~5)
- 4. 目标代码段描述符的 DPL;(同上)
- 5. 目的代码段描述符中的一致性标志C 也要检查;(段描述符数据结构 的 bit10)
(Attention)访问调用门的条件:(干货)
为了访问调用门, 调用者程序的特权级CPL 必须小于或等于 调用门的 DPL, 而且门段选择子RPL 也必须小于或者等于调用门的DPL;
1.3.3)如何验证?-Validation(通过call 或 jmp 访问调用门的特权级检查)
V1)第一步,是检查调用者的特权级 与 调用门的特权级 是否符合特权级访问规则;
V2)如果第一步成功, 则CPU 才会把 调用者的 CPL 与 调用门目的代码段的DPL特权级进行比较检查:
- CALL指令):只有 CALL 指令 可以通过调用门把程序控制转移到特权级更高的非一致性代码段中,
- JMP指令): JMP指令 只能通过调用门吧控制转移到 DPL == CPL 的非一致性代码段中;
- CALL指令 + JMP指令): 这两条指令都可以把控制转移到 更高特权级的一致性代码段中,即转移到 DPL数值 小于或等于CPL的一致性代码段中;
1.3.4)转移之后,CPU都干了什么?
- 1)目标代码段是更高特权级的非一致性代码段:如果一个调用吧控制转移到更高特权级的非一致性代码段中, 那么CPL 就会设置 成 目的代码段中的DPL值, 堆栈也会发生切换;
- 2)目标代码段是更高特权级的一致性代码段:如果一个调用吧控制转移到更高特权级的一致性代码段中, 那么CPL 不会发生改变, 堆栈也不会发生切换;
1.4)调用门的作用
- 1.4.1) 调用门可以让一个代码段中的过程 被不同特权级的程序访问, 例如,位于一个代码段中的os 代码可能含有os 自身和 应用软件都允许访问的代码,故 可以为这些代码设置一个所有特权级代码都能访问的调用门;
- 1.4.2)另外,可以专门为仅用于os 的代码设置一些更高特权级的调用门;
【2】堆栈切换 + 从被调用过程返回
- (如“转移之后,CPU都干了什么?”模块中提到: 如果一个调用吧控制转移到更高特权级的非一致性代码段中, 那么CPL 就会设置 成 目的代码段中的DPL值, 堆栈也会发生切换)
2.1)堆栈切换的目的:
- 1)为了防止高特权级程序由于栈空间不足而引起崩溃;
- 2)同时也为了防止低特权级程序通过共享的堆栈有意或无意地干扰高特权级的程序;
2.2)堆栈切换带来的问题:
- 1)出现的问题: call 指令 执行前后的堆栈已经不再是同一个堆栈 了,那么我们在堆栈A中压入参数和返回地址, 需要出栈(ret or retf)时,堆栈却变成了堆栈B, 这该怎么办呢?
- 2)解决方法: Intel提供一种机制, 将堆栈A的内容复制到 堆栈B中, 如下图;
2.3)从被调用过程返回:
不同特权级间代码段的跳转{ 门 + 跳转(jmp + call) + 返回(ret) }的更多相关文章
- 高特权级代码段转向低特权级代码段(利用 ret(retf) 指令实现 jmp from ring0 to ring3)
[0]写在前面 0.1)本代码旨在演示 从 ring0 转移到 ring3(即,从高特权级 转移到 低特权级) 0.2)本文 只对 与 门相关的 代码进行简要注释,言简意赅: 0.3)文末的个人总结是 ...
- 关于一致/非一致代码段与TSS 关系的个人看法
[0]概念定义 0.1)一致代码段: 简单理解,就是操作系统拿出来被共享的代码段,可以被低特权级的用户直接调用访问的代码, 但是特权级高的程序不允许访问特权级低的数据. 通常这些共享代码,是" ...
- 十五个常用的jquery代码段【转】
好的文章顶一个 回到顶部按钮 通过使用 jQuery 中的 animate 和 scrollTop 方法,你无需插件便可创建一个简单地回到顶部动画: 1 // Back to top 2 $('a.t ...
- 十五个常用的jquery代码段
十五个常用的jquery代码段 回到顶部按钮 通过使用 jQuery 中的 animate 和 scrollTop 方法,你无需插件便可创建一个简单地回到顶部动画: 1 // Back to top ...
- VS.net中快捷键收缩和展开代码段
i. Ctrl-M-O 折叠所有方法 ii. Ctrl-M-P 展开所有方法并停止大纲显示(不可以再折叠了) iii. Ctrl-M-M 折叠或展开当前方法 iv. Ctrl-M-L展开所 ...
- 利用sublime的snippet功能快速创建代码段
在前端开发中我们经常会输入相同的一些基本代码,例如常用的jquery引用,bootstrap框架,cssreset等等,如果每次使用时在复制粘贴感觉很麻烦,这里介绍一种更为简洁的方法 利用sublim ...
- vim下使用YouCompleteMe实现代码提示、补全以及跳转设置
配置YouCompleteMe 1. 安装vundle vundle是一个管理vim插件的工具,使用vundle安装YouCompleteMe比较方便. 按照作者在https://github.com ...
- 超实用 JS 代码段笔记(一)
序1:30段简单代码段(有删减) 1 . 区分 IE 和 非 IE 浏览器 if(!+[1,]){ console.log('ie浏览器'); }else{ console.log('非ie浏览器') ...
- 数据段、代码段、堆栈段、BSS段的区别
进程(执行的程序)会占用一定数量的内存,它或是用来存放从磁盘载入的程序代码,或是存放取自用户输入的数据等等.不过进程对这些内存的管理方式因内存用 途 不一而不尽相同,有些内存是事先静态分配和统一回收的 ...
随机推荐
- 【Visual Studio】Visual Studio 2010 "LNK1123: 转换到 COFF 期间失败: 文件无效或损坏" 的解决方法
1.将 项目|项目属性|配置属性|连接器|清单文件|嵌入清单 “是”改为“否”. 2.找到 C:\Windows\winsxs\x86_netfx-cvtres_for_vc_and_vb_b03f5 ...
- linux下终端录制
主要是以下三步: 一.安装软件:curl -sL https://asciinema.org/install | sh 二.录制终端:asciinema rec filename 三.回放终端:asc ...
- 关于webpack的cdn配置
在webpack中活用cdn可以大幅度减少打包文件的体积,配置方法也非常简单. 首先现在入口html文件(index.html)里引入相应cdn连接,然后配置webpack: module.expor ...
- 爬虫学习笔记(三)requests模块使用
前面在说爬虫原理的时候说了,就写代码自动化的获取数据,保存下来数据,那怎么写代码来请求一个网址,获取结果呢,就得用requests模块了. 这篇博客说一下requests模块的使用,requests模 ...
- 参数化1--jmeter参数化数据(_csvread函数、用户自定义变量等)
以下是转载内容,仔细看过后,觉得用得最多的应该是csvread函数.用户自定义变量以及CSV DATA CONFIG控制器这几个,但是做练习之后,在结果树和聚合报告中怎么查看执行结果是个问题,没找到对 ...
- Delphi中@,^,#,$分别表示什么?
@:取址运算符; var int:integer; p:^integer; new(P); int:=24; p:=@int; dispose(P); ^:指针的引用解析操作符; var pint:^ ...
- 聊聊、Zookeeper 客户端 ZkClient
[ZkClient] ZkClient 是 GitHub 上一个开源的客户端,如果我们用 Maven 来管理工程,则引用如下. <dependency> <groupId>o ...
- Android Actionbar 添加返回按钮
setHomeButtonEnabled这个小于4.0版本的默认值为true的.但是在4.0及其以上是false,该方法的作用:决定左上角的图标是否可以点击.没有向左的小图标. true 图标可以点击 ...
- EasyMvc入门教程-基本控件说明(8)提醒导航
提醒导航顾名思义就是提醒大家注意某些文字了..请看下面的例子: 实现代码如下: @Html.Q().BlockRemind().Text("我可以作为提醒使用") 有的同学会说:这 ...
- IntelliJ IDEA启动加速
IntelliJ IDEA启动加速 学习了:https://blog.csdn.net/xiaosheng_papa/article/details/50378033 需要把插件都不加载就会快一些: ...