敬业的IT人 >> 编程开发 >> 其他语言 >> 控制转移(2)

控制转移(2)

敬业的IT人 互联网 佚名 2008-2-19 15:33:16

2.关于实例三的说明

    有些步骤的实现方法已在前面的实例中做过介绍,下面就任务内无特权级变换的转移和使用局部描述符LDT等作些说明:

(1)实模式下初始化LDT

    演示任务使用了局部描述符表LDT,本实例中该LDT在实模式下初始化(当然,也可以在使用LDT前的保护模式初始化)。为了简便,LDT中各描述符的界限和属性值在定义时预置,利用一个子程序设置各段的段基地址。为方便起见,在定义时把各段的段值安排在相应描述符的段基地址低16位字段中。由于实例中各段在实模式下定位(这是因为程序是从实模式下启动执行的),所以把段值乘以16就是对应的段基地址。

(2)装载LDTR寄存器

    在使用LDT之前,还要装载局部描述符表寄存器LDTR。本实例中的如下两条指令用于装载LDTR:     需要注意的是,无论是否通过调用门,只要不发生特权级变换,就不会切换堆栈。

3.向外层返回

    与使用CALL指令通过调用门向内层变换相反,使用RET指令实现向外层返回。段间返回指令RET从堆栈中弹出返回地址,并且可以采用调整ESP的方法,跳过相应的在调用之前压入堆栈的参数。返回地址的选择子指示要返回的代码段的描述符,从而确定返回的代码段。选择子的RPL确定返回后的特权级,而不是对应描述符的DPL,这是因为,段间返回指令RET可能使控制返回到一致代码段,而一致代码段可以在DPL规定的特权级以外的特权级执行。需要注意的是,RET指令所使用的返回地址的选择子只能使用代码段描述符,而不能使用任何系统段描述符或门描述符,当然,更不能使用数据段描述符,否则会引起异常。与CALL指令相对应,RET指令也不能向内层返回。 段间返回指令完成返回的步骤如下:     (1)RET指令先从堆栈中弹出返回地址。如果弹出地址的选择子的RPL规定相对于CPL更外层的特权级,那么就引起向外层返回。     (2)为向外层返回,跳过内层堆栈中的参数,再从内层栈中弹出指向外层堆栈的指针,并装入SS及 ESP,以恢复外层堆栈。     (3)调整ESP,跳过在相应的调用之前压入到外层堆栈的参数。即返回指令不但弹出内层栈的参数,而且也弹出外层栈的参数。     (4)然后,检查数据段寄存器DS、ES、FS及GS,以保证寻址的段在外层是可访问的,如果段寄存器寻址的段在外层是不可访问的,那么装入一个空选择子,以避免在返回时发生保护空洞。     (5)返回外层继续执行。     上述五步是对带立即数的段间返回指令而言的,立即数规定了堆栈中要跳过的参数的字节数。对于无立即数的段间返回指令缺少第二步和第三步。若RET指令不需要向外层返回,那么就只有(1)和(5)两步。对于有通过堆栈传递参数的子程序,必须使用带立即数的返回指令返回,否则返回时会装载错误的外层栈指针。     若不使用带立即数的返回指令,可以在返回前把外层栈的栈指针存入内层栈中的用于保存返回地址上方两个双字的区域中,由外层返回的过程可知,这可正确恢复外层栈的指针,但在外层程序中,必须人为调整外层栈指针,以便废除在外层栈中压入的参数。在使用C调用约定的程序中可使用此方法。但这会增加代码的长度和处理时间,使代码效率变低。正因为如此,在Windows9X下,新增加了一种STDCALL的调用约定,它正是为了适应Intel系列处理器的体系结构而产生的。

<四>演示任务内特权级变换的实例(实例四)

    下面给出一个演示任务内特权级变换的实例。该实例演示在任务内通过调用门从外层特权级变换到内层特权级;也演示通过段间返回指令从内层特权级变换到外层特权级;还演示通过调用门的无特权级变换的转移。实例使用了任务状态段TSS,这是因为任务内特权级变换时要使用的内层堆栈指针存放在TSS中。

1.实现步骤

    该实例的实现步骤为:     (1)实方式下初始化;     (2)切换到保护模式;     (3)设置TR和LDTR。由于在任务内发生特权级变换时要切换堆栈,而内层堆栈的指针存放在当前任务的TSS中,所以在进入保护模式后设置任务状态段寄存器TR。由于演示任务使用了局部描述符表,所以设置LDTR;     (4)经调用门进入32位过渡代码段;     (5)建立返回3级演示代码段的环境;     (6)利用RET指令转移到3级的演示代码段。为了演示外层程序通过调用门调用内层程序,要使CPL>0。实例先通过段间返回指令RET从特权级0变换到特权级3的演示代码段。在特权级3下,通过调用门调用1级的子程序。随着执行段间返回指令RET,又回到3级的演示代码段;     (7)在3级的演示代码段中,经调用门转移到0级的32位过渡代码段;     (8)直接转0级的临时代码段;     (9)准备返回实模式;     (10)切换回实模式;     (11)实模式下的恢复工作。

2.源程序组织和清单

    实例四由如下部分组成:     (1)全局描述符表GDT。GDT含有演示任务的TSS段描述符和LDT段描述符,此外还含有临时代码段的描述符、规范数据段描述符和视频缓冲区段描述符。     (2)演示任务的LDT段。它含有除临时代码段外的其它代码段的描述符和演示任务各级堆栈段描述符,还含有3个调用门。     (3)演示任务的TSS段。     (4)演示任务的0级、1级和3级堆栈段。     (5)显示子程序段。32位代码段,特权级1。     (6)演示代码段。32位代码段,特权级3。     (7)过渡代码段。32位段,特权级0。     (8)临时代码段。16位段,特权级0。     (9)实模式下的数据和代码段。     该实例的逻辑功能是显示演示代码段执行时的当前特权级CPL。源程序清单如下: ;名称:ASM4.ASM ;功能:演示任务内有特权级变换的转移 ;编译:TASM ASM4.ASM ;连接:TLINK /32 ASM4.OBJ ;---------------------------------------------------------------------------- INCLUDE 386SCD.INC ;---------------------------------------------------------------------------- GDTSeg SEGMENT PARA USE16 ;全局描述符表数据段(16位) ;---------------------------------------------------------------------------- ;全局描述符表 GDT LABEL BYTE ;空描述符 DUMMY Desc <> ;规范段描述符 Normal Desc <0ffffh,,,ATDW,,> ;视频缓冲区段描述符(DPL=3) VideoBuf Desc <07fffh,8000h,0bh,ATDW+DPL3,,> ;---------------------------------------------------------------------------- EFFGDT LABEL BYTE ;任务状态段TSS描述符 DemoTSS Desc <DemoTssLen-1,DemoTSSSeg,,AT386TSS,,> ;局部描述符表段的描述符 DemoLDTD Desc <DemoLDTLen-1,DemoLDTSeg,,ATLDT,,> ;临时代码段描述符 TempCode Desc <0ffffh,TempCodeSeg,,ATCE,,> ;---------------------------------------------------------------------------- GDTLen = $-GDT ;全局描述符表长度 GDNum = ($-EFFGDT)/(SIZE Desc) ;需特殊处理的描述符数 ;---------------------------------------------------------------------------- Normal_Sel = Normal-GDT ;规范段描述符选择子 Video_Sel = VideoBuf-GDT ;视频缓冲区段描述符选择子 ;---------------------------------------------------------------------------- DemoTSS_Sel = DemoTSS-GDT ;任务状态段描述符选择子 DemoLDT_Sel = DemoLDTD-GDT ;局部描述符表段的选择子 TempCode_Sel = TempCode-GDT ;临时代码段的选择子 ;---------------------------------------------------------------------------- GDTSeg ENDS ;全局描述符表段定义结束 ;---------------------------------------------------------------------------- DemoLDTSeg SEGMENT PARA USE16 ;局部描述符表数据段(16位) ;---------------------------------------------------------------------------- DemoLDT LABEL BYTE ;局部描述符表 ;0级堆栈段描述符(32位段) DemoStack0 Desc <DemoStack0Len-1,DemoStack0Seg,,ATDW+DPL0,D32,> ;1级堆栈段描述符(32位段) DemoStack1 Desc <DemoStack1Len-1,DemoStack1Seg,,ATDW+DPL1,D32,> ;3级堆栈段描述符(16位段) DemoStack3 Desc <DemoStack3Len-1,DemoStack3Seg,,ATDW+DPL3,,> ;代码段描述符(32位段,DPL=3) DemoCode Desc <DemoCodeLen-1,DemoCodeSeg,,ATCE+DPL3,D32,> ;过渡代码段描述符(32位段) T32Code Desc <T32CodeLen-1,T32CodeSeg,,ATCE,D32,> ;显示子程序代码段描述符(32位段,DPL=1) EchoSubR Desc <EchoSubRLen-1,EchoSubRSeg,,ATCER+DPL1,D32,> ;---------------------------------------------------------------------------- DemoLDNum = ($-DemoLDT)/(SIZE Desc) ;---------------------------------------------------------------------------- ;0级堆栈描述符选择子(RPL=0) DemoStack0_Sel = DemoStack0-DemoLDT+TIL+RPL0 ;1级堆栈描述符选择子(RPL=1) DemoStack1_Sel = DemoStack1-DemoLDT+TIL+RPL1 ;3级堆栈描述符选择子(RPL=3) DemoStack3_Sel = DemoStack3-DemoLDT+TIL+RPL3 ;代码段描述符选择子(RPL=3) DemoCode_Sel = DemoCode-DemoLDT+TIL+RPL3 ;过渡代码段描述符选择子 T32Code_Sel = T32Code-DemoLDT+TIL ;显示子程序代码段描述符选择子(RPL=1) Echo_Sel1 = EchoSubR-DemoLDT+TIL+RPL1 ;显示子程序代码段描述符选择子(RPL=3) Echo_Sel3 = EchoSubR-DemoLDT+TIL+RPL3 ;---------------------------------------------------------------------------- ;指向过渡代码段内T32Begin点的调用门(DPL=0) ToT32GateA Gate <T32Begin,T32Code_Sel,,AT386CGate,> ;指向过渡代码段内T32End点的调用门(DPL=3) ToT32GateB Gate <T32End,T32Code_Sel,,AT386CGate+DPL3,> ;指向显示子程序代码段的调用门(DPL=3) ToEchoGate Gate <EchoSub,Echo_Sel3,,AT386CGate+DPL3,> ;---------------------------------------------------------------------------- DemoLDTLen = $-DemoLDT ;---------------------------------------------------------------------------- ;指向过渡代码段内T32Begin点的调用门的选择子 ToT32A_Sel = ToT32GateA-DemoLDT+TIL ;指向过渡代码段内T32End点的调用门的选择子 ToT32B_Sel = ToT32GateB-DemoLDT+TIL ;显示子程序调用门的选择子 ToEcho_Sel = ToEchoGate-DemoLDT+TIL ;---------------------------------------------------------------------------- DemoLDTSeg ENDS ;局部描述符表段定义结束 ;---------------------------------------------------------------------------- DemoTSSSeg SEGMENT PARA USE16 ;任务状态段TSS ;---------------------------------------------------------------------------- DD 0 ;Back DD DemoStack0Len ;0级堆栈指针 DD DemoStack0_Sel ;初始化 DD DemoStack1Len ;1级堆栈指针 DD DemoStack1_Sel ;初始化 DD 0 ;2级堆栈指针 DD 0 ;未初始化 DD 0 ;CR3 DD 0 ;EIP DD 0 ;EFLAGS DD 0 ;EAX DD 0 ;ECX DD 0 ;EDX DD 0 ;EBX DD 0 ;ESP DD 0 ;EBP DD 0 ;ESI DD 0 ;EDI DD 0 ;ES DD 0 ;CS DD 0 ;SS DD 0 ;DS DD 0 ;FS DD 0 ;GS DD DemoLDT_Sel ;LDT DW 0 ;调试陷阱标志 DW $+2 ;指向I/O许可位图 DW 0ffffh ;I/O许可位图结束标志 ;---------------------------------------------------------------------------- DemoTSSLen = $ ;---------------------------------------------------------------------------- DemoTSSSeg ENDS ;任务状态段TSS结束 ;---------------------------------------------------------------------------- DemoStack0Seg SEGMENT DWORD STACK USE32 ;0级堆栈段(32位段) DemoStack0Len = 512 DB DemoStack0Len DUP(?) DemoStack0Seg ENDS ;0级堆栈段结束 ;---------------------------------------------------------------------------- DemoStack1Seg SEGMENT DWORD STACK USE32 ;1级堆栈段(32位段) DemoStack1Len = 512 DB DemoStack1Len DUP(?) DemoStack1Seg ENDS ;1级堆栈段结束 ;---------------------------------------------------------------------------- DemoStack3Seg SEGMENT DWORD STACK USE16 ;3级堆栈段(16位段) DemoStack3Len = 512 DB DemoStack3Len DUP(?) DemoStack3Seg ENDS ;3级堆栈段结束 ;---------------------------------------------------------------------------- EchoSubRSeg SEGMENT PARA USE32 ;显示子程序代码段(32位,1级) ASSUME CS:EchoSubRSeg ;---------------------------------------------------------------------------- Message DB 'CPL=',0 ;显示信息(该代码段可读) ;---------------------------------------------------------------------------- EchoSub PROC FAR cld push ebp mov ebp,esp mov ax,Echo_Sel1 ;该代码段是可读段 mov ds,ax ;采用RPL=1的选择子 mov ax,Video_Sel mov es,ax mov edi,1996 mov esi,OFFSET Message mov ah,4eh ;置显示属性(红底黄字) EchoSub1: lodsb or al,al jz EchoSub2 stosw jmp EchoSub1 EchoSub2: mov eax,[ebp+8] ;从堆栈中取调用程序的选择子 and al,3 ;调用程序的CPL在CS的RPL字段 add al,'0' mov ah,4eh ;置显示属性(红底黄字) stosw pop ebp retf EchoSub ENDP ;---------------------------------------------------------------------------- EchoSubRLen = $ ;---------------------------------------------------------------------------- EchoSubRSeg ENDS ;显示子程序代码段结束 ;---------------------------------------------------------------------------- DemoCodeSeg SEGMENT PARA USE32 ;32位代码段(3级) ASSUME CS:DemoCodeSeg ;---------------------------------------------------------------------------- DemoBegin PROC FAR CALL32 ToEcho_Sel,0 ;显示当前特权级(变换到1级) CALL32 ToT32B_Sel,0 ;转到过渡代码段(变换到0级) DemoBegin ENDP DemoCodeLen = $ ;---------------------------------------------------------------------------- DemoCodeSeg ENDS ;32位代码段结束 ;---------------------------------------------------------------------------- T32CodeSeg SEGMENT PARA USE32 ;32位过渡代码段(0级) ASSUME CS:T32CodeSeg ;---------------------------------------------------------------------------- T32Begin PROC FAR mov ax,DemoStack0_Sel ;建立0级堆栈 mov ss,ax mov esp,DemoStack0Len push DWORD PTR DemoStack3_Sel ;压入3级堆栈指针 push DemoStack3Len push DWORD PTR DemoCode_SEL ;压入入口点 push OFFSET DemoBegin retf ;利用RET实现转3级的演示代码 T32Begin ENDP ;---------------------------------------------------------------------------- T32End PROC FAR JUMP32 TempCode_Sel,<OFFSET ToReal> T32End ENDP T32CodeLen = $ ;---------------------------------------------------------------------------- T32CodeSeg ENDS ;---------------------------------------------------------------------------- TempCodeSeg SEGMENT PARA USE16 ;16位临时代码段(0级) ASSUME CS:TempCodeSeg ;---------------------------------------------------------------------------- Virtual PROC FAR mov ax,DemoTSS_Sel ;装载TR ltr ax mov ax,DemoLDT_Sel ;装载LDTR lldt ax JUMP16 ToT32A_Sel,0 ;通过调用门转过渡段 ToReal: mov ax,Normal_Sel ;准备切换回实模式 mov ds,ax mov es,ax mov fs,ax mov gs,ax mov ss,ax mov eax,cr0 and al,11111110b mov cr0,eax JUMP16 <SEG Real>,<OFFSET Real> Virtual ENDP TempCodeLen = $ ;---------------------------------------------------------------------------- TempCodeSeg ENDS ;============================================================================ RDataSeg SEGMENT PARA USE16 ;实方式数据段 VGDTR PDesc <GDTLen-1,> ;GDT伪描述符 SPVar DW ? ;用于保存实方式下的SP SSVar DW ? ;用于保存实方式下的SS RDataSeg ENDS ;---------------------------------------------------------------------------- RCodeSeg SEGMENT PARA USE16 ASSUME CS:RCodeSeg,DS:RDataSeg ;---------------------------------------------------------------------------- Start PROC mov ax,RDataSeg mov ds,ax cld CALL InitGDT ;初始化全局描述符表GDT mov ax,DemoLDTSeg mov fs,ax mov si,OFFSET DemoLDT mov cx,DemoLDNum CALL InitLDT ;初始化局部描述符表LDT mov SSVar,ss mov SPVar,sp lgdt QWORD PTR VGDTR ;装载GDTR并切换到保护方式 cli mov eax,cr0 or al,1 mov cr0,eax JUMP16 <TempCode_Sel>,<OFFSET Virtual> Real: mov ax,RDataSeg mov ds,ax lss sp,DWORD PTR SPVar ;又回到实方式 sti mov ax,4c00h int 21h Start ENDP ;---------------------------------------------------------------------------- InitGDT PROC push ds mov ax,GDTSeg mov ds,ax mov cx,GDNum mov si,OFFSET EFFGDT InitG: mov ax,[si].BaseL movzx eax,ax shl eax,4 shld edx,eax,16 mov WORD PTR [si].BaseL,ax mov BYTE PTR [si].BaseM,dl mov BYTE PTR [si].BaseH,dh add si,SIZE Desc loop InitG pop ds mov bx,16 mov ax,GDTSeg mul bx mov WORD PTR VGDTR.Base,ax mov WORD PTR VGDTR.Base+2,dx ret InitGDT ENDP ;---------------------------------------------------------------------------- ;入口参数:FS:SI=第一个要初始化的描述符,CX=要初始化的描述符数 ;---------------------------------------------------------------------------- InitLDT PROC ILDT: mov ax,WORD PTR FS:[si].BaseL movzx eax,ax shl eax,4 shld edx,eax,16 mov WORD PTR fs:[si].BaseL,ax mov BYTE PTR fs:[si].BaseM,dl mov BYTE PTR fs:[si].BaseH,dh add si,SIZE Desc loop ILDT ret InitLDT ENDP ;---------------------------------------------------------------------------- RCodeSeg ENDS END Start 进入讨论组讨论。
粤ICP备06119539号
Copyright CiscoSky.Org,Some Rights Reserved.
Email:me1228#tom.com