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

参考

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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
%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是我们新添加的代码,运行结果如下:

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

 

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
%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处理器每一个特权级对应一个私有的栈

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

 

补充:关于特权级变化时,栈的保存及切换的内容,参考《x86汇编语言_从实模式到保护模式》中

          14.5.1中的完整描述。

  

  

posted on   lh03061238  阅读(119)  评论(0编辑  收藏  举报

编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示