本文参考文章:保护模式对CPL、RPL、DPL的总结

在本章,首先开始讲了系统调用过程,系统调用过程中涉及到用户空间和系统空间之间的转换,有关的权限检查也是不可少的。由于跳转的方式有两种:(1)直接转移(far call 及 far jmp);(2)使用call gate 进行控制权的转移;(3)中断门或者陷阱门转移。

书上分类分得补清楚,而且也没有说清楚Int 0x80的跳转过程实际上是使用中断门或者陷阱门进行转移

其中,中断门符及陷井门必须存放在IDT中,IDT表也可以存放call gate。

1、 中断调用时的权限检查
   用中断门符进行转移时,所作的权限检查同call gate相同,区别在于intterrupt gate 转移不需要检查RPL,因为,没有RPL需要检查。
★ 必须有足够的权限访问门符,CPL <= DPLg
★ 向同级权限代码转移时,CPL == DPLs,向高权限代码转移时,CPL

总结



if (CPL <= DPLg) { /* 有足够权限访问门符 */
    if (CPL >= DPLs) {
        /* 允许访问目标代码头 */
    } else {
         /* 失败,#GP异常发生 */
    }} else {
/* 失败,#GP异常发生 */
}


2、 控制权的转移
   发生异常或中断调用时
★ 用中断向量在中断描述符表查找描述符:中断向量×8,然后加上IDT表基址得出描述符表。
★ 从查找到的描述符中得到目标代码段选择子,并在相应的GDT或LDT中获取目标代码段描述符。
★ 目标代码段描述符的基址加上门符中的offset,确定最终执行入口点。

例子:

INT 0X80的实际情况是(跟书上的代码一样,但是很明显,该书并没有讲清楚它到底是为那个分类举例):


vector = 0x80;

INTGATE_DESCRIPTOR gate_descriptor = IDTR.base + vector * 8;

CODESEG_DESCRIPTOR target_descriptor;

TSS tss = TR.base;               /* 得到TSS 内存块 */

DPLg = gate_descriptor.DPL;

target_cs = gate_descriptor.selector;
CPL

if (target_cs.TI == 0) {   /* index on GDT */

    target_descriptor = GDTR.base + target_cs.SI * 8;

} else {              /* index on LDT */

target_descriptor = LDTR.base + target_cs.SI * 8;

    }
DPLs = target_descriptor.DPL;



if (CPL > DPLs) {     /* 向高权限代码转移 */


    /* 根据目标代码段的DPL值来选取相应权限的stack结构 */

    switch (DPLs) {

    case 0 :     /* 假如目标代码处理0级,则选0级的stack结构 */

             SS = tss.ss0;

             ESP = tss.esp0;

             break;

        case 1:

             SS = tss.ss1;

            ESP = tss.esp1;

             break;

        case 2: 

             SS = tss.ss2;

             ESP = tss.esp2;

             break;

    }
       /* 以下必须保护旧的stack结构,以便返回 */

    *--esp = SS;          /* 将当前SS入栈保护 */

    *--esp = ESP;         /* 将当前ESP入栈保护 */


} else if (CPL == DPLs) {

     /* 同级转移,继续向下执行 */

} else {

    /* 失败,#GP异常产生,转去处理异常 */

}



*--esp = EFLAGS;          /* eflags 寄存器入栈 */


     /* 分别将 NT、NT、RF及VM标志位清0 */

EFLAGS.TF = 0;            

EFLAGS.NT = 0;

EFLAGS.RF = 0;

EFLAGS.VM = 0;


if (gate_descriptor.type == I_GATE32) { /* 假如是中断门符 */

EFLAGS.IF = 0;          /* 也将IF标志位清0,屏蔽响应中断 */

     }

             

     *--esp = CS;              /* 当前段选择子入栈 */

     *--esp = EIP;             /* 当前EIP 入栈 */
  CS = target_selector;      /* 加载目标代码段 */

CS.RPL = DPLs;            /* 改变当前执行权限级别 */

EIP = gate_descriptor.offset; /* 加载进入EIP */


/* 执行中断例程 */

goto target_descritptor.base + gate_descriptor.offset; 


} else {

/* 失败,#GP 异常产生,转去处理异常 */

}