• 高特权级代码段转向低特权级代码段(利用 ret(retf) 指令实现 jmp from ring0 to ring3)


    【0】写在前面

    • 0.1)本代码旨在演示 从 ring0 转移到 ring3(即,从高特权级 转移到 低特权级)
    • 0.2)本文 只对 与 门相关的 代码进行简要注释,言简意赅;
    • 0.3)文末的个人总结是干货,前面代码仅供参考的,且source code from orange’s implemention of a os.

    ; ==========================================
    ; pmtest5a.asm
    ; 编译方法:nasm pmtest5a.asm -o pmtest5a.com
    ; ==========================================
    
    %include    "pm.inc"    ; 常量, 宏, 以及一些说明
    
    org 0100h
     jmp    LABEL_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; 非一致代码段,32
    LABEL_DESC_CODE16:     Descriptor 0,           0ffffh, DA_C      ; 非一致代码段,16
    LABEL_DESC_CODE_DEST:  Descriptor 0, SegCodeDestLen-1, DA_C+DA_32; 非一致代码段,32
    

    ; ring3的代码段描述符的定义 [add]

    LABEL_DESC_CODE_RING3: Descriptor 0,SegCodeRing3Len-1, DA_C+DA_32+DA_DPL3
    LABEL_DESC_DATA:       Descriptor 0,        DataLen-1, DA_DRW    ; Data
    LABEL_DESC_STACK:      Descriptor 0,       TopOfStack, DA_DRWA+DA_32;Stack, 32 位
    

    ; ring3的堆栈段描述符的定义 [add]

    LABEL_DESC_STACK3:     Descriptor 0,      TopOfStack3, DA_DRWA+DA_32+DA_DPL3
    LABEL_DESC_LDT:        Descriptor 0,         LDTLen-1, DA_LDT    ; LDT
    

    ; ring3的视频段描述符的DPL属性设置为 3 [add]

    LABEL_DESC_VIDEO:      Descriptor 0B8000h,     0ffffh, DA_DRW+DA_DPL3
    
    ; 门                               目标选择子,偏移,DCount, 属性
    LABEL_CALL_GATE_TEST: Gate SelectorCodeDest,   0,     0, DA_386CGate+DA_DPL0
    ; GDT 结束
    
    GdtLen   equ    $ - LABEL_GDT  ; GDT长度
    GdtPtr   dw GdtLen - 1  ; GDT界限
      dd    0    ; GDT基地址
    
    ; GDT 选择子
    SelectorNormal   equ    LABEL_DESC_NORMAL   - LABEL_GDT
    SelectorCode32   equ    LABEL_DESC_CODE32   - LABEL_GDT
    SelectorCode16   equ    LABEL_DESC_CODE16   - LABEL_GDT
    SelectorCodeDest    equ LABEL_DESC_CODE_DEST    - LABEL_GDT
    

    ; ring3的代码段描述符的选择子定义 [add]

    SelectorCodeRing3   equ LABEL_DESC_CODE_RING3   - LABEL_GDT + SA_RPL3
    
    SelectorData     equ    LABEL_DESC_DATA  - LABEL_GDT
    SelectorStack    equ    LABEL_DESC_STACK    - LABEL_GDT
    

    ; ring3的堆栈段描述符的选择子定义 [add]

    SelectorStack3   equ    LABEL_DESC_STACK3   - LABEL_GDT + SA_RPL3
    
    SelectorLDT  equ    LABEL_DESC_LDT   - LABEL_GDT
    

    ;ring3的视频段描述符(DPL==3)的选择子定义 [add]

    SelectorVideo    equ    LABEL_DESC_VIDEO    - LABEL_GDT
    
    SelectorCallGateTest    equ LABEL_CALL_GATE_TEST    - LABEL_GDT
    ; END of [SECTION .gdt]
    

    [SECTION .data1] ; 数据段

    ALIGN   32
    [BITS   32]
    LABEL_DATA:
    SPValueInRealMode   dw  0
    ; 字符串
    PMMessage:   db "In Protect Mode now. ^-^", 0   ; 进入保护模式后显示此字符串
    OffsetPMMessage  equ    PMMessage - $$
    	StrTest:	 db	"ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0
    	OffsetStrTest	 equ	StrTest - $$
    DataLen  equ    $ - LABEL_DATA
    ; END of [SECTION .data1]
    

    ; 全局堆栈段
    [SECTION .gs]

    ALIGN   32
    [BITS   32]
    LABEL_STACK:
     times 512 db 0
    
    TopOfStack  equ $ - LABEL_STACK - 1
    
    ; END of [SECTION .gs]
    

    ; 堆栈段ring3的定义
    [SECTION .s3]

    ALIGN   32
    [BITS   32]
    LABEL_STACK3:
     times 512 db 0
    TopOfStack3 equ $ - LABEL_STACK3 - 1
    ; END of [SECTION .s3]
    

    [SECTION .s16]

    [BITS   16]
    LABEL_BEGIN:
     mov    ax, cs
     mov    ds, ax
     mov    es, ax
     mov    ss, ax
     mov    sp, 0100h
    
     mov    [LABEL_GO_BACK_TO_REAL+3], ax
     mov    [SPValueInRealMode], sp
    
     ; 初始化 16 位代码段描述符
     mov    ax, cs
     movzx  eax, ax
     shl    eax, 4
     add    eax, LABEL_SEG_CODE16
     mov    word [LABEL_DESC_CODE16 + 2], ax
     shr    eax, 16
     mov    byte [LABEL_DESC_CODE16 + 4], al
     mov    byte [LABEL_DESC_CODE16 + 7], ah
    
     ; 初始化 32 位代码段描述符
     xor    eax, eax
     mov    ax, cs
     shl    eax, 4
     add    eax, LABEL_SEG_CODE32
     mov    word [LABEL_DESC_CODE32 + 2], ax
     shr    eax, 16
     mov    byte [LABEL_DESC_CODE32 + 4], al
     mov    byte [LABEL_DESC_CODE32 + 7], ah
    
     ; 初始化测试调用门的代码段描述符
     xor    eax, eax
     mov    ax, cs
     shl    eax, 4
     add    eax, LABEL_SEG_CODE_DEST
     mov    word [LABEL_DESC_CODE_DEST + 2], ax
     shr    eax, 16
     mov    byte [LABEL_DESC_CODE_DEST + 4], al
     mov    byte [LABEL_DESC_CODE_DEST + 7], ah
    
     ; 初始化数据段描述符
     xor    eax, eax
     mov    ax, ds
     shl    eax, 4
     add    eax, LABEL_DATA
     mov    word [LABEL_DESC_DATA + 2], ax
     shr    eax, 16
     mov    byte [LABEL_DESC_DATA + 4], al
     mov    byte [LABEL_DESC_DATA + 7], ah
    
     ; 初始化堆栈段描述符
     xor    eax, eax
     mov    ax, ds
     shl    eax, 4
     add    eax, LABEL_STACK
     mov    word [LABEL_DESC_STACK + 2], ax
     shr    eax, 16
     mov    byte [LABEL_DESC_STACK + 4], al
     mov    byte [LABEL_DESC_STACK + 7], ah
    

    ; 初始化堆栈段描述符(Ring3) [add]

     xor    eax, eax
     mov    ax, ds
     shl    eax, 4
     add    eax, LABEL_STACK3
     mov    word [LABEL_DESC_STACK3 + 2], ax
     shr    eax, 16
     mov    byte [LABEL_DESC_STACK3 + 4], al
     mov    byte [LABEL_DESC_STACK3 + 7], ah
    
     ; 初始化 LDT 在 GDT 中的描述符
     xor    eax, eax
     mov    ax, ds
     shl    eax, 4
     add    eax, LABEL_LDT
     mov    word [LABEL_DESC_LDT + 2], ax
     shr    eax, 16
     mov    byte [LABEL_DESC_LDT + 4], al
     mov    byte [LABEL_DESC_LDT + 7], ah
    
     ; 初始化 LDT 中的描述符
     xor    eax, eax
     mov    ax, ds
     shl    eax, 4
     add    eax, LABEL_CODE_A
     mov    word [LABEL_LDT_DESC_CODEA + 2], ax
     shr    eax, 16
     mov    byte [LABEL_LDT_DESC_CODEA + 4], al
     mov    byte [LABEL_LDT_DESC_CODEA + 7], ah
    

    ; 初始化Ring3描述符 [add]

     xor    eax, eax
     mov    ax, ds
     shl    eax, 4
     add    eax, LABEL_CODE_RING3
     mov    word [LABEL_DESC_CODE_RING3 + 2], ax
     shr    eax, 16
     mov    byte [LABEL_DESC_CODE_RING3 + 4], al
     mov    byte [LABEL_DESC_CODE_RING3 + 7], ah
    
     ; 为加载 GDTR 作准备
     xor    eax, eax
     mov    ax, ds
     shl    eax, 4
     add    eax, LABEL_GDT   ; eax <- gdt 基地址
     mov    dword [GdtPtr + 2], eax ; [GdtPtr + 2] <- gdt 基地址
    
     ; 加载 GDTR
     lgdt   [GdtPtr]
    
     ; 关中断
     cli
    
     ; 打开地址线A20
     in al, 92h
     or al, 00000010b
     out    92h, al
    
     ; 准备切换到保护模式
     mov    eax, cr0
     or eax, 1
     mov    cr0, eax
    
     ; 真正进入保护模式
     jmp    dword SelectorCode32:0  ; 执行这一句会把 SelectorCode32 装入 cs, 并跳转到 Code32Selector:0  处
    
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    
    LABEL_REAL_ENTRY:    ; 从保护模式跳回到实模式就到了这里
     mov    ax, cs
     mov    ds, ax
     mov    es, ax
     mov    ss, ax
    
     mov    sp, [SPValueInRealMode]
    
     in al, 92h  ; ┓
     and    al, 11111101b   ; ┣ 关闭 A20 地址线
     out    92h, al  ; ┛
    
     sti     ; 开中断
    
     mov    ax, 4c00h   ; ┓
     int    21h  ; ┛回到 DOS
    ; END of [SECTION .s16]
    

    [SECTION .s32]; 32 位代码段. 由实模式跳入.

    [BITS   32]
    
    LABEL_SEG_CODE32:
     mov    ax, SelectorData
     mov    ds, ax   ; 数据段选择子
     mov    ax, SelectorVideo
     mov    gs, ax   ; 视频段选择子
    
     mov    ax, SelectorStack
     mov    ss, ax   ; 堆栈段选择子
    
     mov    esp, TopOfStack
    
    
     ; 下面显示一个字符串
     mov    ah, 0Ch  ; 0000: 黑底    1100: 红字
     xor    esi, esi
     xor    edi, edi
     mov    esi, OffsetPMMessage    ; 源数据偏移
     mov    edi, (80 * 10 + 0) * 2  ; 目的数据偏移。屏幕第 10 行, 第 0 列。
     cld
    .1:
     lodsb
     test   al, al
     jz .2
     mov    [gs:edi], ax
     add    edi, 2
     jmp    .1
    .2: ; 显示完毕
    
     call   DispReturn
    

    ; ; 以下代码分步骤演示了如何将A的堆栈拷贝到B的堆栈(A==调用者,B=被调用者)
    ; 1.将ring3的堆栈段描述符的选择子压栈 [add]

     push   SelectorStack3
    

    ; 2.将ring3的堆栈压栈 [add]

     push   TopOfStack3 
    

    ; 3.将ring3的堆栈值 压栈 [add]

     push   SelectorCodeRing3
     push   0   
    

    ; 4.retf 弹出ip + 弹出cs;此时ip==0, cs==SelectorCodeRing3,然后CPU自动解析出SelectorCodeRing3 索引(定位)的段描述符, 该描述符存储有
    ; 4.ring3下代码段LABEL_CODE_RING3 的基地址, 让我们转向 代码末尾的 LABEL_CODE_RING3 代码段吧;

     retf
    
     ; 测试调用门(无特权级变换),将打印字母 'C'
     call   SelectorCallGateTest:0
     ;call  SelectorCodeDest:0
    
     ; Load LDT
     mov    ax, SelectorLDT
     lldt   ax
    
     jmp    SelectorLDTCodeA:0  ; 跳入局部任务,将打印字母 'L'。
    
    ; ------------------------------------------------------------------------
    DispReturn:
     push   eax
     push   ebx
     mov    eax, edi
     mov    bl, 160
     div    bl
     and    eax, 0FFh
     inc    eax
     mov    bl, 160
     mul    bl
     mov    edi, eax
     pop    ebx
     pop    eax
    
     ret
    ; DispReturn 结束---------------------------------------------------------
    
    SegCode32Len    equ $ - LABEL_SEG_CODE32
    ; END of [SECTION .s32]
    

    [SECTION .sdest]; 调用门目标段

    [BITS   32]
    
    LABEL_SEG_CODE_DEST:
     mov    ax, SelectorVideo
     mov    gs, ax   ; 视频段选择子(目的)
    
     mov    edi, (80 * 12 + 0) * 2  ; 屏幕第 12 行, 第 0 列。
     mov    ah, 0Ch  ; 0000: 黑底    1100: 红字
     mov    al, 'C'
     mov    [gs:edi], ax
    
     retf
    
    SegCodeDestLen  equ $ - LABEL_SEG_CODE_DEST
    ; END of [SECTION .sdest]
    

    ; 16 位代码段. 由 32 位代码段跳入, 跳出后到实模式
    [SECTION .s16code]

    ALIGN   32
    [BITS   16]
    LABEL_SEG_CODE16:
     ; 跳回实模式:
     mov    ax, SelectorNormal
     mov    ds, ax
     mov    es, ax
     mov    fs, ax
     mov    gs, ax
     mov    ss, ax
    
     mov    eax, cr0
     and    al, 11111110b
     mov    cr0, eax
    
    LABEL_GO_BACK_TO_REAL:
     jmp    0:LABEL_REAL_ENTRY  ; 段地址会在程序开始处被设置成正确的值
    
    Code16Len   equ $ - LABEL_SEG_CODE16
    
    ; END of [SECTION .s16code]
    

    ; LDT
    [SECTION .ldt]

    ALIGN   32
    LABEL_LDT:
    ;                                         段基址       段界限     ,   属性
    LABEL_LDT_DESC_CODEA:   Descriptor         0,     CodeALen - 1,   DA_C + DA_32  ; Code, 32 位
    
    LDTLen   equ    $ - LABEL_LDT
    
    ; LDT 选择子
    SelectorLDTCodeA    equ LABEL_LDT_DESC_CODEA    - LABEL_LDT + SA_TIL
    ; END of [SECTION .ldt]
    

    ; CodeA (LDT, 32 位代码段)
    [SECTION .la]

    ALIGN   32
    [BITS   32]
    LABEL_CODE_A:
     mov    ax, SelectorVideo
     mov    gs, ax   ; 视频段选择子(目的)
    
     mov    edi, (80 * 13 + 0) * 2  ; 屏幕第 13 行, 第 0 列。
     mov    ah, 0Ch  ; 0000: 黑底    1100: 红字
     mov    al, 'L'
     mov    [gs:edi], ax
    
     ; 准备经由16位代码段跳回实模式
     jmp    SelectorCode16:0
    CodeALen    equ $ - LABEL_CODE_A
    ; END of [SECTION .la]
    

    ; CodeRing3的定义(ring3下的代码段)
    ; 显然, ring3 中的任务执行完后, jmp $ 这句代码使得程序 就会 永久停留在该处;

    [SECTION .ring3]
    ALIGN   32
    [BITS   32]
    LABEL_CODE_RING3:
     mov    ax, SelectorVideo
     mov    gs, ax
    
     mov    edi, (80 * 14 + 0) * 2
     mov    ah, 0Ch
     mov    al, '3'
     mov    [gs:edi], ax
    
     jmp    $
    SegCodeRing3Len equ $ - LABEL_CODE_RING3
    ; END of [SECTION .ring3]
    



    【总结-Conclusion】

    • C1)实现ring0 jmp 到ring3, 使用的是ret 指令,代码如下:(核心代码)

      ; 以下代码分步骤演示了如何将A的堆栈拷贝到B的堆栈(A==调用者,B=被调用者)
      ; 1.将ring3的堆栈段描述符的选择子压栈 [add](堆栈段描述符 存储有新堆栈的基地址ss)

      push    SelectorStack3
      

      ; 2.将ring3的堆栈压栈[add](新栈顶指针esp压栈)

      push    TopOfStack3
      

      ; 3.将ring3的堆栈值 压栈 [add] (将ring3 下的任务代码段基地址cs 压栈)

      push    SelectorCodeRing3
      ;   (偏移量ip == 0 压栈)
       push   0  
      

    ; 4.retf 弹出ip + 弹出cs;此时ip==0, cs==SelectorCodeRing3,然后CPU自动解析出SelectorCodeRing3 索引(定位)的段描述符, 该描述符存储有
    ; 4.ring3下代码段LABEL_CODE_RING3 的基地址;

     retf
    

    要知道: retf == [ pop cs, pop ip ] , ret == pop ip

    • C2)指令ret 用于执行近返回、同特权级远返回和不同特权级的远返回;
    • C2.1)近返回仅在当前代码段中转移程序控制权, 因此CPU 仅仅进行界限检查;
    • C2.2)对于同特权级远返回, CPU同时从堆栈中弹出返回代码段的选择子和返回指令指针;
    • C2.3)会发生特权级改变的远返回仅允许返回到低特权级程序中, 即返回到的代码段DPL要大于CPL;

    • 当执行远返回时, CPU执行以下步骤(也即是上述过程的第4步——指令retf):

    • 1)检查保存的cs 中的RPL 以判断返回时是否要变换特权级;
    • 2)加载被调用者堆栈上的cs 和eip;
    • 3)如果ret 指令含有参数,则增加esp 跳过参数,然后esp 将指向被保存过的调用者ss 和 esp ;ret的参数个数对应 调用门中的 Param Count的值;
    • 4)加载ss 和 esp , 切换到调用者堆栈,被调用者的ss 和 esp 被丢弃;
    • 5)如果ret 指令含有参数, 增加esp 的值以跳过参数;
    • 6)检查ds、es、fs、gs的值,如果其中哪一个寄存器指向的段的DPL 小于CPL(此规则不适用于一致代码段),那么一个空描述符会被加载到该寄存器;


      这里写图片描述
  • 相关阅读:
    字母次数
    hdu 2051 Bitset(十进制到二进制)
    练习1升级
    实验一写能自动生成小学四则运算题目的程序
    TCP/IP bad check sum
    Lua GC 之 Ephemeron
    RHEL6下VNC安装和配置
    qpid安装
    关闭中国电信无线客户端自动更新
    Python GC
  • 原文地址:https://www.cnblogs.com/pacoson/p/4893162.html
Copyright © 2020-2023  润新知