特权级 linux,一个操作系统的实现(5)-关于特权级

检查ds、es、fs、gs的值,如果其中哪一个寄存器指向的段的DPL小于CPL(此规则不适用于一致代码段),那么一个空描述符会被加载到该寄存器。

通过以上两小节可以看出,使用调用门的过程分为两个部分:

一部分是从低特权级到高特权级,通过调用门和call指令来实现

另一部分是从高特权级到低特权级,通过ret指令来实现。

接下来我们就用ret指令实现由高特权级到低特权级的转移:

通过ret指令从ring0进入ring3

通过上面的分析我们知道,在ret指令执行前,堆栈中应该已经准备好了目标代码的cs、eip、ss、esp,另外还可能有参数。这些可以是处理器压入栈的,当然,也可以由我们自己压栈。在接下来的例子中,ret前的堆栈如下图所示:

20160528090247128.png

这样,ret执行之后就可以转移到低特权级代码中了。接下来用代码实现如下:

在原来的代码上添加如下内容:

ring3的代码段、ring3的堆栈段

19 LABEL_DESC_CODE_RING3: Descriptor 0,SegCodeRing3Len-1, DA_C+DA_32+DA_DPL3

...

22 LABEL_DESC_STACK3: Descriptor 0, TopOfStack3, DA_DRWA+DA_32+DA_DPL3

...

25 LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW+DA_DPL3

...

40 SelectorCodeRing3 equ LABEL_DESC_CODE_RING3 - LABEL_GDT + SA_RPL3

...

43 SelectorStack3 equ LABEL_DESC_STACK3 - LABEL_GDT + SA_RPL3

...

75 ; 堆栈段ring3

76 [SECTION .s3]

77 ALIGN 32

78 [BITS 32]

79 LABEL_STACK3:

80 times 512 db 0

81 TopOfStack3 equ $ - LABEL_STACK3 - 1

82 ; END of [SECTION .s3]

...

379 ; CodeRing3

380 [SECTION .ring3]

381 ALIGN 32

382 [BITS 32]

383 LABEL_CODE_RING3:

384 mov ax, SelectorVideo

385 mov gs, ax

386

387 mov edi, (80 * 14 + 0) * 2

388 mov ah, 0Ch

389 mov al, '3'

390 mov [gs:edi], ax

391

392 jmp $

393 SegCodeRing3Len equ $ - LABEL_CODE_RING3

394 ; END of [SECTION .ring3]

ring3堆栈段与ring3代码段的描述符的初始化代码

146 ; 初始化堆栈段描述符(Ring3)

147 xor eax, eax

148 mov ax, ds

149 shl eax, 4

150 add eax, LABEL_STACK3

151 mov word [LABEL_DESC_STACK3 + 2], ax

152 shr eax, 16

153 mov byte [LABEL_DESC_STACK3 + 4], al

154 mov byte [LABEL_DESC_STACK3 + 7], ah

...

176 ; 初始化Ring3描述符

177 xor eax, eax

178 mov ax, ds

179 shl eax, 4

180 add eax, LABEL_CODE_RING3

181 mov word [LABEL_DESC_CODE_RING3 + 2], ax

182 shr eax, 16

183 mov byte [LABEL_DESC_CODE_RING3 + 4], al

184 mov byte [LABEL_DESC_CODE_RING3 + 7], ah

由于这段代码运行在ring3,而在其中由于要写显存而访问到了VIDEO段,为了不会产生错误,我们把VIDEO段的DPL修改为3(第25行)。依据上面所说的规则,RPL不需要修改。

上面代码段和数据段都已经初始化好了。接下来将ss、esp、cs、eip依次压栈,并且执行retf指令。

266 push SelectorStack3

267 push TopOfStack3

268 push SelectorCodeRing3

269 push 0

270 retf

查看结果,如果出现了红色的3并且不返回到DOS(因为新添加的代码段最后是jmp $),说明我们已经成功进入ring3。

20160528090247130.png

上面就是从ring0到ring3的过程。接下来开始使用调用门实现ring3到ring0的转移

通过调用门进行有特权级变换的转移

上面已经进入ring3了,接下来通过调用门重新进入ring0。将上面ring3的代码修改如下:

28 LABEL_CALL_GATE_TEST: Gate SelectorCodeDest, 0, 0, DA_386CGate+DA_DPL3

...

47 SelectorCallGateTest equ LABEL_CALL_GATE_TEST - LABEL_GDT + SA_RPL3

...

379 ; CodeRing3

380 [SECTION .ring3]

381 ALIGN 32

382 [BITS 32]

383 LABEL_CODE_RING3:

384 mov ax, SelectorVideo

385 mov gs, ax

386 mov edi, (80 * 14 + 0) * 2

387 mov ah, 0Ch

388 mov al, '3'

389 mov [gs:edi], ax

390

391 call SelectorCallGateTest:0

392

393 jmp $

394 SegCodeRing3Len equ $ - LABEL_CODE_RING3

395 ; END of [SECTION .ring3]

在jmp $之前,增加了使用调用门的指令,这个调用门是之前已经定义好了的。修改描述符和选择子是为了满足CPL和RPL都小于等于调用门DPL的条件。

不要忘记,从低特权级到高特权级转移的时候,需要用到TSS。因此接下来需要人工准备一个TSS:

24 LABEL_DESC_TSS: Descriptor 0, TSSLen-1, DA_386TSS

...

45 SelectorTSS equ LABEL_DESC_TSS - LABEL_GDT

...

85 ; TSS

86 [SECTION .tss]

87 ALIGN 32

88 [BITS 32]

89 LABEL_TSS:

90 DD 0 ; Back

91 DD TopOfStack ; 0 级堆栈

92 DD SelectorStack ;

93 DD 0 ; 1 级堆栈

94 DD 0 ;

95 DD 0 ; 2 级堆栈

96 DD 0 ;

97 DD 0 ; CR3

98 DD 0 ; EIP

99 DD 0 ; EFLAGS

100 DD 0 ; EAX

101 DD 0 ; ECX

102 DD 0 ; EDX

103 DD 0 ; EBX

104 DD 0 ; ESP

105 DD 0 ; EBP

106 DD 0 ; ESI

107 DD 0 ; EDI

108 DD 0 ; ES

109 DD 0 ; CS

110 DD 0 ; SS

111 DD 0 ; DS

112 DD 0 ; FS

113 DD 0 ; GS

114 DD 0 ; LDT

115 DW 0 ; 调试陷阱标志

116 DW $ - LABEL_TSS + 2 ; I/O位图基址

117 DB 0ffh ; I/O位图结束标志

118 TSSLen equ $ - LABEL_TSS

因为这里只进入ring0,所以在这里先只初始化0级堆栈。

接下来初始化TSS描述符:

223 ; 初始化 TSS 描述符

224 xor eax, eax

225 mov ax, ds

226 shl eax, 4

227 add eax, LABEL_TSS

228 mov word [LABEL_DESC_TSS + 2], ax

229 shr eax, 16

230 mov byte [LABEL_DESC_TSS + 4], al

231 mov byte [LABEL_DESC_TSS + 7], ah

最后需要在特权级变换之前加载TSS:

313 mov ax, SelectorTSS

314 ltr ax

接下来开始运行,运行结果如下:

20160528090247132.png

初始的32位代码段(ring0)打印一串字符串,ring3代码段打印数字3,调用门的目标代码段(ring0)打印字母C.因此到这里,我们实现了从rong0到ring3,然后再返回ring0的整个过程。接下来做最后一步,就是使程序顺利返回实模式,只需要将调用局部任务的代码加入到调用门的目标代码([SECTION .sdest])。最后,程序将由这里进入局部任务,然后由原路返回实模式。代码如下:

346 [SECTION .sdest]; 调用门目标段

347 [BITS 32]

348

349 LABEL_SEG_CODE_DEST:

350 mov ax, SelectorVideo

351 mov gs, ax ; 视频段选择子(目的)

352

353 mov edi, (80 * 12 + 0) * 2 ; 屏幕第 12 行, 第 0 列。

354 mov ah, 0Ch ; 0000: 黑底 1100: 红字

355 mov al, 'C'

356 mov [gs:edi], ax

357

358 ; Load LDT

359 mov ax, SelectorLDT

360 lldt ax

361

362 jmp SelectorLDTCodeA:0 ; 跳入局部任务,将打印字母 'L'。

363

364 ;retf

365

366 SegCodeDestLen equ $ - LABEL_SEG_CODE_DEST

367 ; END of [SECTION .sdest]

20160528090247134.png

从上面的运行结果我们能够知道。到这里就实现了DOS->ring0->ring3->ring0->DOS的整个过程。

源代码

; ==========================================

; pmtest5.asm

; 编译方法:nasm pmtest5.asm -o pmtest5.com

; ==========================================

%include "pm.inc" ; 常量, 宏, 以及一些说明

org 0100h

jmp LABEL_BEGIN

[SECTION .gdt]

; GDT

; 段基址, 段界限 , 属性

LABEL_GDT: Descriptor 0, 0, 0 ;空描述符

LABEL_DESC_NORMAL: Descriptor 0, 0ffffh, DA_DRW ;Normal描述符

LABEL_DESC_CODE32: Descriptor 0, SegCode32Len-1, DA_C+DA_32 ;非一致,32

LABEL_DESC_CODE16: Descriptor 0, 0ffffh, DA_C ;非一致,16

LABEL_DESC_CODE_DEST: Descriptor 0, SegCodeDestLen-1, DA_C+DA_32 ;非一致,32

LABEL_DESC_CODE_RING3: Descriptor 0, SegCodeRing3Len-1, DA_C+DA_32+DA_DPL3

LABEL_DESC_DATA: Descriptor 0, DataLen-1, DA_DRW ;Data

LABEL_DESC_STACK: Descriptor 0, TopOfStack, DA_DRWA+DA_32 ;Stack,32

LABEL_DESC_STACK3: Descriptor 0, TopOfStack3, DA_DRWA+DA_32+DA_DPL3

LABEL_DESC_LDT: Descriptor 0, LDTLen-1, DA_LDT ;LDT

LABEL_DESC_TSS: Descriptor 0, TSSLen-1, DA_386TSS ;TSS

LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW+DA_DPL3

; 门 目标选择子, 偏移, DCount, 属性

LABEL_CALL_GATE_TEST: Gate SelectorCodeDest, 0, 0, DA_386CGate + DA_DPL3

; GDT 结束

GdtLen equ $ - LABEL_GDT ; GDT长度

GdtPtr dw GdtLen - 1 ; GDT界限

dd 0 ; GDT基地址

; GDT 选择子

SelectorNormal equ LABEL_DESC_NORMAL - LABEL_GDT

SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT

SelectorCode16 equ LABEL_DESC_CODE16 - LABEL_GDT

SelectorCodeDest equ LABEL_DESC_CODE_DEST - LABEL_GDT

SelectorCodeRing3 equ LABEL_DESC_CODE_RING3 - LABEL_GDT + SA_RPL3

SelectorData equ LABEL_DESC_DATA - LABEL_GDT

SelectorStack equ LABEL_DESC_STACK - LABEL_GDT

SelectorStack3 equ LABEL_DESC_STACK3 - LABEL_GDT + SA_RPL3

SelectorLDT equ LABEL_DESC_LDT - LABEL_GDT

SelectorTSS equ LABEL_DESC_TSS - LABEL_GDT

SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT

SelectorCallGateTest equ LABEL_CALL_GATE_TEST - LABEL_GDT + SA_RPL3

; END of [SECTION .gdt]

[SECTION .data1] ; 数据段

ALIGN 32

[BITS 32]

LABEL_DATA:

SPValueInRealMode dw 0

; 字符串

PMMessage: db "In Protect Mode now. ^-^", 0 ; 进入保护模式后显示此字符串

OffsetPMMessage equ PMMessage - $$

StrTest: db "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0

OffsetStrTest equ StrTest - $$

DataLen equ $ - LABEL_DATA

; END of [SECTION .data1]

; 全局堆栈段

[SECTION .gs]

ALIGN 32

[BITS 32]

LABEL_STACK:

times 512 db 0

TopOfStack equ $ - LABEL_STACK - 1

; END of [SECTION .gs]

; 堆栈段ring3

[SECTION .s3]

ALIGN 32

[BITS 32]

LABEL_STACK3:

times 512 db 0

TopOfStack3 equ $ - LABEL_STACK3 - 1

; END of [SECTION .s3]

; TSS ---------------------------------------------------------------------------------------------

[SECTION .tss]

ALIGN 32

[BITS 32]

LABEL_TSS:

DD 0 ; Back

DD TopOfStack ; 0 级堆栈

DD SelectorStack ;

DD 0 ; 1 级堆栈

DD 0 ;

DD 0 ; 2 级堆栈

DD 0 ;

DD 0 ; CR3

DD 0 ; EIP

DD 0 ; EFLAGS

DD 0 ; EAX

DD 0 ; ECX

DD 0 ; EDX

DD 0 ; EBX

DD 0 ; ESP

DD 0 ; EBP

DD 0 ; ESI

DD 0 ; EDI

DD 0 ; ES

DD 0 ; CS

DD 0 ; SS

DD 0 ; DS

DD 0 ; FS

DD 0 ; GS

DD 0 ; LDT

DW 0 ; 调试陷阱标志

DW $ - LABEL_TSS + 2 ; I/O位图基址

DB 0ffh ; I/O位图结束标志

TSSLen equ $ - LABEL_TSS

; TSS ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

[SECTION .s16]

[BITS 16]

LABEL_BEGIN:

mov ax, cs

mov ds, ax

mov es, ax

mov ss, ax

mov sp, 0100h

mov [LABEL_GO_BACK_TO_REAL+3], ax

mov [SPValueInRealMode], sp

; 初始化 16 位代码段描述符

mov ax, cs

movzx eax, ax

shl eax, 4

add eax, LABEL_SEG_CODE16

mov word [LABEL_DESC_CODE16 + 2], ax

shr eax, 16

mov byte [LABEL_DESC_CODE16 + 4], al

mov byte [LABEL_DESC_CODE16 + 7], ah

; 初始化 32 位代码段描述符

xor eax, eax

mov ax, cs

shl eax, 4

add eax, LABEL_SEG_CODE32

mov word [LABEL_DESC_CODE32 + 2], ax

shr eax, 16

mov byte [LABEL_DESC_CODE32 + 4], al

mov byte [LABEL_DESC_CODE32 + 7], ah

; 初始化测试调用门的代码段描述符

xor eax, eax

mov ax, cs

shl eax, 4

add eax, LABEL_SEG_CODE_DEST

mov word [LABEL_DESC_CODE_DEST + 2], ax

shr eax, 16

mov byte [LABEL_DESC_CODE_DEST + 4], al

mov byte [LABEL_DESC_CODE_DEST + 7], ah

; 初始化数据段描述符

xor eax, eax

mov ax, ds

shl eax, 4

add eax, LABEL_DATA

mov word [LABEL_DESC_DATA + 2], ax

shr eax, 16

mov byte [LABEL_DESC_DATA + 4], al

mov byte [LABEL_DESC_DATA + 7], ah

; 初始化堆栈段描述符

xor eax, eax

mov ax, ds

shl eax, 4

add eax, LABEL_STACK

mov word [LABEL_DESC_STACK + 2], ax

shr eax, 16

mov byte [LABEL_DESC_STACK + 4], al

mov byte [LABEL_DESC_STACK + 7], ah

; 初始化堆栈段描述符(ring3)

xor eax, eax

mov ax, ds

shl eax, 4

add eax, LABEL_STACK3

mov word [LABEL_DESC_STACK3 + 2], ax

shr eax, 16

mov byte [LABEL_DESC_STACK3 + 4], al

mov byte [LABEL_DESC_STACK3 + 7], ah

; 初始化 LDT 在 GDT 中的描述符

xor eax, eax

mov ax, ds

shl eax, 4

add eax, LABEL_LDT

mov word [LABEL_DESC_LDT + 2], ax

shr eax, 16

mov byte [LABEL_DESC_LDT + 4], al

mov byte [LABEL_DESC_LDT + 7], ah

; 初始化 LDT 中的描述符

xor eax, eax

mov ax, ds

shl eax, 4

add eax, LABEL_CODE_A

mov word [LABEL_LDT_DESC_CODEA + 2], ax

shr eax, 16

mov byte [LABEL_LDT_DESC_CODEA + 4], al

mov byte [LABEL_LDT_DESC_CODEA + 7], ah

; 初始化Ring3描述符

xor eax, eax

mov ax, ds

shl eax, 4

add eax, LABEL_CODE_RING3

mov word [LABEL_DESC_CODE_RING3 + 2], ax

shr eax, 16

mov byte [LABEL_DESC_CODE_RING3 + 4], al

mov byte [LABEL_DESC_CODE_RING3 + 7], ah

; 初始化 TSS 描述符

xor eax, eax

mov ax, ds

shl eax, 4

add eax, LABEL_TSS

mov word [LABEL_DESC_TSS + 2], ax

shr eax, 16

mov byte [LABEL_DESC_TSS + 4], al

mov byte [LABEL_DESC_TSS + 7], ah

; 为加载 GDTR 作准备

xor eax, eax

mov ax, ds

shl eax, 4

add eax, LABEL_GDT ; eax

mov dword [GdtPtr + 2], eax ; [GdtPtr + 2]

; 加载 GDTR

lgdt [GdtPtr]

; 关中断

cli

; 打开地址线A20

in al, 92h

or al, 00000010b

out 92h, al

; 准备切换到保护模式

mov eax, cr0

or eax, 1

mov cr0, eax

; 真正进入保护模式

jmp dword SelectorCode32:0

; 执行这一句会把 SelectorCode32 装入 cs, 并跳转到 Code32Selector:0 处

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

LABEL_REAL_ENTRY: ; 从保护模式跳回到实模式就到了这里

mov ax, cs

mov ds, ax

mov es, ax

mov ss, ax

mov sp, [SPValueInRealMode]

in al, 92h ; ┓

and al, 11111101b ; ┣ 关闭 A20 地址线

out 92h, al ; ┛

sti ; 开中断

mov ax, 4c00h ; ┓

int 21h ; ┛回到 DOS

; END of [SECTION .s16]

[SECTION .s32]; 32 位代码段. 由实模式跳入.

[BITS 32]

LABEL_SEG_CODE32:

mov ax, SelectorData

mov ds, ax ; 数据段选择子

mov ax, SelectorVideo

mov gs, ax ; 视频段选择子

mov ax, SelectorStack

mov ss, ax ; 堆栈段选择子

mov esp, TopOfStack

; 下面显示一个字符串

mov ah, 0Ch ; 0000: 黑底 1100: 红字

xor esi, esi

xor edi, edi

mov esi, OffsetPMMessage ; 源数据偏移

mov edi, (80 * 10 + 0) * 2 ; 目的数据偏移。屏幕第 10 行, 第 0 列。

cld

.1:

lodsb

test al, al

jz .2

mov [gs:edi], ax

add edi, 2

jmp .1

.2: ; 显示完毕

call DispReturn

; Load TSS

mov ax, SelectorTSS

ltr ax

;在任务内发生特权级变换时要切换堆栈,而内层堆栈的指针存放在当前任务的TSS中,所以要设置任务状态段寄存器TR。

push SelectorStack3

push TopOfStack3

push SelectorCodeRing3

push 0

retf ; Ring0 -> Ring3,历史性转移!将打印数字 '3'。

; ------------------------------------------------------------------------

DispReturn:

push eax

push ebx

mov eax, edi

mov bl, 160

p bl

and eax, 0FFh

inc eax

mov bl, 160

mul bl

mov edi, eax

pop ebx

pop eax

ret

; DispReturn 结束---------------------------------------------------------

SegCode32Len equ $ - LABEL_SEG_CODE32

; END of [SECTION .s32]

[SECTION .sdest]; 调用门目标段

[BITS 32]

LABEL_SEG_CODE_DEST:

mov ax, SelectorVideo

mov gs, ax ; 视频段选择子(目的)

mov edi, (80 * 12 + 0) * 2 ; 屏幕第 12 行, 第 0 列。

mov ah, 0Ch ; 0000: 黑底 1100: 红字

mov al, 'C'

mov [gs:edi], ax

; Load LDT

mov ax, SelectorLDT

lldt ax

jmp SelectorLDTCodeA:0 ; 跳入局部任务,将打印字母 'L'。

;retf

SegCodeDestLen equ $ - LABEL_SEG_CODE_DEST

; END of [SECTION .sdest]

; 16 位代码段. 由 32 位代码段跳入, 跳出后到实模式

[SECTION .s16code]

ALIGN 32

[BITS 16]

LABEL_SEG_CODE16:

; 跳回实模式:

mov ax, SelectorNormal

mov ds, ax

mov es, ax

mov fs, ax

mov gs, ax

mov ss, ax

mov eax, cr0

and al, 11111110b

mov cr0, eax

LABEL_GO_BACK_TO_REAL:

jmp 0:LABEL_REAL_ENTRY ; 段地址会在程序开始处被设置成正确的值

Code16Len equ $ - LABEL_SEG_CODE16

; END of [SECTION .s16code]

; LDT

[SECTION .ldt]

ALIGN 32

LABEL_LDT:

; 段基址 段界限 , 属性

LABEL_LDT_DESC_CODEA: Descriptor 0, CodeALen - 1, DA_C + DA_32 ; Code, 32 位

LDTLen equ $ - LABEL_LDT

; LDT 选择子

SelectorLDTCodeA equ LABEL_LDT_DESC_CODEA - LABEL_LDT + SA_TIL

; END of [SECTION .ldt]

; CodeA (LDT, 32 位代码段)

[SECTION .la]

ALIGN 32

[BITS 32]

LABEL_CODE_A:

mov ax, SelectorVideo

mov gs, ax ; 视频段选择子(目的)

mov edi, (80 * 13 + 0) * 2 ; 屏幕第 13 行, 第 0 列。

mov ah, 0Ch ; 0000: 黑底 1100: 红字

mov al, 'L'

mov [gs:edi], ax

; 准备经由16位代码段跳回实模式

jmp SelectorCode16:0

CodeALen equ $ - LABEL_CODE_A

; END of [SECTION .la]

; CodeRing3

[SECTION .ring3]

ALIGN 32

[BITS 32]

LABEL_CODE_RING3:

mov ax, SelectorVideo

mov gs, ax ; 视频段选择子(目的)

mov edi, (80 * 14 + 0) * 2 ; 屏幕第 14 行, 第 0 列。

mov ah, 0Ch ; 0000: 黑底 1100: 红字

mov al, '3'

mov [gs:edi], ax

call SelectorCallGateTest:0 ; 测试调用门(有特权级变换),将打印字母 'C'。

jmp $

SegCodeRing3Len equ $ - LABEL_CODE_RING3

; END of [SECTION .ring3]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值