第十七课 保护模式中的特权级(下)

问题:

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

不幸的事实:

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

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

从高特权级的代码段通过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:

  1 %include "inc.asm"
  2 
  3 org 0x9000
  4 
  5 jmp ENTRY_SEGMENT
  6  
  7 [section .gdt]
  8 ; GDT definition
  9 ;                                 段基址,       段界限,       段属性
 10 GDT_ENTRY       :     Descriptor    0,            0,           0
 11 CODE32_DESC     :     Descriptor    0,    Code32SegLen  - 1,   DA_C + DA_32 + DA_DPL3
 12 VIDEO_DESC      :     Descriptor 0xB8000,       0x07FFF,       DA_DRWA + DA_32 + DA_DPL3
 13 DATA32_DESC     :     Descriptor    0,    Data32SegLen  - 1,   DA_DR + DA_32 + DA_DPL3
 14 STACK32_DESC    :     Descriptor    0,      TopOfStack16,    DA_DRW + DA_32 + DA_DPL3       
 15 ; GDT end
 16 
 17 GdtLen    equ   $ - GDT_ENTRY
 18 
 19 GdtPtr:
 20           dw   GdtLen - 1
 21           dd   0
 22           
 23           
 24 ; GDT Selector
 25 
 26 Code32Selector    equ (0x0001 << 3) + SA_TIG + SA_RPL3
 27 VideoSelector     equ (0x0002 << 3) + SA_TIG + SA_RPL3
 28 Data32Selector    equ (0x0003 << 3) + SA_TIG + SA_RPL3
 29 Stack32Selector   equ (0x0004 << 3) + SA_TIG + SA_RPL3
 30 
 31 ; end of [section .gdt]
 32 
 33 TopOfStack16    equ  0x7c00
 34 
 35 [section .s16]
 36 [bits 16]
 37 ENTRY_SEGMENT:
 38     mov ax, cs
 39     mov ds, ax
 40     mov es, ax
 41     mov ss, ax
 42     mov sp, TopOfStack16
 43     
 44     ; initialize GDT for 32 bits code segment
 45     mov esi, CODE32_SEGMENT
 46     mov edi, CODE32_DESC
 47     
 48     call InitDescItem
 49     
 50     mov esi, DATA32_SEGMENT
 51     mov edi, DATA32_DESC
 52     
 53     call InitDescItem
 54     
 55     mov esi, STACK32_SEGMENT
 56     mov edi, STACK32_DESC
 57     
 58     call InitDescItem
 59     
 60     ; initialize GDT pointer struct
 61     mov eax, 0
 62     mov ax, ds
 63     shl eax, 4
 64     add eax, GDT_ENTRY
 65     mov dword [GdtPtr + 2], eax
 66 
 67     ; 1. load GDT
 68     lgdt [GdtPtr]
 69     
 70     ; 2. close interrupt
 71     cli 
 72     
 73     ; 3. open A20
 74     in al, 0x92
 75     or al, 00000010b
 76     out 0x92, al
 77     
 78     ; 4. enter protect mode
 79     mov eax, cr0
 80     or eax, 0x01
 81     mov cr0, eax
 82     
 83     ; 5. jump to 32 bits code
 84     ; jmp dword Code32Selector : 0
 85     push Stack32Selector    ; mu biao zhan duan xuan ze zi
 86     push TopOfStack32       ; zhan ding zhi zhen wei zhi
 87     push Code32Selector     ; mu biao dai ma duan xuan ze zi
 88     push 0                     ; mu bioa dai ma duan pian yi
 89     retf
 90     
 91 
 92     
 93 ; esi    --> code segment label
 94 ; edi    --> descriptor label
 95 InitDescItem:
 96     push eax
 97     
 98     mov eax, 0
 99     mov ax, cs
100     shl eax, 4
101     add eax, esi
102     mov word [edi + 2], ax
103     shr eax, 16
104     mov byte [edi + 4], al
105     mov byte [edi + 7], ah
106     
107     pop eax
108     
109     ret
110     
111 [section .dat]
112 [bits 32]
113 DATA32_SEGMENT:
114     DTOS                db    "D.T.OS!",0
115     DTOS_OFFSET            equ    DTOS - $$
116     
117 Data32SegLen    equ        $ - DATA32_SEGMENT  
118 
119   
120 [section .s32]
121 [bits 32]
122 CODE32_SEGMENT:
123     mov ax, VideoSelector
124     mov gs, ax
125     
126     mov ax, Data32Selector
127     mov ds, ax
128     
129     mov ax, Stack32Selector
130     mov ss, ax
131     
132     mov ax, Data32Selector
133     mov ds, ax
134     
135     mov ebp, DTOS_OFFSET
136     mov bx, 0x0C
137     mov dh, 12
138     mov dl, 33
139     
140     call PrintString
141     
142     jmp $
143 
144 
145 ; ds:ebp   --> string address
146 ; bx       --> attribute
147 ; dx       --> dh : row, dl : col
148 PrintString:
149     push ebp
150     push eax
151     push edi 
152     push cx
153     push dx
154     
155 print:
156     mov cl, [ds:ebp]
157     cmp cl, 0
158     je end
159     mov eax, 80
160     mul dh
161     add al, dl
162     shl eax, 1
163     mov edi, eax
164     mov ah, bl
165     mov al, cl
166     mov [gs:edi], ax
167     inc ebp
168     inc dl
169     jmp print
170     
171 end:
172     pop dx
173     pop cx
174     pop edi
175     pop eax
176     pop ebp
177     
178     ret
179 
180 Code32SegLen    equ    $ - CODE32_SEGMENT
181 
182 [section .gs]
183 [bits 32]
184 STACK32_SEGMENT:
185     times 1024 * 4 db 0
186 
187 Stack32SegLen    equ    $ - STACK32_SEGMENT
188 TopOfStack32    equ    Stack32SegLen - 1

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

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

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

  1 %include "inc.asm"
  2 
  3 org 0x9000
  4 
  5 jmp ENTRY_SEGMENT
  6  
  7 [section .gdt]
  8 ; GDT definition
  9 ;                                 段基址,       段界限,       段属性
 10 GDT_ENTRY       :     Descriptor    0,            0,           0
 11 CODE32_DESC     :     Descriptor    0,    Code32SegLen  - 1,   DA_C + DA_32 + DA_DPL0
 12 VIDEO_DESC      :     Descriptor 0xB8000,       0x07FFF,       DA_DRWA + DA_32 + DA_DPL0
 13 DATA32_DESC     :     Descriptor    0,    Data32SegLen  - 1,   DA_DR + DA_32 + DA_DPL0
 14 STACK32_DESC    :     Descriptor    0,      TopOfStack16,    DA_DRW + DA_32 + DA_DPL0      
 15 ; GDT end
 16 
 17 GdtLen    equ   $ - GDT_ENTRY
 18 
 19 GdtPtr:
 20           dw   GdtLen - 1
 21           dd   0
 22           
 23           
 24 ; GDT Selector
 25 
 26 Code32Selector    equ (0x0001 << 3) + SA_TIG + SA_RPL0
 27 VideoSelector     equ (0x0002 << 3) + SA_TIG + SA_RPL0
 28 Data32Selector    equ (0x0003 << 3) + SA_TIG + SA_RPL0
 29 Stack32Selector   equ (0x0004 << 3) + SA_TIG + SA_RPL0
 30 
 31 ; end of [section .gdt]
 32 
 33 TopOfStack16    equ  0x7c00
 34 
 35 [section .s16]
 36 [bits 16]
 37 ENTRY_SEGMENT:
 38     mov ax, cs
 39     mov ds, ax
 40     mov es, ax
 41     mov ss, ax
 42     mov sp, TopOfStack16
 43     
 44     ; initialize GDT for 32 bits code segment
 45     mov esi, CODE32_SEGMENT
 46     mov edi, CODE32_DESC
 47     
 48     call InitDescItem
 49     
 50     mov esi, DATA32_SEGMENT
 51     mov edi, DATA32_DESC
 52     
 53     call InitDescItem
 54     
 55     mov esi, STACK32_SEGMENT
 56     mov edi, STACK32_DESC
 57     
 58     call InitDescItem
 59     
 60     ; initialize GDT pointer struct
 61     mov eax, 0
 62     mov ax, ds
 63     shl eax, 4
 64     add eax, GDT_ENTRY
 65     mov dword [GdtPtr + 2], eax
 66 
 67     ; 1. load GDT
 68     lgdt [GdtPtr]
 69     
 70     ; 2. close interrupt
 71     cli 
 72     
 73     ; 3. open A20
 74     in al, 0x92
 75     or al, 00000010b
 76     out 0x92, al
 77     
 78     ; 4. enter protect mode
 79     mov eax, cr0
 80     or eax, 0x01
 81     mov cr0, eax
 82     
 83     ; 5. jump to 32 bits code
 84     ; jmp dword Code32Selector : 0
 85     ; push Stack32Selector    ; mu biao zhan duan xuan ze zi
 86     ; push TopOfStack32       ; zhan ding zhi zhen wei zhi
 87     push Code32Selector     ; mu biao dai ma duan xuan ze zi
 88     push 0                     ; mu bioa dai ma duan pian yi
 89     retf
 90     
 91 
 92     
 93 ; esi    --> code segment label
 94 ; edi    --> descriptor label
 95 InitDescItem:
 96     push eax
 97     
 98     mov eax, 0
 99     mov ax, cs
100     shl eax, 4
101     add eax, esi
102     mov word [edi + 2], ax
103     shr eax, 16
104     mov byte [edi + 4], al
105     mov byte [edi + 7], ah
106     
107     pop eax
108     
109     ret
110     
111 [section .dat]
112 [bits 32]
113 DATA32_SEGMENT:
114     DTOS                db    "D.T.OS!",0
115     DTOS_OFFSET            equ    DTOS - $$
116     
117 Data32SegLen    equ        $ - DATA32_SEGMENT  
118 
119   
120 [section .s32]
121 [bits 32]
122 CODE32_SEGMENT:
123     mov ax, VideoSelector
124     mov gs, ax
125     
126     mov ax, Data32Selector
127     mov ds, ax
128     
129     mov ax, Stack32Selector
130     mov ss, ax
131     
132     mov ax, Data32Selector
133     mov ds, ax
134     
135     mov ebp, DTOS_OFFSET
136     mov bx, 0x0C
137     mov dh, 12
138     mov dl, 33
139     
140     call PrintString
141     
142     jmp $
143 
144 
145 ; ds:ebp   --> string address
146 ; bx       --> attribute
147 ; dx       --> dh : row, dl : col
148 PrintString:
149     push ebp
150     push eax
151     push edi 
152     push cx
153     push dx
154     
155 print:
156     mov cl, [ds:ebp]
157     cmp cl, 0
158     je end
159     mov eax, 80
160     mul dh
161     add al, dl
162     shl eax, 1
163     mov edi, eax
164     mov ah, bl
165     mov al, cl
166     mov [gs:edi], ax
167     inc ebp
168     inc dl
169     jmp print
170     
171 end:
172     pop dx
173     pop cx
174     pop edi
175     pop eax
176     pop ebp
177     
178     ret
179 
180 Code32SegLen    equ    $ - CODE32_SEGMENT
181 
182 [section .gs]
183 [bits 32]
184 STACK32_SEGMENT:
185     times 1024 * 4 db 0
186 
187 Stack32SegLen    equ    $ - STACK32_SEGMENT
188 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处理器每一个特权级对应一个私有的栈

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

原文地址:https://www.cnblogs.com/wanmeishenghuo/p/9623482.html