18 深入特权级转移(上)

参考

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

 

这一节我们来研究从核心代码特权级转移到应用代码特权级。

首先将boot.asm贴出来如下:

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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
org 0x7c00
 
jmp short start
nop
 
define:
    BaseOfStack      equ 0x7c00
    BaseOfLoader     equ 0x9000
    RootEntryOffset  equ 19
    RootEntryLength  equ 14
    EntryItemLength  equ 32
    FatEntryOffset   equ 1
    FatEntryLength   equ 9
 
header:
    BS_OEMName     db "D.T.Soft"
    BPB_BytsPerSec dw 512
    BPB_SecPerClus db 1
    BPB_RsvdSecCnt dw 1
    BPB_NumFATs    db 2
    BPB_RootEntCnt dw 224
    BPB_TotSec16   dw 2880
    BPB_Media      db 0xF0
    BPB_FATSz16    dw 9
    BPB_SecPerTrk  dw 18
    BPB_NumHeads   dw 2
    BPB_HiddSec    dd 0
    BPB_TotSec32   dd 0
    BS_DrvNum      db 0
    BS_Reserved1   db 0
    BS_BootSig     db 0x29
    BS_VolID       dd 0
    BS_VolLab      db "D.T.OS-0.01"
    BS_FileSysType db "FAT12   "
 
start:
    mov ax, cs
    mov ss, ax
    mov ds, ax
    mov es, ax
    mov sp, BaseOfStack
 
    mov ax, RootEntryOffset
    mov cx, RootEntryLength
    mov bx, Buf
 
    call ReadSector
 
    mov si, Target
    mov cx, TarLen
    mov dx, 0
 
    call FindEntry
 
    cmp dx, 0
    jz output
 
    mov si, bx
    mov di, EntryItem
    mov cx, EntryItemLength
 
    call MemCpy
 
    mov ax, FatEntryLength
    mov cx, [BPB_BytsPerSec]
    mul cx
    mov bx, BaseOfLoader
    sub bx, ax
 
    mov ax, FatEntryOffset
    mov cx, FatEntryLength
 
    call ReadSector
 
    mov dx, [EntryItem + 0x1A]
    mov si, BaseOfLoader
 
loading:
    mov ax, dx
    add ax, 31
    mov cx, 1
    push dx
    push bx
    mov bx, si
    call ReadSector
    pop bx
    pop cx
    call FatVec
    cmp dx, 0xFF7
    jnb BaseOfLoader
    add si, 512
    jmp loading
 
output:
    mov bp, MsgStr
    mov cx, MsgLen
    call Print
 
last:
    hlt
    jmp last
 
 
; cx --> index
; bx --> fat table address
;
; return:
;     dx --> fat[index]
FatVec:
    mov ax, cx
    mov cl, 2
    div cl
 
    push ax
 
    mov ah, 0
    mov cx, 3
    mul cx
    mov cx, ax
 
    pop ax
 
    cmp ah, 0
    jz even
    jmp odd
 
even:    ; FatVec[j] = ( (Fat[i+1] & 0x0F) << 8 ) | Fat[i];
    mov dx, cx
    add dx, 1
    add dx, bx
    mov bp, dx
    mov dl, byte [bp]
    and dl, 0x0F
    shl dx, 8
    add cx, bx
    mov bp, cx
    or  dl, byte [bp]
    jmp return
 
odd:     ; FatVec[j+1] = (Fat[i+2] << 4) | ( (Fat[i+1] >> 4) & 0x0F );
    mov dx, cx
    add dx, 2
    add dx, bx
    mov bp, dx
    mov dl, byte [bp]
    mov dh, 0
    shl dx, 4
    add cx, 1
    add cx, bx
    mov bp, cx
    mov cl, byte [bp]
    shr cl, 4
    and cl, 0x0F
    mov ch, 0
    or  dx, cx
 
return:
    ret
 
; ds:si --> source
; es:di --> destination
; cx    --> length
MemCpy:
 
    cmp si, di
 
    ja btoe
 
    add si, cx
    add di, cx
    dec si
    dec di
 
    jmp etob
 
btoe:
    cmp cx, 0
    jz done
    mov al, [si]
    mov byte [di], al
    inc si
    inc di
    dec cx
    jmp btoe
 
etob:
    cmp cx, 0
    jz done
    mov al, [si]
    mov byte [di], al
    dec si
    dec di
    dec cx
    jmp etob
 
done:
    ret
 
; es:bx --> root entry offset address
; ds:si --> target string
; cx    --> target length
;
; return:
;     (dx !=0 ) ? exist : noexist
;        exist --> bx is the target entry
FindEntry:
    push cx
 
    mov dx, [BPB_RootEntCnt]
    mov bp, sp
 
find:
    cmp dx, 0
    jz noexist
    mov di, bx
    mov cx, [bp]
    push si
    call MemCmp
    pop si
    cmp cx, 0
    jz exist
    add bx, 32
    dec dx
    jmp find
 
exist:
noexist:
    pop cx
 
    ret
 
; ds:si --> source
; es:di --> destination
; cx    --> length
;
; return:
;        (cx == 0) ? equal : noequal
MemCmp:
 
compare:
    cmp cx, 0
    jz equal
    mov al, [si]
    cmp al, byte [di]
    jz goon
    jmp noequal
goon:
    inc si
    inc di
    dec cx
    jmp compare
 
equal:
noequal:
 
    ret
 
; es:bp --> string address
; cx    --> string length
Print:
    mov dx, 0
    mov ax, 0x1301
    mov bx, 0x0007
    int 0x10
    ret
 
; no parameter
ResetFloppy:
 
    mov ah, 0x00
    mov dl, [BS_DrvNum]
    int 0x13
 
    ret
 
; ax    --> logic sector number
; cx    --> number of sector
; es:bx --> target address
ReadSector:
 
    call ResetFloppy
 
    push bx
    push cx
 
    mov bl, [BPB_SecPerTrk]
    div bl
    mov cl, ah
    add cl, 1
    mov ch, al
    shr ch, 1
    mov dh, al
    and dh, 1
    mov dl, [BS_DrvNum]
 
    pop ax
    pop bx
 
    mov ah, 0x02
 
read:
    int 0x13
    jc read
 
    ret
 
MsgStr db  "No LOADER ..."
MsgLen equ ($-MsgStr)
Target db  "LOADER     "
TarLen equ ($-Target)
EntryItem times EntryItemLength db 0x00
Buf:
    times 510-($-$$) db 0x00
    db 0x55, 0xaa

inc.asm如下:

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
; Segment Attribute
DA_32    equ    0x4000
DA_DR    equ    0x90
DA_DRW   equ    0x92
DA_DRWA  equ    0x93
DA_C     equ    0x98
DA_CR    equ    0x9A
DA_CCO   equ    0x9C
DA_CCOR  equ    0x9E
 
; Segment Privilege
DA_DPL0  equ   0x00        ; DPL = 0
DA_DPL1  equ   0x20        ; DPL = 1
DA_DPL2  equ   0x40        ; DPL = 2
DA_DPL3  equ   0x60        ; DPL = 3
 
; Special Attribute
DA_LDT             equ        0x82
DA_TaskGate        equ        0x85    ;
DA_386TSS        equ        0x89    ;
DA_386CGate        equ        0x8C    ;
DA_386IGate        equ        0x8E    ;
DA_386tgATE        equ        0x8F    ;
 
; Selector Attribute
SA_RPL0    equ    0
SA_RPL1    equ    1
SA_RPL2    equ    2
SA_RPL3    equ    3
 
SA_TIG    equ    0
SA_TIL    equ    4
 
; 描述符
; usage: Descriptor Base, Limit, Attr
;        Base:  dd
;        Limit: dd (low 20 bits available)
;        Attr:  dw (lower 4 bits of higher byte are always 0)
%macro Descriptor 3                              ; 段基址, 段界限, 段属性
    dw    %2 & 0xFFFF                         ; 段界限1
    dw    %1 & 0xFFFF                         ; 段基址1
    db    (%1 >> 16) & 0xFF                   ; 段基址2
    dw    ((%2 >> 8) & 0xF00) | (%3 & 0xF0FF) ; 属性1 + 段界限2 + 属性2
    db    (%1 >> 24) & 0xFF                   ; 段基址3
%endmacro                                     ; 共 8 字节
 
;Gate
; usage : Gate Selector, Offset, DCount, Attr
;         Selector : dw
;          Offset   : dd
;          DCount   : db
;          Attr       : db
%macro Gate 4
    dw        (%2 & 0xFFFF)                        ; pianyidizhi1
    dw        %1                                    ; xuanzezi
    dw        (%3 & 0x1F) | ((%4 << 8) & 0xFF00)    ; shu xing
    dw        ((%2 >> 16) & 0xFFFF)                ; pianyidizhi2
%endmacro

loader.asm例子如下:

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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
%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,      TopOfStack32,      DA_DRW + DA_32  + DA_DPL0
FUNCTION_DESC   :     Descriptor    0,   FunctionSegLen - 1,   DA_C + DA_32 + DA_DPL0
TASK_A_LDT_DESC :     Descriptor    0,      TaskALdtLen - 1,   DA_LDT + 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
FunctionSelector  equ (0x0005 << 3) + SA_TIG + SA_RPL0
TaskALdtSelector  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, TASK_A_LDT_ENTRY
    mov edi, TASK_A_LDT_DESC
 
    call InitDescItem
 
    mov esi, TASK_A_DATA32_SEGMENT
    mov edi, TASK_A_DATA32_DESC
 
    call InitDescItem
 
    mov esi, TASK_A_CODE32_SEGMENT
    mov edi, TASK_A_CODE32_DESC
 
    call InitDescItem
 
    mov esi, TASK_A_STACK32_SEGMENT
    mov edi, TASK_A_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
 
 
; 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
 
    mov ax, TaskALdtSelector
 
    lldt ax
 
    push TaskAStack32Selector
    push TaskATopOfStack32
    push TaskACode32Selector
    push 0
    retf
 
    jmp $
 
Code32SegLen    equ    $ - CODE32_SEGMENT
 
[section .gs]
[bits 32]
STACK32_SEGMENT:
    times 1024 * 4 db 0
 
Stack32SegLen   equ $ - STACK32_SEGMENT
TopOfStack32    equ Stack32SegLen - 1
 
 
; ======================================
;
;      Global Function 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
 
; ==================================
;
;        Task A Code Segment
;
;===================================
[section .task-a-ldt]
; Task A LDT definition
;                                ¶Î»ùÖ·         ¶ÎœçÏÞ                ¶ÎÊôÐÔ
 
TASK_A_LDT_ENTRY:
TASK_A_CODE32_DESC  : Descriptor   0,    TaskACode32SegLen - 1,  DA_C + DA_32 + DA_DPL3
TASK_A_DATA32_DESC  : Descriptor   0,    TaskAData32SegLen - 1,  DA_DR + DA_32 + DA_DPL3
TASK_A_STACK32_DESC : Descriptor   0,    TaskAStack32SegLen - 1, DA_DRW + DA_32 + DA_DPL3
 
TaskALdtLen        equ   $ - TASK_A_LDT_ENTRY
 
; Task A LDT  Selector
TaskACode32Selector        equ  (0x0000 << 3) + SA_TIL + SA_RPL3
TaskAData32Selector     equ     (0x0001 << 3) + SA_TIL + SA_RPL3
TaskAStack32Selector    equ  (0x0002 << 3) + SA_TIL + SA_RPL3
 
[section .task-a-dat]
[bits 32]
TASK_A_DATA32_SEGMENT:
    TASK_A_STRING        db   "This is Task A", 0
    TASK_A_STRING_OFFSET    equ     TASK_A_STRING - $$
 
TaskAData32SegLen    equ $ - TASK_A_DATA32_SEGMENT
 
[section .task-a-gs]
[bits 32]
TASK_A_STACK32_SEGMENT:
    times 1024 db 0
 
TaskAStack32SegLen    equ     $ - TASK_A_STACK32_SEGMENT
TaskATopOfStack32    equ     TaskAStack32SegLen - 1
 
[section .task-a-s32]
[bits 32]
TASK_A_CODE32_SEGMENT:
    mov ax, TaskAData32Selector
    mov ds, ax
 
    jmp $
 
 
TaskACode32SegLen   equ  $ - TASK_A_CODE32_SEGMENT

第10-16行我们定义的段描述符特权级都是0,处理器从实模式跳转到保护模式后进入的是核心特权级0,进入32位的保护模式后首先执行的是144行的程序,我们首先将一些段选择子放在段寄存器中,157-162行调用了函数进行打印,这个FUNCTION_SEGMENT段也是在特权级0,所以这时候的打印不涉及特权级的转移。

164-166行将任务段的选择子加载进CPU的特殊寄存器中,168-170将任务段的栈段选择子、栈顶地址、任务段的代码段选择子压入高特权级的栈中,第171行压入的0指的是偏移地址,第172行的retf远跳转返回的作用是使处理器从核心特权级0跳转到普通特权级3。跳转的时候处理器会自动的在核心态的栈中加载我们刚刚存进去的值到相应的寄存器中,然后程序就到了275行的任务段中的代码段执行。

为了单步执行我们先使用如下命令对loader可执行程序进行反编译:

ndisasm -b 32 -o 0x9000 loader > loader.txt          

-b 32是以32位的方式进行反编译。

在反编译得到文件中我们找到lldt所在的位置,断点就打在这里:

 

 

启动bochs,使用break 0x915D打上断点。

输入c执行到断点处,结果如下:

 

可以看到下一条指令就是 lldt ax,继续单步执行:

 

单步执行并使用sreg查看寄存器信息,可以看到cs中的值是0x0008,可知最后两位是00,这时处于核心特权级0。

继续单步执行:

 

retf执行完之后再次使用retf查看寄存器的值,可以看到cs的之变成了0x0007,最后两位是11,这说明特权级变为了3。

继续单步执行:

 

程序转移到了276行的地方。

 

继续进行实验,我们我特权级3模拟一个内核函数调用,这就涉及到了低特权级到高特权级的转移,初步给出程序如下:

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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
%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,      TopOfStack32,      DA_DRW + DA_32  + DA_DPL0
FUNCTION_DESC   :     Descriptor    0,   FunctionSegLen - 1,   DA_C + DA_32 + DA_DPL0
TASK_A_LDT_DESC :     Descriptor    0,      TaskALdtLen - 1,   DA_LDT + DA_DPL0
; call Gate
;                                selector         offset     canshugeshu   attr
FUNC_PRINTSTRING_DESC : Gate  FunctionSelector,  PrintString,   0,   DA_386CGate + DA_DPL3
; 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
FunctionSelector  equ (0x0005 << 3) + SA_TIG + SA_RPL0
TaskALdtSelector  equ (0x0006 << 3) + SA_TIG + SA_RPL0
 
; Gate selector
FuncPrintStringSelector    equ   (0x0007 << 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, TASK_A_LDT_ENTRY
    mov edi, TASK_A_LDT_DESC
 
    call InitDescItem
 
    mov esi, TASK_A_DATA32_SEGMENT
    mov edi, TASK_A_DATA32_DESC
 
    call InitDescItem
 
    mov esi, TASK_A_CODE32_SEGMENT
    mov edi, TASK_A_CODE32_DESC
 
    call InitDescItem
 
    mov esi, TASK_A_STACK32_SEGMENT
    mov edi, TASK_A_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
 
 
; 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
 
    mov ax, TaskALdtSelector
 
    lldt ax
 
    push TaskAStack32Selector
    push TaskATopOfStack32
    push TaskACode32Selector
    push 0
    retf
 
Code32SegLen    equ    $ - CODE32_SEGMENT
 
[section .gs]
[bits 32]
STACK32_SEGMENT:
    times 1024 * 4 db 0
 
Stack32SegLen   equ $ - STACK32_SEGMENT
TopOfStack32    equ Stack32SegLen - 1
 
 
; ======================================
;
;      Global Function 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
 
; ==================================
;
;        Task A Code Segment
;
;===================================
[section .task-a-ldt]
; Task A LDT definition
;                                ¶Î»ùÖ·         ¶ÎœçÏÞ                ¶ÎÊôÐÔ
 
TASK_A_LDT_ENTRY:
TASK_A_CODE32_DESC  : Descriptor   0,    TaskACode32SegLen - 1,  DA_C + DA_32 + DA_DPL3
TASK_A_DATA32_DESC  : Descriptor   0,    TaskAData32SegLen - 1,  DA_DR + DA_32 + DA_DPL3
TASK_A_STACK32_DESC : Descriptor   0,    TaskAStack32SegLen - 1, DA_DRW + DA_32 + DA_DPL3
 
TaskALdtLen        equ   $ - TASK_A_LDT_ENTRY
 
; Task A LDT  Selector
TaskACode32Selector        equ  (0x0000 << 3) + SA_TIL + SA_RPL3
TaskAData32Selector     equ     (0x0001 << 3) + SA_TIL + SA_RPL3
TaskAStack32Selector    equ  (0x0002 << 3) + SA_TIL + SA_RPL3
 
[section .task-a-dat]
[bits 32]
TASK_A_DATA32_SEGMENT:
    TASK_A_STRING        db   "This is Task A", 0
    TASK_A_STRING_OFFSET    equ     TASK_A_STRING - $$
 
TaskAData32SegLen    equ $ - TASK_A_DATA32_SEGMENT
 
[section .task-a-gs]
[bits 32]
TASK_A_STACK32_SEGMENT:
    times 1024 db 0
 
TaskAStack32SegLen    equ     $ - TASK_A_STACK32_SEGMENT
TaskATopOfStack32    equ     TaskAStack32SegLen - 1
 
[section .task-a-s32]
[bits 32]
TASK_A_CODE32_SEGMENT:
    mov ax, TaskAData32Selector
    mov ds, ax
 
    mov ebp, TASK_A_STRING_OFFSET
    mov bx, 0x0c
    mov dh, 14
    mov dl, 29
 
    call FuncPrintStringSelector : 0
 
    jmp $
 
 
TaskACode32SegLen   equ  $ - TASK_A_CODE32_SEGMENT

第19行添加了调用门的描述符,偏移位置是PrintString,第39行添加了调用门的选择子。

然后我们在TASKA任务段模拟内核函数的调用,也就是在第281行这里开始,先将任务段中的数据段加载进数据段寄存器ds。

第284行将字符串的偏移放到ebp寄存器里面,这相当于先在用户空间把参数准备好,第289行即通过调用门执行了这个调用,但是实验结果显示,程序崩溃了。

这是为什么呢?

特权级转移的时候栈会变化。

因为从低特权级到高特权级的跳转需要将栈切换到高特权级的栈,这个高特权级的栈去哪里找呢?去TSS段里面去找,这个段里面保存了栈的一些信息,但是现在我们的TSS并没有准备好,所以出错了,对程序进行改进。

TSS段本质也是一段内存。

改进的程序如下:

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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
%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,      TopOfStack32,      DA_DRW + DA_32  + DA_DPL0
FUNCTION_DESC   :     Descriptor    0,   FunctionSegLen - 1,   DA_C + DA_32 + DA_DPL0
TASK_A_LDT_DESC :     Descriptor    0,      TaskALdtLen - 1,   DA_LDT + DA_DPL0
TSS_DESC        :     Descriptor    0,         TSSLen - 1,     DA_386TSS + DA_DPL0
; call Gate
;                                selector         offset     canshugeshu   attr
FUNC_PRINTSTRING_DESC : Gate  FunctionSelector,  PrintString,   0,   DA_386CGate + DA_DPL3
; 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
FunctionSelector  equ (0x0005 << 3) + SA_TIG + SA_RPL0
TaskALdtSelector  equ (0x0006 << 3) + SA_TIG + SA_RPL0
TSSSelector       equ (0x0007 << 3) + SA_TIG + SA_RPL0
; Gate selector
FuncPrintStringSelector    equ   (0x0008 << 3) + SA_TIG + SA_RPL3
 
; end of [section .gdt]
 
[section .tss]
[bits 32]
TSS_SEGMENT:
           dd 0
           dd TopOfStack32         ; 0
           dd Stack32Selector      ;
           dd 0                    ; 1
           dd 0                    ;
           dd 0                    ; 2
           dd 0                    ;
           times 4*18 dd 0
           dw   0
           dw   $ - TSS_SEGMENT + 2
           db   0xFF
 
TSSLen     equ    $ - TSS_SEGMENT
 
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, TASK_A_LDT_ENTRY
    mov edi, TASK_A_LDT_DESC
 
    call InitDescItem
 
    mov esi, TASK_A_DATA32_SEGMENT
    mov edi, TASK_A_DATA32_DESC
 
    call InitDescItem
 
    mov esi, TASK_A_CODE32_SEGMENT
    mov edi, TASK_A_CODE32_DESC
 
    call InitDescItem
 
    mov esi, TASK_A_STACK32_SEGMENT
    mov edi, TASK_A_STACK32_DESC
 
    call InitDescItem
 
    mov esi, TSS_SEGMENT
    mov edi, TSS_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
 
 
; 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
 
    mov ax, TSSSelector
 
    ltr ax
 
    mov ax, TaskALdtSelector
 
    lldt ax
 
    push TaskAStack32Selector
    push TaskATopOfStack32
    push TaskACode32Selector
    push 0
    retf
 
Code32SegLen    equ    $ - CODE32_SEGMENT
 
[section .gs]
[bits 32]
STACK32_SEGMENT:
    times 1024 * 4 db 0
 
Stack32SegLen   equ $ - STACK32_SEGMENT
TopOfStack32    equ Stack32SegLen - 1
 
 
; ======================================
;
;      Global Function 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
 
; ==================================
;
;        Task A Code Segment
;
;===================================
[section .task-a-ldt]
; Task A LDT definition
;                                ¶Î»ùÖ·         ¶ÎœçÏÞ                ¶ÎÊôÐÔ
 
TASK_A_LDT_ENTRY:
TASK_A_CODE32_DESC  : Descriptor   0,    TaskACode32SegLen - 1,  DA_C + DA_32 + DA_DPL3
TASK_A_DATA32_DESC  : Descriptor   0,    TaskAData32SegLen - 1,  DA_DR + DA_32 + DA_DPL3
TASK_A_STACK32_DESC : Descriptor   0,    TaskAStack32SegLen - 1, DA_DRW + DA_32 + DA_DPL3
 
TaskALdtLen        equ   $ - TASK_A_LDT_ENTRY
 
; Task A LDT  Selector
TaskACode32Selector        equ  (0x0000 << 3) + SA_TIL + SA_RPL3
TaskAData32Selector     equ     (0x0001 << 3) + SA_TIL + SA_RPL3
TaskAStack32Selector    equ  (0x0002 << 3) + SA_TIL + SA_RPL3
 
[section .task-a-dat]
[bits 32]
TASK_A_DATA32_SEGMENT:
    TASK_A_STRING        db   "This is Task A", 0
    TASK_A_STRING_OFFSET    equ     TASK_A_STRING - $$
 
TaskAData32SegLen    equ $ - TASK_A_DATA32_SEGMENT
 
[section .task-a-gs]
[bits 32]
TASK_A_STACK32_SEGMENT:
    times 1024 db 0
 
TaskAStack32SegLen    equ     $ - TASK_A_STACK32_SEGMENT
TaskATopOfStack32    equ     TaskAStack32SegLen - 1
 
[section .task-a-s32]
[bits 32]
TASK_A_CODE32_SEGMENT:
    mov ax, TaskAData32Selector
    mov ds, ax
 
    mov ebp, TASK_A_STRING_OFFSET
    mov bx, 0x0c
    mov dh, 14
    mov dl, 29
 
    call FuncPrintStringSelector : 0
 
    jmp $
 
 
TaskACode32SegLen   equ  $ - TASK_A_CODE32_SEGMENT

TSS段本质也是一段内存,因此在44-59行我们定义了这个段,在这个段中,我们只定义了特权级0的栈的信息,特权级1和2的栈的信息赋值为0。

第17行定义了这个段的段描述符,第38行定义了这个段的选择子。

此外第20行的调用门的描述符的DPL要定义成3。

第40行的调用门的选择子的RPL也要定义成3。

在程序运行的时候不要忘了TSS段的段基址的初始化,这是在113-116行完成的。

在程序还在核心态特权级0的时候,还要将TSS加载进处理器内部的特殊寄存器中,194-196正是完成了这个操作,用到的指令时ltr。

进行完这些修改之后,我们再次执行程序,发现程序还是崩溃,进行单步执行操作:

首先使用如下指令进行反汇编:

ndisasm -b 32 -o 0x9000 loader > loader.txt

找到lldt后面的那个retf所在的位置:

位置为0x92DB。

打断点再次单步执行:

上图中马上要经过调用门了,继续单步执行:

通过调用门调用之后可以看到cs的值为0x0028,最后两位是00,到了核心态特权级0。

调用门之后程序到了232行的PrintStringFunc处,继续单步执行:

在第250行的程序之前程序还没有出错,继续单步执行第250行:

这时候程序崩溃了,产生了上图中的错误。

第250行的程序的本质是内存赋值,即从ax指向的内存复制到gs代表的内存,ax代表的内存由DS寄存器获得,上图中可以看到DS的rpl为3,而GS的rpl为0,这相当于从用户态向内核态赋值数据,这是一个很危险的操作,错误就出在了这里。

 为了解决这个错误,我们将Vedio段描述符的dpl改为3:

选择子的rpl也改为3:

再次执行程序结果如下:

程序可以正常的执行了。

 RPL究竟是什么?有什么用?

RPL的意义:

  位置意义:

    选择子或者段寄存器的最低两位。

  请求意义:

    资源请求的特权级(不同于当前特权级CPL)。

请求特权级表明了,请求这个资源的时候所使用的特权级是什么。

如果当前执行的代码段的CPL为0,那么是不是就可以访问任意的内存了呢?

这时候还取决于资源的请求特权级也即RPL,即使这时候CPL为0,但是如果RPL很低也是不能请求对应的资源的。

资源请求时:

CPL、RPL、DPL共同确定请求是否合法。

当前代码(CPL)-> 选择子(RPL) ->  数据段(DPL)。

 

小结:

 

TSS是通过调用门转移到高特权级的关键。

TSS是处理器的硬件数据结构,用于实现多任务。

TSS的结构遵循保护模式下的内存使用规则。

RPL在请求资源时是合法性判断的依据之一。

处理器使用CPL、RPL、DPL共同确定合法性。

  

  

  

  

  

posted on   lh03061238  阅读(150)  评论(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
点击右上角即可分享
微信分享提示