zoukankan      html  css  js  c++  java
  • 19 深入特权级转移(中)

     

    参考

    https://www.cnblogs.com/wanmeishenghuo/tag/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/

    https://blog.51cto.com/13475106/category6.html

    处理器通过什么规则判断资源请求或者代码跳转是否合法?

    数据段的访问规则:(数据段无可执行属性)

      访问者权限(CPL)高于或者等于数据段权限(DPL)

      请求特权级(RPL)高于或者等于数据段权限(DPL)

      即(CPL <= DPL)&& (RPL <= DPL)

    上面的公式没有阐述CPL和RPL的关系,这是因为对数据段进行访问的时候CPL和RPL是没有关系的。

    怎么判断一个段是不是数据段呢?

    这就要根据有无可执行属性来判断了,无可执行属性就是数据段了。

    访问示例:

      CPL=2, RPL=1, DPL=3,是否合法?合法

      CPL=0, RPL=3, DPL=2,是否合法?不合法

      CPL=0, RPL=1, DPL=2,是否合法?合法

    合法性的检查发生在对段寄存器赋值的时候,例如mov  ds, ax。

    每个特权级都有不同的栈,每个特权级只能使用自己特权级的栈,例如,0特权级只能使用0特权级的栈,3特权级只能使用3特权级的栈。

    如果0特权级使用2特权级的栈,则在mov  ss, ax的时候会报错。

    对于栈来说段描述符的DPL也要和栈段选择子的RPL相等。

    对于栈段来说:

    当为ss寄存器赋值时,使用规则(CPL==RPL)&& (CPL==DPL)保证特权级的匹配。

    定义栈的时候要让RPL和DPL的值相同。

    示例代码:

    %include "inc.asm"
    
    org 0x9000
    
    jmp ENTRY_SEGMENT
    
    [section .gdt]
    ; GDT definition
    ;                                 段基址,       段界限,       段属性
    GDT_ENTRY       :     Descriptor    0,            0,           0
    CODE32_DESC     :     Descriptor    0,    Code32SegLen - 1,    DA_C + DA_32 + DA_DPL0
    VIDEO_DESC      :     Descriptor 0xB8000,     0x07FFF,         DA_DRWA + DA_32 + DA_DPL3
    DATA32_DESC     :     Descriptor    0,    Data32SegLen - 1,    DA_DR + DA_32 + DA_DPL2
    STACK32_DESC    :     Descriptor    0,     TopOfStack32,       DA_DRW + DA_32 + DA_DPL0
    ; GDT end
    
    GdtLen    equ   $ - GDT_ENTRY
    
    GdtPtr:
              dw   GdtLen - 1
              dd   0
              
              
    ; GDT Selector
    
    Code32Selector     equ (0x0001 << 3) + SA_TIG + SA_RPL0
    VideoSelector      equ (0x0002 << 3) + SA_TIG + SA_RPL3
    Data32Selector     equ (0x0003 << 3) + SA_TIG + SA_RPL1
    Stack32Selector    equ (0x0004 << 3) + SA_TIG + SA_RPL0
    
    ; end of [section .gdt]
    
    
    ; CPL = 2, RPL = 1, DPL = 3, 访问是否合法?   ===> yes
    ; CPL = 0, RPL = 3, DPL = 2, 访问是否合法?   ===> no
    ; CPL = 0, RPL = 1, DPL = 2, 访问是否合法?   ===> yes
    
    
    
    TopOfStack16    equ 0x7c00
    
    [section .s16]
    [bits 16]
    ENTRY_SEGMENT:
        mov ax, cs
        mov ds, ax
        mov es, ax
        mov ss, ax
        mov sp, TopOfStack16
        
        ; initialize GDT for 32 bits code segment
        mov esi, CODE32_SEGMENT
        mov edi, CODE32_DESC
        
        call InitDescItem
        
        mov esi, DATA32_SEGMENT
        mov edi, DATA32_DESC
        
        call InitDescItem
        
        mov esi, STACK32_SEGMENT
        mov edi, STACK32_DESC
    
        call InitDescItem
        
        ; initialize GDT pointer struct
        mov eax, 0
        mov ax, ds
        shl eax, 4
        add eax, GDT_ENTRY
        mov dword [GdtPtr + 2], eax
    
        ; 1. load GDT
        lgdt [GdtPtr]
        
        ; 2. close interrupt
        cli 
        
        ; 3. open A20
        in al, 0x92
        or al, 00000010b
        out 0x92, al
        
        ; 4. enter protect mode
        mov eax, cr0
        or eax, 0x01
        mov cr0, eax
        
        ; 5. jump to 32 bits code
        ; push Stack32Selector   ; 目标栈段选择子
        ; push TopOfStack32      ; 栈顶指针位置
        ; push Code32Selector    ; 目标代码段选择子
        ; push 0                 ; 目标代码段偏移
        ; retf
        jmp word Code32Selector : 0
    
    
    ; esi    --> code segment label
    ; edi    --> descriptor label
    InitDescItem:
        push eax
    
        mov eax, 0
        mov ax, cs
        shl eax, 4
        add eax, esi
        mov word [edi + 2], ax
        shr eax, 16
        mov byte [edi + 4], al
        mov byte [edi + 7], ah
        
        pop eax
        
        ret
    
    [section .dat]
    [bits 32]
    DATA32_SEGMENT:
        DTOS               db  "D.T.OS!", 0
        DTOS_OFFSET        equ DTOS - $$
    
    Data32SegLen equ $ - DATA32_SEGMENT
    
       
    [section .s32]
    [bits 32]
    CODE32_SEGMENT:
        mov ax, VideoSelector
        mov gs, ax
        
        mov ax, Data32Selector
        mov ds, ax
        
        mov ax, Stack32Selector
        mov ss, ax
        
        mov eax, TopOfStack32
        mov esp, eax
        
        mov ebp, DTOS_OFFSET
        mov bx, 0x0C
        mov dh, 12
        mov dl, 33
        
        call PrintString
        
        jmp $
        
    ; ds:ebp    --> string address
    ; bx        --> attribute
    ; dx        --> dh : row, dl : col
    PrintString:
        push ebp
        push eax
        push edi
        push cx
        push dx
        
    print:
        mov cl, [ds:ebp]
        cmp cl, 0
        je end
        mov eax, 80
        mul dh
        add al, dl
        shl eax, 1
        mov edi, eax
        mov ah, bl
        mov al, cl
        mov [gs:edi], ax
        inc ebp
        inc dl
        jmp print
    
    end:
        pop dx
        pop cx
        pop edi
        pop eax
        pop ebp
        
        ret
        
    Code32SegLen    equ    $ - CODE32_SEGMENT
    
    [section .gs]
    [bits 32]
    STACK32_SEGMENT:
        times 1024 * 4 db 0
        
    Stack32SegLen equ $ - STACK32_SEGMENT
    TopOfStack32  equ Stack32SegLen - 1
    

    结论:

    选择子被段寄存器加载时,会进行保护模式的检查

      检查选择子的下标是否合法(段描述符的合法性,前面博客写过)

      检查特权级是否合法,(CPL & RPL <= DPL)

      检查特权级时CPL和RPL之间不会进行比较(仅对普通数据段的访问成立,不包括栈段的使用)

    代码段的分类:

      非系统段(S=1)

        一致性代码段

        非一致性代码段

      系统段(S=0)

        LDT, TSS, 各种门结构

    S是段描述符中的一个位域。

    这里的系统不是指的操作系统,而是处理器本身。

    LDT和TSS是用于多任务实现的结构,一般叫做系统段。

    非系统段就是我们定义的数据段、代码段等。

    代码段又分为两类,一致性代码段和非一致性代码段,这个概念就和段描述符中的TYPE位域有关了。

    TYPE域有5位,A、R、C、X、S。

    一致性代码段:X=1   C=1

    非一致性代码段:X=1  C=0

    一致性和非一致性有什么区别呢?

    代码段之间的跳转规则(我们这里讲解的跳转是不借助门描述符的)

      非一致性代码段

        代码段之间只能平级转移(CPL==DPL, RPL<=DPL)CPL和RPL不用比较

      一致性代码段

        支持低特权级代码向高特权级代码的转移(CPL>=DPL),高跳到低不允许

        虽然可以成功转移到高特权级代码段,但是当前特权级不变,特权级不变,栈就不变

     非一致性代码段能跳转到一致性代码段,可以从低特权级的代码段跳转到高特权级代码段,高跳到低不允许

    一致性代码段能跳到非一致性代码段(这时在一致性代码段中的特权级是由原来跳转到这个代码段时非一致性代码段的特权级决定的,因为上面我们说了特权级不变)

    数据段只有一种,没有一致性和非一致性的区分,并且数据段不允许被低特权级的代码段访问。

    降特权级跳转(retf)时,目标代码段特权级与目标栈段特权级必须完全相同,即(ss.rpl==cs.rpl)&& (ss.dpl==cs.rpl)

    示例代码:

    %include "inc.asm"
    
    org 0x9000
    
    jmp ENTRY_SEGMENT
    
    [section .gdt]
    ; GDT definition
    ;                                 段基址,       段界限,       段属性
    GDT_ENTRY       :     Descriptor    0,            0,           0
    CODE32_DESC     :     Descriptor    0,    Code32SegLen - 1,    DA_C + DA_32 + DA_DPL1
    VIDEO_DESC      :     Descriptor 0xB8000,     0x07FFF,         DA_DRWA + DA_32 + DA_DPL2
    DATA32_DESC     :     Descriptor    0,    Data32SegLen - 1,    DA_DR + DA_32 + DA_DPL2
    STACK32_DESC    :     Descriptor    0,     TopOfStack32,       DA_DRW + DA_32 + DA_DPL1
    FUNCTION_DESC   :     Descriptor    0,   FunctionSegLen - 1,   DA_C + DA_32 + DA_DPL1
    NEW_DESC        :     Descriptor    0,      NewSegLen - 1,     DA_CCO + DA_32 + DA_DPL0
    ; GDT end
    
    GdtLen    equ   $ - GDT_ENTRY
    
    GdtPtr:
              dw   GdtLen - 1
              dd   0
              
              
    ; GDT Selector
    
    Code32Selector     equ (0x0001 << 3) + SA_TIG + SA_RPL1
    VideoSelector      equ (0x0002 << 3) + SA_TIG + SA_RPL2
    Data32Selector     equ (0x0003 << 3) + SA_TIG + SA_RPL2
    Stack32Selector    equ (0x0004 << 3) + SA_TIG + SA_RPL1
    FunctionSelector   equ (0x0005 << 3) + SA_TIG + SA_RPL1
    NewSelector        equ (0x0006 << 3) + SA_TIG + SA_RPL0
    
    ; end of [section .gdt]
    
    
    
    TopOfStack16    equ 0x7c00
    
    [section .s16]
    [bits 16]
    ENTRY_SEGMENT:
        mov ax, cs
        mov ds, ax
        mov es, ax
        mov ss, ax
        mov sp, TopOfStack16
        
        ; initialize GDT for 32 bits code segment
        mov esi, CODE32_SEGMENT
        mov edi, CODE32_DESC
        
        call InitDescItem
        
        mov esi, DATA32_SEGMENT
        mov edi, DATA32_DESC
        
        call InitDescItem
        
        mov esi, STACK32_SEGMENT
        mov edi, STACK32_DESC
        
        call InitDescItem
        
        mov esi, FUNCTION_SEGMENT
        mov edi, FUNCTION_DESC
        
        call InitDescItem
        
        mov esi, NEW_SEGMENT
        mov edi, NEW_DESC
        
        call InitDescItem
        
        ; initialize GDT pointer struct
        mov eax, 0
        mov ax, ds
        shl eax, 4
        add eax, GDT_ENTRY
        mov dword [GdtPtr + 2], eax
    
        ; 1. load GDT
        lgdt [GdtPtr]
        
        ; 2. close interrupt
        cli 
        
        ; 3. open A20
        in al, 0x92
        or al, 00000010b
        out 0x92, al
        
        ; 4. enter protect mode
        mov eax, cr0
        or eax, 0x01
        mov cr0, eax
        
        ; 5. jump to 32 bits code
        push Stack32Selector   ; 目标栈段选择子
        push TopOfStack32      ; 栈顶指针位置
        push Code32Selector    ; 目标代码段选择子
        push 0                 ; 目标代码段偏移
        retf
    
    
    ; esi    --> code segment label
    ; edi    --> descriptor label
    InitDescItem:
        push eax
    
        mov eax, 0
        mov ax, cs
        shl eax, 4
        add eax, esi
        mov word [edi + 2], ax
        shr eax, 16
        mov byte [edi + 4], al
        mov byte [edi + 7], ah
        
        pop eax
        
        ret
    
    [section .dat]
    [bits 32]
    DATA32_SEGMENT:
        DTOS               db  "D.T.OS!", 0
        DTOS_OFFSET        equ DTOS - $$
    
    Data32SegLen equ $ - DATA32_SEGMENT
    
       
    [section .s32]
    [bits 32]
    CODE32_SEGMENT:
        mov ax, VideoSelector
        mov gs, ax
        
        mov ax, Data32Selector
        mov ds, ax
        
        mov ax, Stack32Selector
        mov ss, ax
        
        mov eax, TopOfStack32
        mov esp, eax
        
        ; mov ebp, DTOS_OFFSET
        ; mov bx, 0x0C
        ; mov dh, 12
        ; mov dl, 33
        
        ; call FunctionSelector : PrintString
        
        jmp NewSelector : 0
        
    Code32SegLen    equ    $ - CODE32_SEGMENT
    
    [section .new]
    [bits 32]
    NEW_SEGMENT:
        mov ebp, DTOS_OFFSET
        mov bx, 0x0C
        mov dh, 12
        mov dl, 33
        
        call FunctionSelector : PrintString
        
        jmp $
        
    NewSegLen    equ    $ - NEW_SEGMENT
    
    [section .func]
    [bits 32]
    FUNCTION_SEGMENT:
    
    ; ds:ebp    --> string address
    ; bx        --> attribute
    ; dx        --> dh : row, dl : col
    PrintStringFunc:
        push ebp
        push eax
        push edi
        push cx
        push dx
        
    print:
        mov cl, [ds:ebp]
        cmp cl, 0
        je end
        mov eax, 80
        mul dh
        add al, dl
        shl eax, 1
        mov edi, eax
        mov ah, bl
        mov al, cl
        mov [gs:edi], ax
        inc ebp
        inc dl
        jmp print
    
    end:
        pop dx
        pop cx
        pop edi
        pop eax
        pop ebp
        
        retf
        
    PrintString    equ   PrintStringFunc - $$
    
    FunctionSegLen    equ   $ - FUNCTION_SEGMENT
    
    [section .gs]
    [bits 32]
    STACK32_SEGMENT:
        times 1024 * 4 db 0
        
    Stack32SegLen equ $ - STACK32_SEGMENT
    TopOfStack32  equ Stack32SegLen - 1
    

    结论:

      特权级降低转移时,retf指令会触发栈段的特权级检查(通过选择子中的rpl进行检查)

      一致性代码段可以直接跳转到其他同级非一致性代码段执行

    一致性代码段和非一致性代码段中的代码没有本质的区别,这两种代码段仅仅是跳转时使用的合法性判断规则不同,因此,一致性代码段到非一致性代码段的直接同级跳转是合法的。

    小技巧:

      大多数情况下,选择子中的RPL和对应段描述符中的DPL可设置为相同值。

    小结:

      CPL、RPL和DPL是处理器进行特权级保护的依据。

      对于数据段,CPL<=DPL,RPL<=DPL

      对于非一致性代码段,CPL==DPL,RPL<=DPL

      对于一致性代码段,CPL>=DPL,转以后CPL不变。

      

      

  • 相关阅读:
    Linux查看用于终止进程命令
    Linux查看当前正在运行的进程
    Windows 和 Linux 平台下的端口转发工具
    Windows 和 Linux 平台下的端口转发工具
    linux下最简单的端口转发工具
    linux下最简单的端口转发工具
    try与finally块中return的问题
    try与finally块中return的问题
    为啥还要写呢?——北漂18年序言
    JavaScript DOM对象和JQuery对象相互转换
  • 原文地址:https://www.cnblogs.com/lh03061238/p/14153571.html
Copyright © 2011-2022 走看看