码迷,mamicode.com
首页 > 其他好文 > 详细

17 保护模式中的特权级(下)

时间:2020-12-18 13:23:41      阅读:5      评论:0      收藏:0      [点我收藏+]

标签:ret   指令   efi   gpo   返回   ddr   ade   label   ini   

参考

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

 

问题:

  使用调用门如何实现不同特权级代码之间的跳转(如:从高特权级到低特权级)?

不幸的事实:

  调用门只支持从低特权级到高特权级执行

  无法利用调用门从高特权级到低特权级执行

 

从高特权级的代码段通过return  far可以返回到低特权级的代码段执行。这时return  far是一个跳转指令,完成从高特权级到低特权级的跳转,这正是我们想要的。

return的本质是做跳转的,而不是我们根深蒂固的做返回的。只是最常用的方式是做返回使用。

思路整理:

  调用门的特权级跳转:

    1、通过远调用(call  far),低特权级 -> 高特权级

    2、通过远返回(retf),高特权级 -> 低特权级

 

retf的本质就是恢复cs和eip的值,因此,我们需要首先将cs和eip的值放入栈中。

需要提前知道的事实:

  x86处理器对于不同的特权级需要使用不同的栈

  每一个特权级对应一个私有的栈(最多四个栈)

  特权级跳转变化之前必须指定好相应的栈

解决方案(高特权级 -> 低特权级)

  1、指定目标栈段选择子(push)

  2、指定栈顶指针位置(push)

  3、指定目标代码段选择子(push)

  4、指定目标代码段偏移(push)

  5、跳转(retf)

 

实验1:

%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_DPL3
VIDEO_DESC      :     Descriptor 0xB8000,       0x07FFF,       DA_DRWA + DA_32 + DA_DPL3
DATA32_DESC     :     Descriptor    0,    Data32SegLen  - 1,   DA_DR + DA_32 + DA_DPL3
STACK32_DESC    :     Descriptor    0,      TopOfStack16,    DA_DRW + DA_32 + DA_DPL3
; GDT end

GdtLen    equ   $ - GDT_ENTRY

GdtPtr:
          dw   GdtLen - 1
          dd   0


; GDT Selector

Code32Selector    equ (0x0001 << 3) + SA_TIG + SA_RPL3
VideoSelector     equ (0x0002 << 3) + SA_TIG + SA_RPL3
Data32Selector    equ (0x0003 << 3) + SA_TIG + SA_RPL3
Stack32Selector   equ (0x0004 << 3) + SA_TIG + SA_RPL3

; 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

    ; 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
    ; jmp dword Code32Selector : 0
    push Stack32Selector    ; mu biao zhan duan xuan ze zi
    push TopOfStack32       ; zhan ding zhi zhen wei zhi
    push Code32Selector     ; mu biao dai ma duan xuan ze zi
    push 0                     ; mu bioa dai ma duan pian yi
    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 ax, Data32Selector
    mov ds, ax

    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

11-14行我们给每一个段加上了特权级DA_DPL3,同时26-29行也必须加上DA_RPL3。85-89是我们新添加的代码,运行结果如下:

技术图片

成功的从高特权级跳到了低特权级。

 

 将程序改成下面的样子可以得到同样的结果:

%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_DPL0
DATA32_DESC     :     Descriptor    0,    Data32SegLen  - 1,   DA_DR + DA_32 + DA_DPL0
STACK32_DESC    :     Descriptor    0,      TopOfStack16,    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_RPL0
Data32Selector    equ (0x0003 << 3) + SA_TIG + SA_RPL0
Stack32Selector   equ (0x0004 << 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

    ; 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
    ; jmp dword Code32Selector : 0
    ; push Stack32Selector    ; mu biao zhan duan xuan ze zi
    ; push TopOfStack32       ; zhan ding zhi zhen wei zhi
    push Code32Selector     ; mu biao dai ma duan xuan ze zi
    push 0                     ; mu bioa dai ma duan pian yi
    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 ax, Data32Selector
    mov ds, ax

    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

这个实验告诉我们:

1、retf就是一个跳转指令,87-89行的代码与84行是等价的。

2、在相同的特权级之间跳转时不需要栈发生变化的。

3、特权级改变时一定要指定栈,要不然程序就会发生崩溃

 

单步实验:

 首先用ndisasm -o 0x9000 loader > loader.txt进行反汇编,找到retf的断点位置0x90A6。

启动bochs开始执行。

运行到0x90A6时结果如下:

技术图片

可以看到此时cs寄存器的最后两位是0,这正是默认的特权级,继续执行。

执行了retf过后,结果如下:

技术图片

此时cs的最后一个字节为b,可以算出最后两位为11,确实跳转到了特权级为3的代码段了。

 

小结:

调用门只支持从低特权级跳转到高特权级

利用远返回(retf)可以从高特权级转移到低特权级

x86处理器每一个特权级对应一个私有的栈

特权级跳转变化之前必须指定好相应的栈

  

  

17 保护模式中的特权级(下)

标签:ret   指令   efi   gpo   返回   ddr   ade   label   ini   

原文地址:https://www.cnblogs.com/lh03061238/p/14131752.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!