本文参考文章:保护模式对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 > DPLs
总结
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;
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;
if (CPL <= DPLg) { /* 允许访问门符 */
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 异常产生,转去处理异常 */
}
CS.RPL = DPLs; /* 改变当前执行权限级别 */
EIP = gate_descriptor.offset; /* 加载进入EIP */
/* 执行中断例程 */
goto target_descritptor.base + gate_descriptor.offset;
} else {
/* 失败,#GP 异常产生,转去处理异常 */
}