热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

《X86汇编从实模式到保护模式》14.任务和特权级保护

文章目录DPLCPL特权指令RPL-请求特权级栈段特权级转移低特权返回高特权高特权转移到低特权门DPL对于数据段来说,决定了访问它们所应当具备的最低特权级。CPL


文章目录

    • DPL
    • CPL
    • 特权指令
    • RPL - 请求特权级
    • 栈段
    • 特权级转移
      • 低特权返回高特权
      • 高特权转移到低特权


DPL


  • 对于数据段来说,决定了访问它们所应当具备的最低特权级。

    CPL <&#61; DPL

    假设 DPL 为 2&#xff0c;则只有特权级为 0、1、2 的程序才可访问它。


CPL


  • 当前处理器正在执行的代码段&#xff0c;则该代码段的特权级称为当前特权级。
  • 位置&#xff1a;选择子位于 CS 寄存器中&#xff0c;最低两位就是当前特权级的值。

特权指令


  • 指令当前特权级 CPL 为 0 时才能执行的指令&#xff0c;称为特权级指令。

RPL - 请求特权级

无论是通过 jmp / call 指令&#xff0c;并且提供目标代码段的选择子&#xff0c;转移到其它代码段。还是访问内存数据&#xff0c;通过一些寄存器&#xff0c;且提供段选择子加载到段寄存器中。
这些都可以看做是一个请求&#xff0c;请求者提供一个段选择子&#xff0c;请求访问某个代码段&#xff0c;又或者是请求访问内存数据。
在大多数时候&#xff0c;请求者都是当前程序自己&#xff0c;因此 CPL &#61; RPL&#xff0c;要判断请求者是谁&#xff0c;最简单的方法就是看谁提供的选择子。

[SECTION A] ; 书上没有这个&#xff0c;放在这只是为了方便理解jmp dword 0x0010:flush ; 由当前 A 段提供的选择子&#xff0c;向 CS 请求 0x0010
[SECTION A]mov eax, 0x0008 ; 这里并不触发 RPL&#xff0c;只是单纯把值传入 EAXmov ds, eax ; 由当前 A 段提供的选择子&#xff0c;向 DS 请求 0x0008

当然&#xff0c;也有 CPL !&#61; RPL 的情况&#xff1a;
image-20220925215435457
特权级为 3 的应用程序想要访问磁盘&#xff0c;只能通过调用特权级为 0 的例程来实现&#xff0c;但必须通过调用门&#xff0c;因为特权级间的控制转移必须通过门。假设必须传入三个参数&#xff0c;分别为 CX 中保存的数据段选择子、EBX 寄存器中保存的段内偏移&#xff0c;以及 EAX 中的逻辑扇区号。
高特权级别的程序可以访问低特权级别的数据段。 因此操作系统会使用传入的参数 CX 中的数据段选择子&#xff0c;将其代入段寄存器&#xff1a;

mov ds, cx

在执行这条执行时&#xff0c;CX 中的段选择子&#xff0c;其中 RPL 字段值为 3&#xff0c;当前特权级 CPL 变为 0&#xff0c;因为通过 call 调用门可以改变当前特权级。
而请求者并非当前程序&#xff08;即特权级为 0 的例程&#xff09;&#xff0c;而是特权级为 3 的应用程序&#xff0c;RPL !&#61; CPL。
在这里插入图片描述

如图所示&#xff0c;假设应用程序已知操作系统数据段的选择子&#xff0c;希望通过这个选择子访问操作系统的数据段。这显然不可能&#xff0c;因为操作系统中的数据段 DPL 为 0&#xff0c;而应用程序的 CPL 为3&#xff0c;处理器会拒绝该访问。
但是&#xff0c;可以借助调用门。调用门工作在目标代码段的特权级上&#xff0c;一旦处理器的执行流离开应用程序&#xff0c;通过调用门进入操作系统例程是&#xff0c;当前特权级从 3 变为 0。当某个恶意程序将一个执行操作系统的数据段的选择子通过 CX 寄存器作为参数传入调用门时&#xff0c;因为当前特权级已经从 3 变为 0&#xff0c;因此可以从磁盘读写数据。

引入请求特权级&#xff08;RPL) 的原因是处理器在遇到一条将选择子传送到寄存器的指令时&#xff0c;无法区分真正的请求者是谁。
就拿上面的图来说&#xff0c;应用程序通过调用门调用内核例程&#xff0c;并且传入 CX 内容为指向内核数据段的选择子&#xff0c;内核例程中有这么一条语句 mov ds, cx。此时虽然我们可以人为分析出来&#xff0c;请求者是应用程序&#xff0c;但处理器并不知道请求者到底是应用程序还是内核例程。
引入 RPL 并不能解决这个问题&#xff0c;这只是处理器和操作系统之间的一种协议。

每当处理器执行一个将段选择子传送到段寄存器&#xff08;DS、ES、FS、GS&#xff09;的指令时&#xff0c;会检查以下两个条件是否都满足&#xff1a;


  • 当前特权级 CPL 高于或和数据段描述符的 DPL 相同。

    CPL <&#61; 数据段描述符的 DPL

  • 请求特权级 RPL 高于或和数据段描述符的 DPL 相同。

    RPL <&#61; 数据段描述符的 DPL

若以上两个条件不能同时成立&#xff0c;则处理器就会阻止这种操作&#xff0c;并引发异常。


引入 RPL 的意图是 “确保特权代码不会代替应用程序访问一个段&#xff0c;除非应用程序自己拥有那个段的权限”。



栈段

处理器要求&#xff0c;在任何时候&#xff0c;栈段的特权级别必须和当前特权级 CPL 相同。因此&#xff0c;要对段寄存器 SS 进行修改时&#xff0c;必须进行特权级检查。

mov ss, ax ; 修改 SS

在对段寄存器 SS 进行修改时&#xff0c;要求当前特权级 CPL 和请求特权级 RPL 必须等于目标栈段描述符的 DPL。即在数值上&#xff1a;

CPL &#61; 目标栈段描述符的 DPL
RPL &#61; 目标栈段描述符的 DPL

特权级转移

一般来说&#xff0c;控制转移只允许发生在两个特权级相同的代码段之间。


低特权返回高特权

第一种方法&#xff1a;将高特权级的代码段定义为依从的。
在段描述符中&#xff0c;有个 TYPE 字段&#xff0c;其中的 C 位&#xff0c;若 C &#61; 0&#xff0c;则该代码段只可被相同的特权级所调用。若 C &#61; 1&#xff0c;则该代码段为依从的代码段。

// 若要调用某个过程&#xff0c;则需要满足&#xff1a;
CPL >&#61; 目标代码段描述符的 DPL

若要调用的目标过程的 DPL 为 1&#xff0c;则该代码段只能被特权级分别为 1、2、3 的代码段所调用。


第二种方式&#xff1a;通过调用门。


高特权转移到低特权

不允许直接从高转移到低&#xff0c;只能通过 ref 或 iret 才能实现从高特权转移到低特权级。


门的类型&#xff1a;


  • 调用门&#xff1a;用于不同特权级之间的切换。
  • 中断门 / 陷阱门&#xff1a;作为中断过程使用。
  • 任务门&#xff1a;对应单个任务&#xff0c;用来执行任务切换。

通过调用门进行转移&#xff1a;jmp far 或 call far 指令&#xff0c;将调用门描述符的选择子作为操作数进行转移。


  • jmp far&#xff1a;可以将控制通过门转移到当前特权级高的代码段&#xff0c;但不改变当前特权级。
  • call far&#xff1a;可以将控制通过门转移到当前特权级高的代码段&#xff0c;同时将当前特权级提升到目标代码段的特权级别。

推荐阅读
author-avatar
姑获_626
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有