操作系统实验六:保护模式之使用调用门提升特权级

对IA32分段机制中特权级的个人总结:

 

在IA32的分段机制里,分为4个特权等级(ring0~ring3):

      Level0                高(内层)
    L e v e l 1                           
  L  e  v  e  l  2                        
L   e   v   e   l   3       低(外层)


    各特权等级的区别在于对指令的限制(主要是系统指令的限制,如lgdt,lldt等)。这个非常容易理解,就是特权级越高,允许执行的指令越多。通过这个限制,普通应用程序无法使用一些特殊的系统指令(通常也不需要使用)。
    若需要实现一些必须通过特权指令才能实现的功能时,可以通过操作系统提供的服务来实现。此时,就会涉及到特权等级的转变。假设普通程序运行在ring3,当它调用一个特权等级为0的系统服务时,在将执行权交给系统服务时,执行的特权提升为ring0(此时可以使用特权指令完成特定的功能),服务完成后,将执行权返还给普通程序时,执行权降为ring3(此时处于限制状态,只能执行普通指令)。

    关于特权级的验证与转变,IA32有一套复杂的机制。为简单起见,在这里,仅对涉及到的地方做简单的介绍。

首先,需要对3个名词概念要有所认识:CPL、DPL、RPL。

当前特权级CPL(Current Privilege Level):
    CPL表示的是当前程序运行时所处在的特权级别,存储在CS和SS寄存器中。一般而言,CPL与当前所处在的代码段的描述符中DPL相同(当前代码段为一致代码段时除外,因为目前尚未涉及到,在此不进行考虑)。

描述符特权级DPL(Descriptor Privilege Level):
    DPL作为描述符(GDT、LDT等)属性中的一部分,存储在描述符的数据结构体中。当程序通过选择子使用或调用描述符中定义的数据段或代码段时,就会使用程序所具有的CPL值(一般而言其特权等级等于调用的代码段的在描述符中的DPL)与描述符中的DPL相比较,一般需要CPL的值小于等于被调用者的DPL,也就是说特权等级高的能调用特权级低的,随着代码段间不断相互调用,特权等级(CPL)只会不断降低或保持不变。若需要提高特权等级(执行特权级高的代码段),就需要用到调用门或其他手段了。

请求特权级RPL(Requested Privilege Level):
    RPL作为选择子的属性之一存储在选择子这个数据结构的第一位和第二位。当程序通过选择子使用或调用描述符中的某个数据段或代码时,除了CPL会和DPL相比较以外,RPL也会与DPL相比较,若CPL的特权等级高于或等于DPL但RPL的特权等级却低于DPL时,也是无法成功调用的。


    前面已经提到,若是直接调用的话,特权等级(CPL)只会越来越低,想要提高特权等级,可以通过任务门等手段。下面就看看使用任务门的一些情况。

    首先看看调用门在描述符中的情况:最明显的,门描述符中包含一个选择子,由上一次实验可知,通过使用任务门,可以转到门任务中所包含的选择子所指向的代码段。一个程序调用任务门,必须遵循上面提到的规则,也就是不能向上越权调用任务门。而任务门也是不能包含任意的选择子来转到任意特权等级的代码段的,必须是任务门的DPL大于或等于所指向的代码段的DPL。也就是说,使用任务门,特权等级只会提高或保持不变。
    通常使用jmp或call来调用调用门。对于jmp的使用也是有限制的,jmp只能调用那些DPL与所指向的代码段DPL相等的调用门。总结一下就是:可以暂时性的使用调用门提高特权等级执行一些代码然后返回低特权级代码段(使用call),若想要使用jmp使用调用门(可能一去不复返),那么必须保证使用调用门后特权等级依然不变。


    下面回到此次实验中来。此次实验是体验在低特权级别下使用任务门提高特权等级。但是之前的实验一直是在默认的最高特权等级rong0运行的。因此需要现降低运行特权等级。

使用的方法就是:
    假装自己是被一个低特权级别的代码段调用的(将ss,esp,cs,eip依次压入堆栈当前代码段所使用的堆栈),然后使用retf指令返回。

push 需要降到的代码段所使用的堆栈段的选择子(ss)
push 需要降到的代码段所使用的堆栈段栈顶指针(esp)
push 需要降到的代码段的选择子(cs)
push 需要降到的代码段所执行到的指令条数(eip)
retf


    通过这个方法就可以降权跳转到一个特权级别更低的代码段。同时我们需要为两个不同特权等级的代码段提供两个不同堆栈段。但是由于引导扇区空间限制,不能再代码中显示声明一段空间来作为堆栈使用,而是直接通过宏指定不会用到的内存做为堆栈使用。

#define StackSpace   0x7c00   //堆栈地址
#define StackTop       0x4f       //堆栈顶
#define StackSpace3 0x7a00  //堆栈地址
#define StackTop3     0x4f       //堆栈顶


    跳转到特权等级更高的代码段,必须要告诉系统这个在这个特权等级所使用的堆栈段是什么以及一些其他相关的信息,因此需要事先准备一个叫做TSS(Task-State Segment)的数据结构,TSS实际是保存各种寄存器到内存中以便以后使用的一组数据结构。同样因为引导扇区空间有限,直接接通过宏指定一段内存做为TSS(具体看pm.h中的代码)。

此次实验的流程:
    1.跳转到保护模式
    (pm32.c中main函数)
    2.重新加载新的GDT
    3.显示字符串:Protect.
    4.为GDT中门任务函数初始化地址
    5.为GDT中特权为3的函数初始化地址
    6.初始化TSS并使用ltr指令加载
    7.降权跳转到特权级为3的代码段(CodeRing3函数)
    8.显示字符串:Ring 3.
    9.使用call调用调用门跳转到CodeDest函数(默认特权级为0的代码段)
    10.显示字符串:Gate.
    11.不返回,而是在GDT的上设置好LDT的基地址并加载局部描述符(LDT)然后跳转到局部任务。
    12.显示字符串:Local.
    13.进入死循环。

以下是此次实验代码:
code:run.c

 

code:pm.h

 

code:pm16.c

 

code:pm32.c

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值