你的位置:首页 > 操作系统

[操作系统]Linux内核系列—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	retfSegCodeDestLen	equ	$ - LABEL_SEG_CODE_DEST; END of [SECTION .sdest]

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

LABEL_DESC_CODE_DEST: Descriptor 0,SegCodeDestLen-1, DA_C+DA_32; 非一致代码段,32SelectorCodeDest	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_DPL3LABEL_DESC_STACK3:   Descriptor 0,   TopOfStack3, DA_DRWA+DA_32+DA_DPL3; 堆栈段ring3[SECTION .s3]ALIGN	32[BITS	32]LABEL_STACK3:	times 512 db 0TopOfStack3	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表明从低特权级到高特权级的转移。

 

一个码农的日常 

源码