特权级之间的转换
发布日期:2021-06-30 12:11:00 浏览次数:3 分类:技术文章

本文共 3502 字,大约阅读时间需要 11 分钟。

一、特权级

       在IA32的分段机制下,特权级总共有4个特权级别,从高到低分别是0、1、2、3。数字越小表示的特权级越大。特权级如下图所示:

较为核心的代码和数据,将被存放在特权级较高的层级中。处理器将用这样的机制来避免低特权级的任务在不被允许的情况下访问位于高特权级的段。

二、一致代码段和 非一致代码段

      系统要安全,必须保证内核与用户程序分离开,内核要安全,必须不能被用户来打扰。但是有的时候,用户程序也是需要访问内核中的部分数据,那怎么办?

于是操作系统就将内核中的段分为共享的代码段和非共享的代码段两部分。

其中一致代码段就是操作系统拿出来被共享的代码段,可以被低特权级的用户直接访问的代码。
一致代码段的限制作用:
(1)特权级高的代码段不允许访问特权级低的代码段:即内核态不允许调用用户态下的代码。
(2)特权级低的代码段可以访问特权级高的代码段,但是当前的特权级不发生变化。即:用户态可以访问内核态的代码,但是用户态仍然是用户态。

非一致代码段:为了避免低特权级的访问而被操作系统保护起来的系统代码,也就是非共享代码。

非一致代码段的限制作用:
(1)只允许同特权级间访问
(2)绝对禁止不同级间访问,即:用户态不能访问内核态,内核态也不访问用户态。

三、CPL、DPL、RPL分别代表的含义,存储在什么位置,以及它们之间的关系

1、CPL(Current PrivilegeLevel)是当前执行的程序或任务的特权级。它被存储在CS和SS的第0位和第1位上。通常情况下,CPL等于代码的段的特权级。在遇到一致代码段时,一致代码段可以被相同或者更低特权级的代码访问。当处理器访问一个与CPL特权级不同的一致代码段时,CPL不会被改变。

2、DPL(DescriptorPrivilege Level):DPL表示段或者门的特权级,它被存储在段描述符或者门描述符的DPL字段中。当当前代码段试图访问一个段或者门时,DPL将会和CPL以及段或门选择子的RPL相比较,根据段或者门类型的不同,DPL将会被区别对待,下面介绍一下各种类型的段或者门的情况。

(1)数据段: CPL(RPL)<= DPL才可以访问
(2)非一致代码段(不使用调用门的情况下): CPL(RPL)=DPL才可以访问
(3)调用门:DPL规定了当前执行的程序或任务可以访问此调用门的最低特权级(这与数据段的规则是一致的)。
(4)一致代码段和通过调用门访问的非一致代码段:CPL(RPL)=> DPL

3、RPL(RequestedPrivilege Level):RPL是通过选择子的第0位和第1位表现出来的。处理器通过检查RPL和CPL来确认一个访问请求是否合法。

四、不同特权级数据段之间的访问规则
 

CPL(RPL)<=DPL 

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

     使用jmp或call指令可以实现下列4种转移

(1)目标操作数包含目标代码段的段选择子。
(2)目标操作数指向一个包含目标代码段                       选择子的调用门描述符。
(3)目标操作数指向一个包含目标代码段选择子的TSS。
(4)目标操作数指向一个任务门,这个任务门指向一个包含目标代码段选择子的TSS。
这4种方式可以看做是两大类,一类是通过jmp和call的直接转移(上述第一种),另一类是通过某个描述 
符的间接转移(上述第2,3,4种)。

1、通过jmp或call进行直接转移

 

2、通过调用门进行转移

(1)门描述符的结构

调用门描述符里面保存着目标代码段的段选择子,偏移量,以及属性。

(2)调用门的使用方式

假设我们想由代码A转移到代码B,运用一个调用门G,即调用门G中的目标选择子指向代码B的段。实际上,这个问题主要涉及这几个元素:CPL、RPL、代码B的DPL(记做DPL_B),调用门G的DPL(记做DPL_G)。

调用门使用时特权级检验的规则如下:

也就是说,通过调用门和call指令,可以实现从低特权级到高特权级的转移,无论目标代码段时一致的还是非一致的。

通过调用门和jmp指令,如果目标代码段是一致的,则可以实现从低特权级到高特权级的转移。如果目标代码段是非一致的,则只能实现相同特权级的转移。

 

六、代码段之间的转移对堆栈的影响

1、“长”跳转/调用 和 “短”跳转/调用

     如果一个调用或跳转指令时段间而不是段内进行的,那么我们称之为“长”的(Farjmp/call),反之,如果在段内则是“短”的(Near jmp/call)。

那么长的和短的jmp或call有什么分别呢?

对于jmp而言,仅仅是结果不同罢了,短跳转对应段内,长跳转对应段间。
对于call来说,就比较复杂一些,因为call指令是会影响堆栈的,长调用和短调用对堆栈的影响是不同的。

下面我们讨论短调用对堆栈的影响,call指令执行时下一条指令的eip压栈,到ret指令执行时,这个eip会被从堆栈中弹出,

如下图所示:

 

这是短调用的情况。

下面我们讨论长调用对堆栈的影响,call指令执行时会将调用者的cs和eip压栈,到ret指令执行时,这个eip和cs会被从堆栈中弹出,如下图所示:

2、有特权级变换的转移对堆栈的影响

     在不同特权级下的堆栈段不同,所以每一个任务最多可能在4个特权级间转移,所以,每个任务实际上需要4个堆栈。可是我们只有一个ss和一个esp,那么当发生堆栈切换,我们该从哪里获得其余堆栈的ss和esp呢?

1)当特权级3的程序在执行的时候,特权级3的堆栈的段选择符合堆栈指针都保存在SS和ESP中,发生堆栈切换的时候,会保存到被调用的堆栈中

2)特权级0、1和2的都保存在TSS段中,是只读的,CPU运行时并不会改变他们,当调用更高特权级时,CPU才会建立新堆栈,返回时该相应栈不存在,以后使用在创建

     

解决这个问题,需要一个数据结构TSS(Task-State Stack),如图:

当堆栈发生切换时,内层的ss和esp就是从这里取得的,比如,我们当前所在的是ring3,当转移至ring1时,堆栈将被自动切换到由ss1和esp1指定的位置。由于只是在由外层转移到内层(低特权级到高特权级)切换时新堆栈才会从TSS中取得,所以TSS中没有位于最外层的ring3的堆栈信息。

下面让我们来看看整个的转移过程是怎么样的?

执行call前后堆栈段的变化:

 

(1)根据目标代码段的DPL(新的CPL)从TSS中选择应该切换至哪个ss和esp

(2)从TSS中读取新的ss和esp。在这过程中如果发现ss、esp或者TSS界限错误都会导致无效TSS异常
(3)对ss描述符进行检验,如果发生错误,同样产生#TS异常
(4)暂时性地保存当前ss和esp的值
(5)加载新的ss和esp
(6)将刚刚保存起来的ss和esp的值压入新栈(保存在那个堆栈中呢)
(7)从调用者堆栈中将参数复制到被调用者堆栈(新堆栈)中,复制参数的数目由调用门中Param Count一项来决定。
(8)如果Param Count是零的话,将不会复制参数。
(9)将当前的cs和eip压栈
(10)加载调用门中指定的新的cs和eip,开始执行被调用者过程。

执行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指令来实现。????

  

用户进程如何访问内核代码呢(低特权级访问高特权级的)

答:可以通过call+调用门,如果直接使用call是不能跳转的

内核如何返回到用户进程

答:使用RET返回指令,当时不同特权级返回时,只允许从高特权级返回到低特权级

低特权级变为高特权级时,堆栈的切换

转载地址:https://jadeshu.blog.csdn.net/article/details/108459380 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:<3>Lua函数
下一篇:CPL、DPL和RPL

发表评论

最新留言

能坚持,总会有不一样的收获!
[***.219.124.196]2024年05月02日 09时29分18秒