手写简易操作系统(八)--特权级以及TSS

前情提要

我们在这里梳理一下上面几节讲的内容

首先是计算机开机,BIOS接过第一棒,将第一个扇区MBR的内容导入到内存 0x7c00 的位置。

然后就是MBR中我们自己写的内容,将Loader导入到 0x600 的地址,Loader设置了GDT,打开了保护模式,并且开启了内存分页。最后将内核载入到内存的 0xc0001500 的位置,也就是物理内存 0x1500 的位置。

这一节没有代码,我们讲一下特权级的问题。

一、特权级

特权级按照权力从大到小分为0、1、2、3级,数字越小,权力越大,然而实际上Linux中只是用了两个特权级,其中0特权级为内核特权级,3特权级为用户特权级。

为何要设立特权级?为了不让用户程序直接操作硬件,用户程序在操作硬件或者一些危险操作时只能通过操作系统去执行。

二、TSS

TSS,即Task State Segment,意为任务状态段,它是处理器在硬件上原生支持多任务的一种实现方式。

其结构如下

image-20240314155554711

TSS是每个任务都有的结构,它用于一个任务的标识,相当于任务的身份证,程序拥有此结构才能运行,这是处理器硬件上用于任务管理的系统结构,处理器能够识别其中每一个字段。

104字节是TSS的最小尺寸,根据需要,后面IO位图的大小不定。目前只关注28字节之下的部分,这里包括了3个栈指针。

任务是由处理器执行的,任务在特权级变换时,本质上是处理器的当前特权级在变换,由一个特权级变成了另外一个特权级。这就开始涉及栈的问题了,处理器固定,处理器在不同特权级下,应该用不同特权级的栈,所以TSS结构中有三个不同的栈指针,分别对应0,1,2特权级。哎????不对啊,不是四个特权级嘛?先别急

特权级在变换时,需要用到不同特权级下的栈,当处理器进入不同的特权级时,它自动在TSS中找同特权级的栈,你懂的,TSS是处理器硬件原生的系统级数据结构,处理器当然知道TSS中哪些字段是目标栈的选择子及偏移量。

特权级转移分为两类

  • 特权级由低到高,通过中断门或者调用门实现
  • 特权级由高到低,通过调用返回指令实现

对于第一种,由于不知道目标特权级对应的栈地址在哪里,所以要提前把目标栈的地址记录在某个地方,当处理器向高特权级转移时再从中取出来加载到SS和ESP中以更新栈。处理器会自动地从TSS中找到对应的高特权级栈地址,这一点不需要写程序赋值。

对于第二种,当中断发生时,处理器会自动将当前特权级别的栈指针(SS:ESP)保存到中断堆栈(中断栈)中,并切换到特权级0(内核态)来执行中断服务程序。这个过程中,处理器会自动在中断堆栈顶部保存被中断前的 SS 和 ESP 的值。

也就意味着没有哪个特权级的程序会进入3特权级,那么用户程序也就不需要保存3特权级的栈了。

三、CPL、DPL、RPL

x86访问内存的机制是“段基址:偏移地址”,无论是实模式,还是保护模式,都要遵循此方式。保护模式下,段寄存器中的不再是段基址,而是段选择子,通过该选择子从GDT或LDT中找到相应的段描述符,从该描述符中获取段的起始地址。

选择子的0~1位就是RPL字段,它就是请求特权级。即RPL

计算机中,谁是访问者,谁要去访问计算机资源,代码!所以,就用代码段寄存器CS中选择子的RPL位表示代码请求别人资源能力的等级,那么CS寄存器中选择子低2位的值不仅称为请求特权级,又称为处理器的当前特权级,也就是说处理器的当前特权级是CS.RPL。即CPL

在段描述符中有一个属性还为该内存标明了特权等级,这就是段描述符中的DPL,即描述符特权级

3.1、处理器当前特权级为什么会变化

当前正在运行的代码所在的代码段的特权级DPL就是处理器的当前特权级,当处理器从一个特权级的代码段转移到另一个特权级的代码段上执行时,由于两个代码段的特权级不一样,处理器当前的特权身份起了变化,这就是当前特权级CPL改变的原因。

其实就是使用了那些能够改变程序执行流的指令,如int、call等,这样就使CS和EIP的值改变,从而使处理器执行到了不同特权级的代码。

不过特权转移并不是随便转移的,那么设置这个特权级的意义就没有了,处理器要检查特权变换的条件。当处理器特权级检查的条件通过后,新代码段的DPL就变成了处理器的CPL,也就是目标代码段描述符的DPL将保存在代码段寄存器CS中的RPL位。

3.2、第一个处理器当前特权级怎么来的

我们的代码中,再打开保护模式前,CS寄存器的值一直为0。在打开了保护模式后,进行了一次远跳程序刷新流水线,其段选择子为SELECTOR_ CODE,这个选择子的特权级为0,也就是说一进入保护模式,我们的当前特权级就是0了。远跳时,由于是从0到0,才能成功。

3.3、受访者是谁

DPL是段描述符所代表的内存区域的“门槛”权限,访问者能否迈过此门槛访问到本描述符所代表的资源,其特权级至少要等于这个门槛,访问者特权能否大于该门槛?这要看受访资源是代码,还是数据。

所以访问者是代码,受访者是资源,门槛是段描述符

3.4、不涉及RPL受访者为数据段

数据段段描述符中type字段中未有X可执行属性

只有访问者的权限大于等于该DPL表示的最低权限才能够继续访问,否则连这个门槛都迈不过去。

3.5、不涉及RPL受访者为代码段

代码段段描述符中type字段中含有X可执行属性

只有访问者的权限等于该DPL表示的最低权限才能够继续访问,即只能平级访问。

对于受访者为代码段一这说法,实际上是指处理器从当前运行的代码段上转移到受访者这个目标代码段上去执行,并不是说把该目标代码段当数据一样访问。为什么呢?

因为高特权级的代码什么都可以干,没必要降低特权级完成一件事,所以高特权级代码不会降到低特权级,低特权级代码又不能访问高特权级,这就导致只能在平级之间转换。

但凡事皆有例外,处理器从中断处理程序中返回到用户态的时候实现了从高特权降到低特权。

中断处理都是在0特权级下进行的,因为中断的发生多半是外部硬件发生了某种状况或发生了某种不可抗力事件而必须要通知CPU导致的,所以,在中断的处理过程中需要具备访问硬件的能力,在大多数情况下只有CPU处于0特权级才能访问硬件,这是因为eflags寄存器中的IOPL位的值通常被设置为0(该位的作用就是限制访问IO端口的最低特权级),并且TSS中不存在 IO位图,有关这部分后面马上会讲到。再者,有些中断处理中需要的指令只能在0特权级下使用,这部分指令称为特权指令,所以中断发生后其处理的过程必须在0特权级下进行。用户进程是在3特权级,在运行用户程序时若发生了中断,CPU会暂停用户程序的执行,随后CPU就会自动由3特权级进入到0特权级,在0特权级下将执行用户程序时的现场环境(也就是著名的概念:上下文)保存起来(这个保存上下文的动作可以由CPU通过TSS完成,这是CPU在硬件上提供的功能,但其效率并不高,所以大多数操作系统都是自己写代码手动保存上下文环境),待中断处理完成后,CPU会恢复用户程序的执行,也就是说会回到3特权级。

3.6、如何实现特权级转移

如果如上面所说,代码段只能平级访问,那初始进入的是哪个特权级,后面就只能在这个特权级了嘛?这一想也不对,要实现特权级转移有两种方式,一种是使用一致性代码段

一致性代码段也称为依从代码段,Conforming,用来实现从低特权级的代码向高特权级的代码转移。一致性代码段是指如果自己是转移后的目标段,自己的特权级(DPL)一定要大于等于转移前的CPL,即数值上CPL≥DPL,也就是一致性代码段的DPL是权限的上限,任何在此权限之下的特权级都可以转到此代码段上执行。代码段可以有一致性和非一致性之分,但所有的数据段总是非一致的,即数据段不允许被比本数据段特权级更低的代码段访问。

但是一般不用这个,linux中使用了中断,除了中断还有3个门,都可以实现特权级的转移。

四、门

门结构是什么呢?就是记录一段程序起始地址的描述符。

任务门

image-20240314164740718

中断门

image-20240314164754266

陷阱门

image-20240314164807809

调用门

image-20240314164819173

除了任务门外,其他三种门都是对应到一段例程,即对应一段函数,而不是像段描述符对应的是一片内存区域。任何程序都属于某个内存段,所以程序确切的地址必须用“代码段选择子+段内偏移量”来描述,可见,门描述符基于段描述符,例程是用段描述符来给出基址的,所以门描述符中要给出代码段的选择子,但光给出基址远远不够,还必须给出例程的偏移量,这就是门描述符中记录的是选择子和偏移量的原因。

任务门描述符可以放在GDT、LDT和IDT(中断描述符表),调用门可以位于GDT、LDT中,中断门和陷阱门仅位于IDT中

任务门、调用门都可以用call和jmp指令直接调用,原因是这两个门描述符都位于描述符表中,要么是GDT,要么是LDT,访问它们同普通的段描述符是一样的。陷阱门和中断门只存在于IDT中,因此不能主动调用,只能由中断信号来触发调用。

4.1、为何提供了四种门

提供了4种门的原因是它们都有各自的应用环境,但它们都用来实现从低特权级的代码段转向高特权级的代码段。

1.调用门call和jmp指令后接调用门选择子为参数,以调用函数例程的形式实现从低特权向高特权转移,可用来实现系统调用。call指令使用调用门可以实现向高特权代码转移,jmp指令使用调用门只能实现向平级代码转移。

2.中断门以int指令主动发中断的形式实现从低特权向高特权转移,Linux系统调用便用此中断门实现。

3.陷阱门以int3指令主动发中断的形式实现从低特权向高特权转移,这一般是编译器在调试时用。

4.任务门任务以任务状态段TSS为单位,用来实现任务切换,它可以借助中断指令发起。当中断发生时,如果对应的中断向量号是任务门,则会发起任务切换。也可以像调用门那样,用call或jmp指令后接任务门的选择子或任务TSS的选择子。

4.2、我们用到了哪个

中断门,我们仿照Linux使用中断门进行设计,任务门切换任务开销过大。调用门实现复杂且需要自己做特权级的转换。陷阱门只用作调试。

五、逻辑上的漏洞

现在看来好像没什么破绽,通过CPL与DPL保证用户程序可以跳到特权级0,且可以返回特权级3,实现了特权级之间的转换。很和谐。但是要有人搞破坏怎么办,不管受访者的DPL是多少,如果特权检查仅仅靠CPL和DPL这两项的话,数值上CPL≤DPL,这个时候处理器可以访问并获得任何资源。

举个例子,调用门A可以帮助用户程序把硬盘某个扇区的数据写入到用户指定的内存缓冲区中,如果指向的缓冲区是内核的内存地址怎么办。这个时候用户程序通过调用门A把权提到了0,直接把读来的硬盘内容覆盖了内核,电脑gg

发生这种事情的主要原因在于受访问者不知道真正请求资源的是谁,在上面的例子中,真正的资源请求者的特权级为3,但是CPU无法知道,只知道CPL为0,那么什么都可以干,干就完了。

请求特权级RPL完美地解决了这个问题,它代表真正请求者的特权级,在上面的例子中就是3

以后在请求某特权级为DPL级别的资源时,参与特权检查的不只是CPL,还要加上RPL,CPL和RPL的特权必须同时大于等于受访者的特权DPL。

用户程序的CPL是不会骗人的,不可能伪造,它起始是由操作系统在加载用户程序时赋予的,记录在段寄存器CS中的低2位,就是RPL的位置,而CS寄存器只能通过call、jmp、ret、int、sysenter等指令修改,即使改的话,用户程序也只能在3级特权下折腾,只要用户进程不请求操作系统服务,它的CPL是不会变的,当它申请了系统服务,如果提交了选择子作为参数,选择子中的RPL也会被操作系统修改为用户进程的CPL。所以,即使用户程序提交了个伪造的选择子也没用,其RPL会被操作系统用其CPL替换,还其“真身”。

除了加载用户程序时,在其他时段的 CPL是由目标代码段的DPL变成的,即切换到新特权代码段后,新代码段的DPL被存储到段寄存器CS中的低2位,就是RPL的位置。其实这再合理不过了,CPU是切换到新的特权级代码段上运行了,身份变了,当然要用新代码段的DPL做CPL。

受访者若为数据,特权级检查会发生在往数据段寄存器中加载段选择子的时候,数据段寄存器包括DS和附加段寄存器ES、FS、GS

举个例子,mov ds,ax时便会触发特权级检查。ax中的值被当作选择子,处理器会拿ax中的低2位,即RPL和CPL分别与ax中选择子所指向的段描述符的DPL做比较,如果满足RPL≤DPL && CPL≤DPL,选择子才能被加载到DS中。

操作系统提供了一致性代码段和门结构来实现从低特权级到高特权级的代码段转移,这会给恶意攻击者用低特权级的程序访问高特权级资源,使攻击者有诸如篡改内核之类的危险操作的机会,因此必须在CPL和DPL的基础上增加条件。

访问者穿越特权屏障是因为操作系统允许通过特定方式实现从低特权级代码段到高特权级代码段的转移,即通过高特权级代码段来间接获得自身无法拥有的权限。由于真正的资源请求者是低特权级代码段,因此需要标识出资源请求者的真实身份,这就是请求特权级(Request Privilege Level,RPL)的作用。

RPL代表了真正资源请求者的特权级,因此在请求特权级为DPL的资源时,需要检查的不仅是CPL,还要加上RPL,CPL和RPL的特权级必须同时大于等于受访者的DPL。

RPL引入的目的是避免低特权级的程序访问高特权级的资源,有了RPL后,访问内存段的特权检查规则如下(不通过调用门):

  • 如果目标为非一致性代码段,要求数值上:CPL=RPL=目标代码段DPL

  • 如果目标为一致性代码段,要求数值上:CPL≤目标代码段DPL && RPL≥目标代码段DPL

  • 如果目标为数据段时,要求数值上:CPL≤目标数据段DPL && RPL ≤ 目标数据段DPL

栈段的特权级检查比较特殊,因为在各个特权级下处理器都有对应的栈,所以往段寄存器中赋予选择子时,要求CPL等于栈段选择子对应的数据段的DPL,即数值上CPL=RPL=用作栈的目标数据段DPL。

六、IO特权级

IO读写特权是由标志寄存器eflags中的IOPL位和TSS中的IO位图决定的,它们用来指定执行IO操作的最小特权级。IO相关的指令只有在当前特权级大于等于IOPL时才能执行,所以它们称为IO敏感指令(I/O Sensitive Instruction),如果当前特权级小于IOPL时执行这些指令会引发处理器异常。这类指令有in、out、cli、sti。

在eflags寄存器中第12~13位便是IOPL(I/O Privilege Level),即IO特权级,它除了限制当前任务进行IO敏感指令的最低特权级外,还用来决定任务是否允许操作所有的IO端口,对,没错,是全部IO端口,IOPL位是打开所有IO端口的开关(用来单独设置端口访问的方式是IO位图,一会儿介绍)。每个任务(内核进程或用户进程)都有自己的eflags寄存器,所以每个任务都有自己的IOPL,它表示当前任务要想执行全部IO指令的最低特权级,也就是处理器最低的CPL,只有任务的当前特权级大于等于IOPL才允许执行全部IO指令,即数值上CPL≤IOPL;

CPL为0时处理器是法力无边的,所以0特权级下处理器是不受IO限制的。

IOPL是所有IO端口的开关,不过,这个开关还留有余地,如果将开关打开,便可以访问全部65536个端口,如果开关被关上,即数值上CPL > IOPL,则可以通过IO位图来设置部分端口的访问权限。也就是说,先在整体上关闭,再从局部上打开。这有点像设置防火墙的规则,先默认为全部禁止访问,想放行哪些端口再单独打开。

65536个端口号,正好占用8KB。

所以TSS结构变成了这个样子

image-20240314193915164

位图的结尾必须是0xFF,但是位图可以满8KB。

第一,处理器允许I/O位图中不映射所有的端口,即I/O位图长度可以不足8KB,但位图的最后一字节必须为0xFF。如果在位图范围外的端口,处理器一律默认禁止访问。这样一来,如果位图最后一字节的0xFF属于全部65536个端口范围之内,字节各位全为1表示禁止访问此字节代表的全部端口,这并没什么过错。

第二,如果该字节已经超过了全部端口的范围,它并不用来映射端口,只是用来作为位图的边界标记,用于跨位图最后一个字节时的“余量字节”。避免越界访问TSS外的内存。

结束语

这一节讲了很多理论的东西,希望大家没有瞌睡,哈哈哈

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/457330.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

智能工厂核心功能系统-MES生产管理系统

MES在未来智能制造中扮演着至关重要的角色,通过其在生产管理中的应用,将帮助企业实现智能化转型,提升生产效率和产品质量,推动整个制造业向着更加智能、高效、可持续的方向发展。 通过对MES在未来智能制造发展趋势中的地位进行深…

ip广播智慧工地广播喊话号角 IP网络号角在塔吊中应用 通过寻呼话筒预案广播

ip广播智慧工地广播喊话号角 IP网络号角在塔吊中应用 通过寻呼话筒预案广播 SV-704XT是深圳锐科达电子有限公司的一款壁挂式网络有源号角,具有10/100M以太网接口,可将网络音源通过自带的功放和号角喇叭输出播放,可达到功率50W。SV-704XT内置有…

如何配置Apache的反向代理

目录 前言 一、反向代理的工作原理 二、Apache反向代理的配置 1. 安装Apache和相关模块 2. 配置反向代理规则 3. 重启Apache服务器 三、常见的使用案例 1. 负载均衡 2. 缓存 3. SSL加密 总结 前言 随着Web应用程序的不断发展和扩展,需要处理大量的请求和…

企业架构设计方法与实践中的架构治理演进、架构评估方法、架构成熟度模型

企业架构设计方法与实践中的架构治理演进、架构评估方法、架构成熟度模型。 架构治理演进: 架构治理是指通过设立和执行一套政策和程序,来管理和控制一个组织的架构活动。架构治理演进是一个持续的过程,需要根据组织的实际情况进行定期审查和调整。 在演进过程中,重点需要…

Spring Boot 集成 WebSocket 实例 | 前端持续打印远程日志文件更新内容(模拟 tail 命令)

这个是我在 CSDN 的第一百篇原则博文,留念😎 #1 需求说明 先说下项目结构,后端基于 Spring Boot 3,前端为 node.js 开发的控制台程序。现在希望能够在前端模拟 tail 命令,持续输出后端的日志文件。 #2 技术方案 #2.…

应用案例 | 基于三维机器视觉的自动化码垛解决方案

Part.1 行业背景 通过显扬科技三维机器视觉设备,搭配三维视觉分析系统,实现码垛的智能化升级,早期使用机器人码垛采用机械臂和简单的控制系统,码垛能力和效率较低。随着现代工业的发展,机器人码垛也开始采用先进的传感…

Flink通讯模型—Akka与Actor模型

Carl Hewitt 在1973年对Actor模型进行了如下定义:"Actor模型是一个把Actor作为并发计算的通用原语". Actor是异步驱动,可以并行和分布式部署及运行的最小颗粒。也就是说,它可以被分配,分布,调度到不同的CPU&…

金蝶BI方案能解决云星空数据分析痛点吗?

金蝶云星空作为一个主攻企业管理流程的软件确实立下了汗马功劳,但一到数据分析方面那就阻碍重重了。直接的感受是分析步骤多且复杂,数据展现不够直观易懂,有些分析指标的计算真的很难实现,跨部门跨组织计算指标、合并账套什么的能…

音频读取之wave和liborsa

wave 常见的语音信号处理python库有librosa, scipy, soundfile等等。wave库是python的标准库,对于python来说相对底层,wave不支持压缩/解压,但支持单声道/立体声语音的读取。 读取音频 import wave #导入库file_path D:/ba.wav #文件路径…

微服务分布式基于Springcloud的拍卖管理系统597wx

越来越多的用户利用互联网获得信息,但各种信息鱼龙混杂,信息真假难以辨别。为了方便用户更好的获得信息,因此,设计一种安全高效的拍卖管理系统极为重要。 为设计一个安全便捷,并且使用户更好获取拍卖管理系统&#xff…

数据结构:链式二叉树

对于二叉树而言,如果不是完全二叉树,就不再适合用数组存储了 二叉树的遍历 顺序 访问顺序(n NULL) 1.前序 根,左子树,右子树 1 2 3 n n n 4 5 n n 6 n n 2.中序 左子树,根,右子树 n 3 n 2 n 1 n 5 n 4 n 6 n 3.后…

【算法与数据结构】深入解析二叉树(一)

文章目录 📝数概念及结构🌠 树的概念🌉树的表示🌠 树在实际中的运用(表示文件系统的目录树结构) 🌉二叉树概念及结构🌠概念🌉数据结构中的二叉树🌠特殊的二叉…

深入浅出:Objective-C中使用MWFeedParser下载豆瓣RSS

摘要 本文旨在介绍如何在Objective-C中使用MWFeedParser库下载豆瓣RSS内容,同时展示如何通过爬虫代理IP技术和多线程提高爬虫的效率和安全性。 背景 随着信息量的激增,爬虫技术成为了获取和处理大量网络数据的重要手段。Objective-C作为一种成熟的编程…

软考77-上午题-【面向对象技术3-设计模式】-创建型设计模式02

一、生成器模式 1-1、意图 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。 1-2、结构图 Builder 为创建一个 Product 对象的各个部件指定抽象接口。ConcreteBuilder 实现 Builder 的接口以构造和装配该产品的各个部件,定…

IDEA如何删除git最新一次远程提交

IDEA如何删除git最新一次远程提交 选择应用 -> Git -> Show History 选择最新提交上一次提交 -> Reset Current Branch to Here… Reset 提示框选择 Hard push到远程分支 -> 选择Force Push 结果验证 (最新分支已被删除)

QT网络编程之实现UDP广播发送和接收

推荐一个不错的人工智能学习网站,通俗易懂,内容全面,作为入门科普和学习提升都不错,分享一下给大家:前言https://www.captainbed.cn/ai 一.UDP通信 1.QT中实现UDP通信主要用到了以下类:QUdpSocket、QHost…

AI 大模型赋能手机影像,小米14 Ultra 让真实有层次

2月22日,小米龙年第一场重磅发布会,正式发布专业影像旗舰小米14 Ultra。 此前小米发布的两代 Ultra,在不同维度,引领了移动影像行业的走向。最新的小米14 Ultra 在定义的时候,我们反复在思考:怎么才能把移动…

解决iview表格固定列横向滚动条无法拖动问题

问题描述: iview的table添加固定列以后,滚动条在固定列下面无法拖动,只能在滚动区域有所反应 解决办法 【写入main.js引入的全局文件时不需要::v-deep; 写入单个文件需要加::v-deep】 方法一:【带合计行也适用】 //解决iview表…

uniapp报错:[获取文件失败] 以下文件已被配置忽略打包上传,模拟器无法获取...

uniapp分包控制台报错: Error: module ‘pagesMember/address/address.js’ is not defined, require args is ‘pagesMember/address/address.js’ 以及 [获取文件失败] 以下文件已被配置忽略打包上传,模拟器无法获取: pagesMember/address/…

【开源-土拨鼠充电系统】鸿蒙 HarmonyOS 4.0+微信小程序+云平台

本人自己开发的开源项目:土拨鼠充电系统 ✍GitHub开源项目地址👉:https://github.com/cheinlu/groundhog-charging-system ✍Gitee开源项目地址👉:https://gitee.com/cheinlu/groundhog-charging-system ✨踩坑不易&am…