操作系统实践之第二章(特权级变换*实战篇)

来源:互联网 发布:linux内核架构 编辑:程序博客网 时间:2024/05/22 13:45

纸上得来终觉浅,绝知此事还是得捣鼓捣鼓才行啊。理论与实践还是要两手抓两手都要硬嘛。

从理论上来说,在ret指令执行前,堆栈中应该已经准备好了目标代码段的CS、EIP,以及SS和ESP,另外还可能有参数。需要注意的是,这些既可以使处理器压入栈的,也可以使由我们自己压入栈的。ret指令执行前的堆栈可能如下图示:


这样,执行ret后就可以转移到低特权级的代码中 了。我们在前面写过的代码中添加可以添加一个ring3的代码段和一个ring3的堆栈段,如下:

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……LABEL_DESC_VIDEO:      Descriptor 0B8000h,     0ffffh, DA_DRW+DA_DPL3……SelectorCodeRing3equLABEL_DESC_CODE_RING3- LABEL_GDT + SA_RPL3……SelectorStack3equLABEL_DESC_STACK3- LABEL_GDT + SA_RPL3……; 堆栈段ring3[SECTION .s3]ALIGN32[BITS32]LABEL_STACK3:times 512 db 0TopOfStack3equ$ - LABEL_STACK3 - 1; END of [SECTION .s3]……; CodeRing3[SECTION .ring3]ALIGN32[BITS32]LABEL_CODE_RING3:movax, SelectorVideomovgs, axmovedi, (80 * 14 + 0) * 2movah, 0Chmoval, '3'mov[gs:edi], axjmp$SegCodeRing3Lenequ$ - LABEL_CODE_RING3; END of [SECTION .ring3]

这段ring3的代码一目了然,只是由于这段代码运行在ring3,而在其中要写显存而访问到VIDEO段,为了不会产生错误,我们把VIDEO段的DPL修改为了3。

另外,新段对应的描述符的属性加上了DA_DPL3,让它的DPL变成了3,相应的选择子的SA_RPL3将RPL也置为了3。初始化描述符的代码与前面相似,就不再重复了。这样,代码段和堆栈段就准备好了,下一步就是为retf指令准备SS、ESP、CS、EIP这些寄存器的目标值了,具体操作如下:

pushSelectorStack3pushTopOfStack3pushSelectorCodeRing3push0retf

至此需要修改的就改完了,我们将程序编译为test.com然后拷贝到pm.img中,可以运行程序看到如下结果(书本完整源码pmtest5a.asm):

看到最后红色的3就表明我们已经由ring0跳转到ring3了!历史性的特权级间跳转成功!

进入ring3之后就可以体验一下调用门了,先将调用门的描述符和选择子以及[SECTION  .ring3]的代码稍作修改。

; 门                               目标选择子,偏移,DCount, 属性LABEL_CALL_GATE_TEST: Gate SelectorCodeDest,   0,     0, DA_386CGate+DA_DPL3……SelectorCallGateTestequLABEL_CALL_GATE_TEST- LABEL_GDT + SA_RPL3……; CodeRing3[SECTION .ring3]ALIGN32[BITS32]LABEL_CODE_RING3:movax, SelectorVideomovgs, axmovedi, (80 * 14 + 0) * 2movah, 0Chmoval, '3'mov[gs:edi], axcallSelectorCallGateTest:0jmp$SegCodeRing3Lenequ$ - LABEL_CODE_RING3; END of [SECTION .ring3]

在进入死循环前,我们使用了调用门指令,修改相应的描述符和选择子是为了满足CPL和RPL都小于等于调用门DPL的条件。但是现在还不能运行,因为从低特权级到高特权级的转移需要TSS的帮助,下面我们就来准备一个TSS。

LABEL_DESC_TSS:        Descriptor 0,          TSSLen-1, DA_386TSS……SelectorTSSequLABEL_DESC_TSS- LABEL_GDT……; TSS[SECTION .tss]ALIGN32[BITS32]LABEL_TSS:DD0; BackDDTopOfStack; 0 级堆栈DDSelectorStack; DD0; 1 级堆栈DD0; DD0; 2 级堆栈DD0; DD0; CR3DD0; EIPDD0; EFLAGSDD0; EAXDD0; ECXDD0; EDXDD0; EBXDD0; ESPDD0; EBPDD0; ESIDD0; EDIDD0; ESDD0; CSDD0; SSDD0; DSDD0; FSDD0; GSDD0; LDTDW0; 调试陷阱标志DW$ - LABEL_TSS + 2; I/O位图基址DB0ffh; I/O位图结束标志TSSLenequ$ - LABEL_TSS

上面我们只初始化了0级堆栈,添加初始化TSS描述符的代码后,TSS就算是准备好了,我们需要在特权级转换之前加载它。

callDispReturnmovax, SelectorTSSltraxpushSelectorStack3pushTopOfStack3pushSelectorCodeRing3push0retf

到目前为止,我们已经成功地实现了由高特权级到低特权级或者由低特权级到高特权级的双向转换,并且熟悉了调用门这种经典门描述符的使用。

为了可以让程序顺利地返回实模式,我们将调用局部任务的代码加入到调用门的目标代码(SECTION  .sdest)。最后,程序将由这里进入局部任务,然后原路返回实模式。

[SECTION .sdest]; 调用门目标段[BITS32]LABEL_SEG_CODE_DEST:movax, SelectorVideomovgs, ax; 视频段选择子(目的)movedi, (80 * 12 + 0) * 2; 屏幕第 12 行, 第 0 列。movah, 0Ch; 0000: 黑底    1100: 红字moval, 'C'mov[gs:edi], ax; Load LDTmovax, SelectorLDTlldtaxjmpSelectorLDTCodeA:0; 跳入局部任务,将打印字母 'L'。;retfSegCodeDestLenequ$ - LABEL_SEG_CODE_DEST; END of [SECTION .sdest]

最后的执行结果如下图(完整代码pmtest5.asm):


总结一下过程:一个是从高特权级到低特权级的跳转如何确定是跳转到哪一级呢?这个是由当时对应堆栈中的CS、EIP等决定的。那从低特权级到高特权级又是如何确定的呢?根据目标代码段的DPL(新的CPL)从TSS中选择应该切换至哪个SS和ESP。







0 0
原创粉丝点击