操作系统开发系列—5.特权级及特权级的转移

CPL——当前执行的程序或任务的特权级,它被存储在cs和ss的第0位和第1位上。

DPL——段或者门的特权级,如果是数据段DPL则规定了可以访问此段的最低特权级

RPL——通过段选择子的第0位和第1位表现出来的。处理器通过检查RPL和CPL来确认一个访问请求是否合法。RPL保证了操作系统不会越俎代庖地代表一个程序去访问一个段。

我们先来展示一下特权级错误访问版本。

先把LABEL_DESC_DATA对应的段描述符的DPL修改为1:

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

继续修改,把选择子的RPL改为3:

SelectorData		equ	LABEL_DESC_DATA		- LABEL_GDT + SA_RPL3

运行结果如下:

虚拟机崩溃。因为RPL & CPL 必须 <= DPL

下面就演示一下从低特权转到高特权的方法:

通过jmp和call所能进行的代码段间转移是非常有限的,对于非一致代码段,只能在相同特权级代码段之间转移。遇到一致代码段也最多能从低到高,而且CPL不会改变。如果想自由地进行不同特权级之间的转移,显然需要其他几种方式,即运用门描述符或者TSS。调用门描述符格式如下:

一个门描述了由一个选择子和一个偏移所指定的线性地址,程序正是通过这个地址进行转移的。

以下是通过调用门转移的目标段:

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

LABEL_SEG_CODE_DEST:
	;jmp	$
	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

	retf

SegCodeDestLen	equ	$ - LABEL_SEG_CODE_DEST
; END of [SECTION .sdest]

下面是代码段描述符,选择子及初始化描述符的代码:

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

SelectorCodeDest	equ	LABEL_DESC_CODE_DEST	- LABEL_GDT

; 初始化测试调用门的代码段描述符
	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

现在添加调用门:

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

宏Gate的定义在pm.inc中

描述符的属性是DA_386CGate表明是一个调用门。里面指定的选择子是SelectorCodeDest,表明目标代码段是刚刚新添加的代码段。偏移地址是0,表示将跳转到目标代码段的开头处。另外,我们把其DPL指定为0.

现在调用门准备就绪,它指向的位置是SelectorCodeDest:0,即标号LABEL_SEG_CODE_DEST处的代码。

假设我们想由代码A转移到代码B,运用一个调用门G,即调用门G中的目标选择子指向代码B的段。代码B的DPL记做DPL_B,在用call指令时,要求目标代码DPL_B<=CPL;在用jmp指令时,只能是DPL_B=CPL。

现在添加一个低特权的代码段ring3和堆栈:

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

; 堆栈段ring3
[SECTION .s3]
ALIGN	32
[BITS	32]
LABEL_STACK3:
	times 512 db 0
TopOfStack3	equ	$ - LABEL_STACK3 - 1
; END of [SECTION .s3]

; CodeRing3
[SECTION .ring3]
ALIGN	32
[BITS	32]
LABEL_CODE_RING3:
	mov	ax, SelectorVideo
	mov	gs, ax

	mov	edi, (80 * 14 + 0) * 2
	mov	ah, 0Ch
	mov	al, '3'
	mov	[gs:edi], ax

	jmp	$
SegCodeRing3Len	equ	$ - LABEL_CODE_RING3
; END of [SECTION .ring3]

执行如下:

打印了红色的3,表明我们由ring0到ring3的转移成功。接下来试验一下调用门的使用。

把调用门的描述符和选择子改成特权等级为3.还有从低特权级到高特权级转移的时候,需要用到TSS,我们来准备一个TSS。

LABEL_DESC_TSS:        Descriptor 0,          TSSLen-1, DA_386TSS

; 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

我们需要在特权级变换之前加载它

	mov	ax, SelectorTSS
	ltr	ax

运行结果如下:

 

看到字母C表明从低特权级到高特权级的转移。

一个码农的日常 

源码

原文地址:https://www.cnblogs.com/joey-hua/p/5374216.html