特权级概述

本文详细介绍了处理器的特权级概念,包括CPL、DPL和RPL,以及它们在段描述符和门描述符中的应用。特权级分为4个等级,数字越小权限越高。处理器通过比较这些值来确保合法的访问请求,防止非法访问高特权级资源。调用门和返回指令在不同特权级间转移时起关键作用,涉及堆栈切换和参数传递。文章还讨论了如何通过调用门实现不同特权级间的代码转移,并强调了保护机制的重要性。
摘要由CSDN通过智能技术生成
  • DPL所代表的Descriptor Privilege Level以及RPL所代表的Requested Privilege Level都是用来表示特权级别
  • 前面的例子都是运行在最高特权级下,涉及到的DPL和RPL都是0
  • 特权级总共有4个特权级别,从高到低分别是0、1、2、3。数字越小表示的特权级越大。
  • 较为核心的代码和数据,将被放在特权级较高的层级中。处理器将用这样的机制来避免低特权级的任务在不被允许的情况下访问位于高特权级的段。如果处理器检测到一个访问请求是不合法的,将会产生常规保护错误(#GP)。
  • 也将高特权级称做内层,而把低特权级称做外层。
    在这里插入图片描述

CPL、DPL、RPL

  • 处理器通过识别CPL、DPL、RPL 这三种特权级进行特权级检验
CPL
  • CPL 是当前执行的程序或任务的特权级
  • 储存在cs和ss的第0位和第1位上
  • 通常情况下,CPL等于代码所在的段的特权级。当程序转移到不同特权级的代码段时,处理器将改变CPL。
  • 在遇到一致代码段时,情况稍稍有点特殊,一致代码段可以被相同或者更低特权级的代码访问。当处理器访问一个与CPL特权级不同的一致代码段时,CPL不会被改变。
DPL
  • DPL表示段或者门的特权级
  • 它被存储在段描述符或者门描述符的DPL字段中
  • 当当前代码段试图访问一个段或者门时,DPL将会和CPL以及段或门选择子的RPL相比较,根据段或者门类型的不同,DPL将会被区别对待,下面介绍一下各种类型的段或者门的情况。
    数据段: DPL规定了可以访问此段的最低特权级。比如,一个数据段的DPL是1,那么只有运行在CPL为0或者1的程序才有权访问它。
    非一致代码段(不使用调用门的情况下): DPL规定访问此段的特权级。比如,一个非一致代码段的特权级为0,那么只有CPL为0的程序才可以访问它。
    调用门: DPL规定了当前执行的程序或任务可以访问此调用门的最低特权级(这与数据段的规则是一致的)。
    一致代码段和通过调用门访问的非一致代码段: DPL规定了访问此段的最高特权级。比如,一个一致代码段的DPL是2,那么CPL为0和1的程序将无法访问此段。
    TSS: DPL规定了可以访问此TSS的最低特权级(这与数据段的规则是一致的)。
RPL
  • RPL是通过段选择子的第0位和第1位表现出来的
  • 处理器通过检查RPL和CPL来确认一个访问请求是否合法。即便提出访问请求的段有足够的特权级,如果RPL不够也是不行的。也就是说,如果RPL 的数字比CPL大(数字越大特权级越低),那么RPL将会起决定性作用,反之亦然。
  • 操作系统过程往往用RPL来避免低特权级应用程序访问高特权级段内的数据

一个小试验

  • 特权级检验:只要CPL和RPL都小于被访问的数据段的DPL就可以了
  • 先将LABEL_DESC_DATA对应的段描述符的DPL修改为1:
    在这里插入图片描述
  • 编译运行和之前一样,说明我们对这个段的数据访问是合法的
  • 把数据段的选择子的RPL改为3
    在这里插入图片描述
    在这里插入图片描述
  • 系统崩溃了,因为违反了特权级的规则,用RPL = 3的选择子去访问DPL=1的段。

不同特权级代码段之间的转移

  • 程序从一个代码段转移到另一个代码段之前,目标代码段的选择子会被加载到cs中。作为加载过程的一部分,处理器将会检查描述符的界限、类型、特权级等内容。如果检验成功,cs将被加载,程序控制将转移到新的代码段中,从eip指示的位置开始执行。
  • 程序控制转移的发生,可以是由指令jmp、call、ret、sysenter、sysexit、int n或iret引起的,也可以由中断和异常机制引起。
    使用jmp或call指令可以实现下列4种转移:
  1. 目标操作数包含目标代码段的段选择子。
  2. 目标操作数指向一个包含目标代码段选择子的调用门描述符。
  3. 目标操作数指向一个包含目标代码段选择子的TSS。
  4. 目标操作数指向一个任务门,这个任务门指向一个包含目标代码段选择子的TSS。
    这4 种方式可以看做是两大类,一类是通过jmp和call的直接转移(上述第1种),另一类是通过某个描述符的间接转移(上述第2、3、4种)。

特权级转移

通过jmp或call进行直接转移
  • 如果目标是非一致代码段,要求CPL必须等于目标段的DPL,同时要求RPL小于等于DPL
  • 如果目标是一致代码段,则要求CPL大于或者等于目标段的DPL,RPL此时不做检查。当转移到一致代码段中后,CPL会被延续下来,而不会变成目标代码段的DPL
  • 如果想要进行不同特权级之间的转移,需要运用门描述符或者TSS
调用门初体验

在这里插入图片描述

  • 门也是一种描述符,定义了目标代码对应段的选择子、入口地址的偏移和一些属性等。程序正是通过这个地址进行转移的。
  • 门描述符分为4种:
    调用门(Call gates)
    中断门(Interrupt gates)
    陷阱门(Trap gates)
    任务门(Task gates)
  • 中断门和陷阱门是特殊的调用门
  • 调用门调用过程:
    在这里插入图片描述
  • 调用门最主要的用途是实现不同特权级的代码之间的转移
  • 假设我们想由代码A转移到代码B,运用一个调用门G,即调用门G中的目标选择子指向代码B的段。实际上,我们涉及了这么几个要素:CPL、RPL、代码B的DPL(记做DPL_B)、调用门G的DPL(记做DPL_G)。调用门使用时特权检验的规则如下:
    在这里插入图片描述
    综上所述:通过调用门和call指令,可以实现从低特权级到高特权级的转移,无论目标代码段是一致的还是非一致的

关于堆栈

  • 如果一个调用或跳转指令是在段间,称为"长";在段内,称为"短"
  • 对于jmp而言,仅仅是结果不同罢了,短跳转对应段内,而长跳转对应段间;而call则稍微复杂一些,因为call指令是会影响堆栈的,长调用和短调用对堆栈的影响是不同的。
  • 对于短调用来说,call指令执行时下一条指令的eip压栈;ret指令执行时,这个eip会被从堆栈中弹出
    在这里插入图片描述
  • 在函数foo调用最后一条指令ret(带有参数)返回之前和之后,堆栈的变化如图所示
    在这里插入图片描述
  • 长调用时的情况:是call指令执行时被压栈的就不仅有eip,还应该有cs
    在这里插入图片描述
    带参数的ret指令执行前后的情形如图所示
    在这里插入图片描述

通过调用门进行有特权级变换的转移

  • call 指令执行前后的堆栈已经不再是同一个。Intel 提供了一种机制,将堆栈A的内容复制到堆栈B中。
    有特权级变换的转移时堆栈变化
    在这里插入图片描述
  • 每一个任务最多都可能在4个特权级间转移,所以,每个任务实际上需要4个堆栈。可是,我们只有一个ss和一个esp,那么当发生堆栈切换。我们可以从TSS中获得其余堆栈的ss和esp(在最下面偏移4到偏移27的3个ss和3个esp,当发生堆栈切换时,内层的ss和esp就是从这里取得的)
    在这里插入图片描述
  • 由于只是在由外层到内层(低特权级到高特权级)切换时新堆栈才会从TSS中取得,所以TSS中没有位于最外层的ring3的堆栈信息。
  • 整个转移过程:
  1. 根据目标代码段的DPL(新的CPL)从TSS中选择应该切换至哪个ss和esp。
  2. 从TSS中读取新的ss和esp。在这过程中如果发现ss、esp或者TSS界限错误都会导致无效TSS异常(#TS)。
  3. 对ss描述符进行检验,如果发生错误,同样产生#TS 异常。
  4. 暂时性地保存当前ss和esp的值。
  5. 加载新的ss和esp。
  6. 将刚刚保存起来的ss和esp的值压入新栈。
  7. 从调用者堆栈中将参数复制到被调用者堆栈(新堆栈)中,复制参数的数目由调用门中ParamCount一项来决定。如果ParamCount是零的话,将不会复制参数。
  8. 将当前的cs和eip压栈。
  9. 加载调用门中指定的新的cs和eip,开始执行被调用者过程。
  • Param Count只有5位,也就是说,最多只能复制31个参数。如果参数多于31个该怎么办呢?这时可以让其中的某个参数变成指向一个数据结构的指针,或者通过保存在新堆栈里的ss和esp来访问旧堆栈中的参数
  • ret这个指令不仅可以实现短返回和长返回,而且可以实现带有特权级变换的长返回
  1. 检查保存的cs中的RPL以判断返回时是否要变换特权级。
  2. 加载被调用者堆栈上的cs和eip(此时会进行代码段描述符和选择子类型和特权级检验)。
  3. 如果ret指令含有参数,则增加esp的值以跳过参数,然后esp将指向被保存过的调用者ss和esp。注意,ret的参数必须对应调用门中的ParamCount 的值。
  4. 加载ss和esp,切换到调用者堆栈,被调用者的ss和esp被丢弃。在这里将会进行ss描述符、esp以及ss段描述符的检验。
  5. 如果ret指令含有参数,增加esp的值以跳过参数(此时已经在调用者堆栈中)。
  6. 检查ds、es、fs、gs的值,如果其中哪一个寄存器指向的段的DPL小于CPL(此规则不适用于一致代码段),那么一个空描述符会被加载到该寄存器。
    在这里插入图片描述
  • 使用调用门的过程实际上分为两个部分,一部分是从低特权级到高特权级,通过调用门和call指令来实现;另一部分则是从高特权级到低特权级,通过ret指令来实现

进入ring3

  • 在ret指令执行之前,堆栈中应该已经准备好了目标代码段的cs、eip,以及ss和esp,另外,还可能有参数。
    在这里插入图片描述
  • 代码:
    在这里插入图片描述

通过调用门进行有特权级变换的转移──实践篇

在这里插入图片描述

对保护二字的一点思考

  • 描述符中段基址、段界限和段属性都是对段的一种保护
  • 在涉及特权级的每一步中,处理器都会对CPL、RPL、DPL等内容进行比较,这种比较无疑是动态的,是在运行过程中进行的,是发生在多个因素之间的行为。相对而言,段描述符中的界限、属性等内容则是静态的,是对某一项内容的界定和约束。保护模式其实是通过这样动静相宜的方式去见证“保护”二字的含义。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值