(第三章 九)通过调用门进行有特权级变换的转移(二)
本文展示《(第三章 9)通过调用门进行有特权级变换的转移(一)》的主要流程:
跳入保护模式
[SECTION .s32]-->
[SECTION .ring3]-->
调用门-->[SECTION .sdest]-->
[SECTION .la]
跳回实模式
***************************************************************************************************************
1、本段([SECTION .s32])属性:
[SECTION .gdt]
LABEL_DESC_CODE32: Descriptor 0, SegCode32Len-1, DA_C+DA_32 ;非一致,32
SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT
[SECTION .s32];32位代码段,由实模式跳入 ... (10行0列显示PMMessage,即"In Protect Mode now. ^-^") ... mov ax, SelectorTSS ltr ax ; 在任务内发生特权级变换时要切换堆栈,而内层堆栈的指针存放在当前任务的TSS中,所以要设置任务状态段寄存器TR push SelectorStack3 ;ss push TopOfStack3 ;esp push SelectorCodeRing3 ;cs push 0 ;eip retf ;ring0~~>ring3跳转
从[SECTION .s32]跳到[SECTION .ring3]是从高到低特权级的跳转!
本来ret(retf)是和call配合使用的指令,用来返回断点。这里单独使用,可以理解为“从[SECTION .32]返回[SECTION .ring3]”,用来从高特权级跳转到低特权级,具体过程如书P59叙述,P60图3.21所示:
1)检查被调用者堆栈中保存的CS中的RPL(对应代码push SelectorCodeRing3),以判断返回时是否要变换特权级。
此时发现当前特权级为0,转到特权级为3的代码段,发生了特权级变化(高-->低)。
2)加载被调用者堆栈上的cs和eip(SelectorCodeRing3和0)。
(此时怎么进行特权级检验???)
此时,就返回断点了——在本程序中cs和eip已经指向[SECTION .ring3]段了。
3)此retf不含参数,不用增加esp跳过参数。当前堆栈是被调用者([SECTION .s32])堆栈。
4)加载被调用者([SECTION .s32)堆栈中的ss和esp,切换到调用者([SECTION .ring3])堆栈。此时,被调用者([SECTION .s32)堆栈中的ss和esp被丢弃,但由于等会儿还要从低特权级转换回高特权级,故需要将“0级堆栈的SelectorStack和TopOfStack”提前放入TSS。
此时,当前堆栈从被调用者([SECTION .s32])堆栈变成了调用者([SECTION .ring3)堆栈了。
5)此retf不含参数,不用增加esp跳过参数。当前堆栈是调用者([SECTION .ring3)堆栈。
6)检查ds、es、fs、gs的值,如果其中哪一个寄存器指向的段的DPL小于CPL(此规则不适用于一致代码段),那么一个空描述符被加载到该寄存器。我想:此时这几个寄存器都被置空描述符了吧~(???)
***************************************************************************************************************
2、本段([SECTION .ring3])属性:
[SECTION .gdt]
LABEL_DESC_CODE_RING3: Descriptor 0, SegCodeRing3Len-1, DA_C+DA_32+DA_DPL3
LABEL_DESC_STACK3: Descriptor 0, TopOfStack3, DA_DRWA+DA_32+DA_DPL3
SelectorCodeRing3 equ LABEL_DESC_CODE_RING3 - LABEL_GDT + SA_RPL3
SelectorStack3 equ LABEL_DESC_STACK3 - LABEL_GDT + SA_RPL3
[SECTION .ring3] ... (14行0列显示'3') ... call SelectorCallGateTest:0 ;调用调用门中的目标代码段,如果在目标段最后有retf可以返回这个断点,但我们不这么做。我们在目标段中继续进入局部段[SECTION .la],然后返回实模式 ...
这个跳转是“通过调用门 从低到高特权级”!
1)step1: 指示调用门的选择子的RPL<=门描述符DPL & 当前代码段的CPL<=门描述符的DPL。
此时,在[SECTION .ring3]中。因为[SECTION .ring3]是非一致代码段,故在从[SECTION .s32]跳转到该段时,已经设置CPL=3 (参见http://chuanwang66.iteye.com/admin/blogs/1075472)。即是说,此时CPL=3。
call SelectorCallGateTest:0调用调用门,由SelectorCallGateTest equ LABEL_CALL_GATE_TEST - LABEL_GDT + SA_RPL3)可知,调用门的RPL为3。即是说,此时RPL=3。
又调用门的DPL=3。
由上面三段的描述有: CPL<=调用门DPL & RPL<=调用门DPL。故可以访问到调用门中的目标段选择子了^_^
2)step2: CPL>=DPL,RPL不作检查(因为RPL总被清0)
现在,CPL=3; 目标段[SECTION .sdest]的DPL=0,且为非一致代码段。故CPL>=DPL(RPL不作检查),满足特权级检查,跳转到[SECTION .sdest],哈哈
3)跳转后,CPL被修改为0(原来为3)
因为CPL=目标段[SECTION .sdest]的DPL(=0),因此,跳转到[SECTION .sdest]后CPL=0
***************************************************************************************************************
3、本段([SECTION .sdest])属性:
[SECTION .gdt]
; 门 目标选择子, 偏移, DCount, 属性
LABEL_CALL_GATE_TEST: Gate SelectorCodeDest, 0, 0, DA_386CGate + DA_DPL3
LABEL_DESC_CODE_DEST: Descriptor 0, SegCodeDestLen-1, DA_C+DA_32 ;非一致,32
SelectorCallGateTest equ LABEL_CALL_GATE_TEST - LABEL_GDT + SA_RPL3
SelectorCodeDest equ LABEL_DESC_CODE_DEST - LABEL_GDT
[SECTION .sdest] ;调用门目标段 ... (12行0列显示'C') ... mov ax, SelectorLDT lldt ax jmp SelectorLDTCodeA:0 ;跳到LDT中定义的局部段
[SECTION .sdest]段是非一致32位段,而且DPL=0,并且当前CPL=0。而此后用到的“DPL”和“选择子中的RPL”都为0,均在最高特权级上跳转,不需要设计权限检查了!啊,是不是突然感觉轻松许多了呢^_^
***************************************************************************************************************
4、本段([SECTION .la])属性:
[SECTION .gdt]
LABEL_DESC_LDT: Descriptor 0, LDTLen-1, DA_LDT ;LDT
SelectorLDT equ LABEL_DESC_LDT - LABEL_GDT
[SECTION .ldt]
LABEL_LDT_DESC_CODEA: Descriptor 0, CodeALen - 1, DA_C + DA_32 ; Code, 32 位
SelectorLDTCodeA equ LABEL_LDT_DESC_CODEA - LABEL_LDT + SA_TIL
[SECTION .la] ... (13行0列显示'L') ... jmp SelectorCode16:0 ;准备经由16位代码段跳回实模式
***************************************************************************************************************
参考: