• x86 --- 任务隔离特权级保护


    程序是记录在载体上的数据和指令。
    程序正在执行时的一个副本叫做任务

    所有段描述符都放在GDT --> 不做区分。
    内核程序(任务)所占段在GDT中,用户程序(任务)所占段在LDT中 --> 做区分。

    每个任务都有自己独立的LDT,也用来存放描述符。
    需要跟踪所有任务的LDT,也需要一个特定寄存器LDTR(类似GDTR),LDT不止一个,多任务系统中会有很多个任务在CPU上流转,而某一时刻只有一个任务在CPU上工作,这个任务被称为“当前任务” Current Task。LDTR就指向当前任务的LDT,每当任务切换,新的任务“上台“,LDTR就转而指向它。

    以前只有一个GDT,现在多了LDT,CPU该执行哪一个呢?取决于段选择子的TI位:
    TI = 1 从当前任务的LDT中加载描述符
    TI = 0 从GDT中加载描述符
    在这里插入图片描述

    以前整个机器上只有内核程序和一个用户程序的时候,没有任务切换的说法,任务不会中途被CPU切出,转而去执行另一个程序。现代多任务操作系统,在任务切出时上一个任务的”现场“需要保存,以便切回到它时CPU能够知道上一次执行它时执行到哪儿了,是个什么样的状态。核心需求就是”保存“这个动作。保存到哪儿?如何保存?这是亟待解决的问题。

    每个任务都有一个LDT,为了解决切换任务时保护现场的问题,TSS(Task State Segment)任务状态段,用来保存任务切换时的状态,切换时任务就像被静止了一样,所有状态,包括以下图中的信息,都需要保存在TSS中,在切换回来时再从TSS中读取恢复。
    在这里插入图片描述
    和LDT一样,TSS也需要一个特殊寄存器去指向它,也就是TR。

    多任务操作系统中,操作系统要肩负着任务的创建,以及在任务之间调度和切换的工作。不过,更为繁重和基础的工作是对处理器、设备及存储器的管理。对不同事件的响应,有些可以由任务来处理,有些只能被操作系统去处理,比如中断,内存分配,内存回收,设备访问,以及进程的排队与调度。

    在这里插入图片描述
    从程序员的角度来看,任务的全局空间包含了操作系统的段,是由别人编写的,但是他可以调用这些段的代码,或者获取这些段中的数据;任务局部空间的内容是由程序员自己创建的。通常,任务会在自己的局部空间运行,当它需要操作系统提供的服务时,转入全局空间执行。

    由于这个特性,一个任务既能在其自己的局部空间执行,也可以到全局空间去执行。这就导致它可以访问的空间更加大了。

    每个段描述符都对应着一个内存段。很显然,在一个任务的全局地址空间上,可以划分出2^13 个段,也就是8192 个段。因为GDT 的0 号描述符不能使用,故实际上是8191 个段,但这无关紧要。又因为段内偏移是32 位的,段的长度最大的4GB,因此,一个任务的全局地址空间,
    其总大小为2^13 × 2 ^32 =245 字节,即32TB。同样的道理,局部描述符表LDT 可以定义2 ^13 个,也就是8192 个描述符,每个段的最大长度也是4GB,故,一个任务的局部地址空间为2 ^13×2 ^32 =245 字节,同样是32TB。这样一来,每个任务的总地址空间为2 ^45 +2 ^45 =2 ^45 ×2=2 ^45 ×21 = 2 ^46 字节,即64TB。

    特权级保护

    特权级(Privilege Level)
    存在于描述符 以及 其选择子中的一个数值。
    Intel 处理器可以识别4 个特权级别,分别是0 到3,较大的数值意味着较低的特权级别,反之亦然。如图所示,这是Intel 处理器所提供的4 级环状保护结构。
    在这里插入图片描述
    在上面描述符选择子的图片中最低两位是RPL,request privilege level
    在下面这张段描述符的图片中高32位14 13位是DPL。descriptor privilege level
    2个bit位的表示范围是:0,1,2,3
    数值越低 级别越高
    在这里插入图片描述
    DPL 决定了访问其所在描述符所应当具备的最低特权级别。

    当处理器正在一个代码段中取指令和执行指令时,那个代码段的特权级叫做当前特权级(Current Privilege Level,CPL)

    一般情况下:

    范围特权级
    操作系统0
    驱动程序1 or 2
    用户程序3

    一般来说,操作系统是最先从BIOS 那里接收处理器控制权的,进入保护模式的工作也是由它做的,而且,最重要的是,它还肩负着整个计算机系统的管理工作,所以,它必须工作在0 特权级别上,当操作系统的代码正在执行时,当前特权级CPL 就是0

    当任务在局部空间运行时(CPL = 3),想要访问硬件资源,这个时候就需要操作系统介入(操作系统拥有最高级别)(CPL = 0)

    一些权力至上,比如停机指令hlt 和对控制寄存器CR0 的写操作,像这样的指令只能由最高特权级别的程序来做。因此,那些只有在当前特权级CPL 为0 时才能执行的指令,称为特权指令(Privileged Instructions)型的特权指令包括加载全局描述符表的指令lgdt(它在实模式下也可执行)、加载局部描述符表的指令lldt、加载任务寄存器的指令ltr、读写控制寄存器的mov 指令、停机指令hlt 等十几条。

    想要从一个特权级的代码段,跳转到另一个特权级的代码段,需要遵循一些原则和方法

    1. 一般来说控制转移只允许发生在两个特权级相同的代码段之间。
    2. 低特权级要跳转到高特权级有两种办法:高特权级代码设置为依从 或是 使用 门描述符

    依从

    简单来说就是让一个高特权级的段,设置为服从于当前正在执行的低特权级的段。当低特权级的段
    必须满足 CPL>= 目标代码段描述符的DPL 就可以让控制转移到依从的代码段上执行,但是执行时并不使用依从代码段的DPL,而是使用调用放的特权级

    门(Gate)是另一种形式的描述符,称为门描述符,简称门。
    和段描述符不同,段描述符用于描述内存段,门描述符则用于描述可执行的代码,比如一段程序、一个过程(例程)或者一个任务
    门描述符 描述了 目标过程(例程)所在代码段的选择子,以及段内偏移

    不同门的作用分类:

    门类型作用
    不同特权级之间切换调用门
    中断处理过程中断门/陷阱门
    任务切换任务门

    要想通过调用门进行控制转移,可以使用jmp far 或者call far 指令,并把调用门描述符的选择子作为操作数。

    使用jmp far 或者 call far指令 来调用门进行转移 参数为门描述符所在的选择子。

    不同之处:

    jmp farcall far
    特权级变化不改变CPLCPL被赋值为目标代码段DPL

    不允许从特权级高的代码段将控制转移到特权级低的代码段,因为操作系统不会引用可靠性比自己低的代码。

    不管是实施控制转移(jmp和call)还是访问数据段,都可以看作一个请求。请求者通过提供一个段选择子来请求访问指定段。此处的请求使用的就是RPL请求者特权级别

    在绝大多数时候,请求者都是当前程序自己(程序自己提供了选择子),因此,CPL=RPL。
    判断请求者是谁最简单的方法是看谁提供了选择子

    有些情况下CPL != RPL特权级为3 的应用程序希望从硬盘读一个扇区,并传送到自己的数据段,因此,数据段描述符的DPL 同样会是3。由于I/O 特权级的限制,应用程序无法自己访问硬盘。好在位于0 特权级的操作系统提供了相应的例程,但必须通过调用门才能使用,因为特权级间的控制转移必须通过门。

    假设,通过调用门使用操作系统例程时,必须传入3 个参数,分别是CX 寄存器中的数据段选择子、EBX 寄存器中的段内偏移,以及EAX 中的逻辑扇区号。

    高特权级别的程序可以访问低特权级别的数据段,这是没有问题的。因此,操作系统例程会用传入的数据段选择子代入段寄存器,以便代替应用程序访问那个段:mov ds,cx

    在这里插入图片描述

    RPL的必要性

    在这里插入图片描述
    虚线表示应用程序即使知道了内核数据段的选择子的值,也无法直接访问DPL=0的内核数据段。
    但是恶意软件可以通过借助调用门。
    调用门工作在目标代码段的特权级上,通过调用门进入操作系统例程时特权级从 3 --> 0。
    此时如果应用程序想要操控内核程序的DPL=0的数据段,他可以这样做:
    向调用门传递CX寄存器,该寄存器可以保存一个内核数据段的选择子。调用门跳转到内核例程后,就可以通过这个选择子操作DPL=0的内核数据段了。因为此时特权级时0,可以从硬盘读取数据,也可以访问操作系统的数据段。

    现在引入RPL看看
    我们前面说过,想要分清RPL的值是多少,很简单,只要看谁的带着选择子来请求的。
    这相当于标志了请求者的身份,上面没有RPL的情况下调用门可以改变CPL的值,也就是说在CPU上工作的身份改变了。
    RPL的则是另一个表示身份的标志。也就是说现在光是CPL达标了还不行,还得检查你RPL是否达标了。

    当前特权级CPL 高于或者和数据段描述符的DPL 相同。即,在数值上,CPL≤数据段描述符的DPL;
    请求特权级RPL 高于或者和数据段描述符的DPL 相同。即,在数值上,RPL≤数据段描述符的DPL。
    如果以上两个条件不能同时成立,处理器就会阻止这种操作,并引发异常中断

    这里解释不清楚,直接放原文:

    引入请求特权级(RPL)的原因是处理器在遇到一条将选择子传送 到段寄存器的指令时,无法区分真正的请求者是谁。但是,引入RPL 本
    身并不能完全解决这个问题,这只是处理器和操作系统之间的一种协 议,处理器负责检查请求特权级RPL,判断它是否有权访问,但前提是
    提供了正确的RPL;内核或者操作系统负责鉴别请求者的身份,并有义 务保证RPL 的值和它的请求者身份相符,因为这是处理器无能为力的。
    因此,在引入RPL 这件事上,处理器的潜台词是,仅依靠现有的 CPL 和DPL,无法解决由请求者不同而带来的安全隐患。那么,好吧,
    再增加一道门卫,但前提是,操作系统只将通行证发放给正确的人。

    之前的章节里,内核程序和用户程序都运行在0特权级,是普通的段间控制转移
    在用户程序内直接调用内核例程,不会有问题。

    现在用户程序的段描述符中,DPL应该是3。内核程序的段描述符中,DPL应该是0。
    由于处理器禁止将控制从特权级低的程序转移到特权级高的程序,因此,如果还像以前那样直接调用内核例程,百分之百不会成功,一定会引发处理器异常中断。
    为了给出高特权级到低特权级这一条路,调用门也是需要实现的。

    调用门(Call-Gate)用于在不同特权级的程序之间进行控制转移。本质上,它只是一个描述符,一个不同于代码段和数据段的描述符,可以安装在GDT 或者LDT 中。该描述符的格式如图14-9 所示,下面是低32 位,上面是高32 位。
    在这里插入图片描述
    再对照之前画的图来看:
    描述符中的TYPE 字段用于标识门的类型,共4 比特,值“1100”表示调用门
    在这里插入图片描述

  • 相关阅读:
    多因子组合优化:指数增强策略(附源码入口)
    排序算法··
    1700*D. Flowers(DP&&前缀和&&预处理打表)
    【leetcode】传递信息 c++
    一个基于.NET Core开源、跨平台的仓储管理系统
    Mybatis单表查询
    java计算机毕业设计高校就业宣讲会系统源码+mysql数据库+系统+lw文档+部署
    一文整理深度学习【深度学习linux的docker+pytorch+cuda+nvidia-docker+vscode配置】
    Windows 服务器中使用 mysqldump 命令导出数据,解决中文乱码问题
    C++对象模型(17)-- 构造函数语义学:成员初始化列表
  • 原文地址:https://blog.csdn.net/weixin_43604927/article/details/127577085