初步学习Protected Mode(3)

来源:互联网 发布:阿里云上海代理商 编辑:程序博客网 时间:2024/05/10 19:37

程序从一个代码段转移到另一个代码段之前,目标代码段的选择子会被加载到cs中。作为加载过程的一部分,处理器会检查描述符的界限、类型、特权级等内容。如果检验成功,cs会被将在,程序控制将转移到新的代码段中,从eip指示的位置开始执行。


使用jmp或call进行直接转移:

如果目标是非一致代码段,要求CPL必须等于目标段的DPL,同时要求RPL小于DPL。

如果目标是一致代码段,则要求CPL大于或者等于目标段的DPL,RPL此时不做检查。当转移到一致代码段中后,CPL会被延续下来,而不会变成目标代码段的DPL。也就是说,通过jmp和call所能进行的代码段间转移是非常有限的,对于非一致代码段,只能在相同特权级代码段之间转移。遇到一致代码段也最多能从低到高,而且CPL不会改变。如果想自由的进行不同特权级之间的转移,显然需要其他几种方式。运用门描述符或者TSS。


门:

门描述符的结构和GDT LDT的描述符有很大不同,它主要定义了目标代码段对应段的选择子、入口地址的偏移和一些属性等。直观来看,一个门描述了由一个选择子和一个偏移所指定的线性地址,程序正式通过这个地址继续拧转移的。

门描述符分为4种:

调用门 call gates

中断门 interrupt gates

陷阱门 trap gates

任务门 task gates


下面的代码增加一个代码段作为通过调用门转移的目标段。

; ==========================================; pmtest4.asm; 编译方法:nasm pmtest4.asm -o pmtest4.com; ==========================================%include"pm.inc"; 常量, 宏, 以及一些说明org0100hjmpLABEL_BEGIN[SECTION .gdt]; GDT;                                         段基址,       段界限     , 属性LABEL_GDT:Descriptor       0,                 0, 0     ; 空描述符LABEL_DESC_NORMAL:Descriptor       0,            0ffffh, DA_DRW; Normal 描述符LABEL_DESC_CODE32:Descriptor       0,  SegCode32Len - 1, DA_C + DA_32; 非一致代码段, 32LABEL_DESC_CODE16:Descriptor       0,            0ffffh, DA_C; 非一致代码段, 16LABEL_DESC_CODE_DEST:Descriptor       0,SegCodeDestLen - 1, DA_C + DA_32; 非一致代码段, 32LABEL_DESC_DATA:Descriptor       0,DataLen - 1, DA_DRW; DataLABEL_DESC_STACK:Descriptor       0,        TopOfStack, DA_DRWA + DA_32; Stack, 32 位LABEL_DESC_LDT:Descriptor       0,        LDTLen - 1, DA_LDT; LDTLABEL_DESC_VIDEO:Descriptor 0B8000h,            0ffffh, DA_DRW; 显存首地址; 门                                            目标选择子,       偏移, DCount, 属性LABEL_CALL_GATE_TEST:Gate  SelectorCodeDest,          0,      0, DA_386CGate + DA_DPL0; GDT 结束GdtLenequ$ - LABEL_GDT; GDT长度GdtPtrdwGdtLen - 1; GDT界限dd0; GDT基地址; GDT 选择子SelectorNormalequLABEL_DESC_NORMAL- LABEL_GDTSelectorCode32equLABEL_DESC_CODE32- LABEL_GDTSelectorCode16equLABEL_DESC_CODE16- LABEL_GDTSelectorCodeDestequLABEL_DESC_CODE_DEST- LABEL_GDTSelectorDataequLABEL_DESC_DATA- LABEL_GDTSelectorStackequLABEL_DESC_STACK- LABEL_GDTSelectorLDTequLABEL_DESC_LDT- LABEL_GDTSelectorVideoequLABEL_DESC_VIDEO- LABEL_GDTSelectorCallGateTestequLABEL_CALL_GATE_TEST- LABEL_GDT; END of [SECTION .gdt][SECTION .data1] ; 数据段ALIGN32[BITS32]LABEL_DATA:SPValueInRealModedw0; 字符串PMMessage:db"In Protect Mode now. ^-^", 0; 进入保护模式后显示此字符串OffsetPMMessageequPMMessage - $$StrTest:db"ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0OffsetStrTestequStrTest - $$DataLenequ$ - LABEL_DATA; END of [SECTION .data1]; 全局堆栈段[SECTION .gs]ALIGN32[BITS32]LABEL_STACK:times 512 db 0TopOfStackequ$ - LABEL_STACK - 1; END of [SECTION .gs][SECTION .s16][BITS16]LABEL_BEGIN:movax, csmovds, axmoves, axmovss, axmovsp, 0100hmov[LABEL_GO_BACK_TO_REAL+3], axmov[SPValueInRealMode], sp; 初始化 16 位代码段描述符movax, csmovzxeax, axshleax, 4addeax, LABEL_SEG_CODE16movword [LABEL_DESC_CODE16 + 2], axshreax, 16movbyte [LABEL_DESC_CODE16 + 4], almovbyte [LABEL_DESC_CODE16 + 7], ah; 初始化 32 位代码段描述符xoreax, eaxmovax, csshleax, 4addeax, LABEL_SEG_CODE32movword [LABEL_DESC_CODE32 + 2], axshreax, 16movbyte [LABEL_DESC_CODE32 + 4], almovbyte [LABEL_DESC_CODE32 + 7], ah; 初始化测试调用门的代码段描述符xoreax, eaxmovax, csshleax, 4addeax, LABEL_SEG_CODE_DESTmovword [LABEL_DESC_CODE_DEST + 2], axshreax, 16movbyte [LABEL_DESC_CODE_DEST + 4], almovbyte [LABEL_DESC_CODE_DEST + 7], ah; 初始化数据段描述符xoreax, eaxmovax, dsshleax, 4addeax, LABEL_DATAmovword [LABEL_DESC_DATA + 2], axshreax, 16movbyte [LABEL_DESC_DATA + 4], almovbyte [LABEL_DESC_DATA + 7], ah; 初始化堆栈段描述符xoreax, eaxmovax, dsshleax, 4addeax, LABEL_STACKmovword [LABEL_DESC_STACK + 2], axshreax, 16movbyte [LABEL_DESC_STACK + 4], almovbyte [LABEL_DESC_STACK + 7], ah; 初始化 LDT 在 GDT 中的描述符xoreax, eaxmovax, dsshleax, 4addeax, LABEL_LDTmovword [LABEL_DESC_LDT + 2], axshreax, 16movbyte [LABEL_DESC_LDT + 4], almovbyte [LABEL_DESC_LDT + 7], ah; 初始化 LDT 中的描述符xoreax, eaxmovax, dsshleax, 4addeax, LABEL_CODE_Amovword [LABEL_LDT_DESC_CODEA + 2], axshreax, 16movbyte [LABEL_LDT_DESC_CODEA + 4], almovbyte [LABEL_LDT_DESC_CODEA + 7], ah; 为加载 GDTR 作准备xoreax, eaxmovax, dsshleax, 4addeax, LABEL_GDT; eax <- gdt 基地址movdword [GdtPtr + 2], eax; [GdtPtr + 2] <- gdt 基地址; 加载 GDTRlgdt[GdtPtr]; 关中断cli; 打开地址线A20inal, 92horal, 00000010bout92h, al; 准备切换到保护模式moveax, cr0oreax, 1movcr0, eax; 真正进入保护模式jmpdword SelectorCode32:0; 执行这一句会把 SelectorCode32 装入 cs, 并跳转到 Code32Selector:0  处;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;LABEL_REAL_ENTRY:; 从保护模式跳回到实模式就到了这里movax, csmovds, axmoves, axmovss, axmovsp, [SPValueInRealMode]inal, 92h; ┓andal, 11111101b; ┣ 关闭 A20 地址线out92h, al; ┛sti; 开中断movax, 4c00h; ┓int21h; ┛回到 DOS; END of [SECTION .s16][SECTION .s32]; 32 位代码段. 由实模式跳入.[BITS32]LABEL_SEG_CODE32:movax, SelectorDatamovds, ax; 数据段选择子movax, SelectorVideomovgs, ax; 视频段选择子movax, SelectorStackmovss, ax; 堆栈段选择子movesp, TopOfStack; 下面显示一个字符串movah, 0Ch; 0000: 黑底    1100: 红字xoresi, esixoredi, edimovesi, OffsetPMMessage; 源数据偏移movedi, (80 * 10 + 0) * 2; 目的数据偏移。屏幕第 10 行, 第 0 列。cld.1:lodsbtestal, aljz.2mov[gs:edi], axaddedi, 2jmp.1.2:; 显示完毕callDispReturncallSelectorCallGateTest:0; 测试调用门(无特权级变换),将打印字母 'C'。;callSelectorCodeDest:0; Load LDTmovax, SelectorLDTlldtaxjmpSelectorLDTCodeA:0; 跳入局部任务,将打印字母 'L'。; ------------------------------------------------------------------------DispReturn:pusheaxpushebxmoveax, edimovbl, 160divblandeax, 0FFhinceaxmovbl, 160mulblmovedi, eaxpopebxpopeaxret; DispReturn 结束---------------------------------------------------------SegCode32Lenequ$ - LABEL_SEG_CODE32; END of [SECTION .s32][SECTION .sdest]; 调用门目标段[BITS32]LABEL_SEG_CODE_DEST:;jmp$movax, SelectorVideomovgs, ax; 视频段选择子(目的)movedi, (80 * 12 + 0) * 2; 屏幕第 12 行, 第 0 列。movah, 0Ch; 0000: 黑底    1100: 红字moval, 'C'mov[gs:edi], axretfSegCodeDestLenequ$ - LABEL_SEG_CODE_DEST; END of [SECTION .sdest]; 16 位代码段. 由 32 位代码段跳入, 跳出后到实模式[SECTION .s16code]ALIGN32[BITS16]LABEL_SEG_CODE16:; 跳回实模式:movax, SelectorNormalmovds, axmoves, axmovfs, axmovgs, axmovss, axmoveax, cr0andal, 11111110bmovcr0, eaxLABEL_GO_BACK_TO_REAL:jmp0:LABEL_REAL_ENTRY; 段地址会在程序开始处被设置成正确的值Code16Lenequ$ - LABEL_SEG_CODE16; END of [SECTION .s16code]; LDT[SECTION .ldt]ALIGN32LABEL_LDT:;                                         段基址       段界限     ,   属性LABEL_LDT_DESC_CODEA:Descriptor       0,     CodeALen - 1,   DA_C + DA_32; Code, 32 位LDTLenequ$ - LABEL_LDT; LDT 选择子SelectorLDTCodeAequLABEL_LDT_DESC_CODEA- LABEL_LDT + SA_TIL; END of [SECTION .ldt]; CodeA (LDT, 32 位代码段)[SECTION .la]ALIGN32[BITS32]LABEL_CODE_A:movax, SelectorVideomovgs, ax; 视频段选择子(目的)movedi, (80 * 13 + 0) * 2; 屏幕第 13 行, 第 0 列。movah, 0Ch; 0000: 黑底    1100: 红字moval, 'L'mov[gs:edi], ax; 准备经由16位代码段跳回实模式jmpSelectorCode16:0CodeALenequ$ - LABEL_CODE_A; END of [SECTION .la]

现在添加调用门:

LABEL_CALL_GATE_TEST: Gate SelectorCodeDest, 0, 0, DA_386CGate + DA_DPL0

Gate的宏定义如下:

%macro Gate 4dw(%2 & 0FFFFh); 偏移 1(2 字节)dw%1; 选择子(2 字节)dw(%3 & 1Fh) | ((%4 << 8) & 0FF00h); 属性(2 字节)dw((%2 >> 16) & 0FFFFh); 偏移 2(2 字节)%endmacro ; 共 8 字节

门描述符的属性是DA_386CGate 表明它是一个调用门,里面指定的选择子是SelectorCodeDest


SelectorCallGateTest equ LABEL_CALL_GATE_TEST - LABEL_GDT


调用门本质上只是个入口地址加上了若干属性而已。调用门存在的原因是要用它来实现不同特权级别的代码之间的转移。下面是使用调用门进行转移时特权级检验的规则:

代码A转移到代码B,运用一个调用门G,即调用门G中的目标选择子指向代码B的段。实际上,CPL、RPL、代码B的DPL(DPL_B)、调用门G的DPL(DPL_G).A访问G这个调用门时,规则相当于访问一个数据段,要求CPL和RPL都小于等于DPL_G。也就是说CPL和RPL需要在更高的特权级上。

除了这一步之外,系统还将比较CPL和DPL_B。如果是一致代码段的话,要求DPL_B<=CPL。如果是非一致代码段的话,call指令和jmp指令又有所不同。在用call指令时,要求DPL_B<=CPL;在用jmp指令时,只能是DPL_B=CPL

总之,如果是一致代码段,那么call指令可以实现从低特权指令到高特权指令。

如果不是一致代码段,那么call指令和调用门可以实现从低特权指令到高特权指令。

而然,特权级变换的时候,堆栈也要发生变化。处理器通过一定机制避免了高特权级的过程由于栈空间不足而崩溃。而且,如果不同特权级共享同一个堆栈的话,高特权级的程序可能因此受到有意无意的干扰。今天就这样吧。


























0 0