CPL和DPL入门
RPL,请求特权级,在选择之子中的0~1位。
选择子结构
15~3 | 2 | 1~0 |
---|---|---|
描述符索引值 | TI | RPL |
计算机中具备能动性,能够访问其他资源的只有计算机指令。而计算机当前在执行的指令是由CS:EIP指向的,所以位于CS寄存器选择子中低2位的值不仅称为请求特权级,又称为处理器的当前特权级。也就是说处理器的当前特权级是CS.RPL.
段描述符格式(8字节大小)
DPL(Descriptor Privilege Level) 描述符特权级
31~24 | 23 | 22 | 21 | 20 | 19~16 | 15 | 14~13 | 12 | 11~8 | 7~0 |
---|---|---|---|---|---|---|---|---|---|---|
段基址31~24 | G | D/B | L | AVL | 段界限19~16 | P | DPL | S | TYPE | 段基址23~16 |
31~16 | 15~0 |
---|---|
段基址15~0 | 段界限15~0 |
在CPU中运行的是指令,其运行过程中的指令总会属于某个代码段,该代码段的特权级,也就是代码段描述符中的DPL字段,便是当前CPU所处的特权级,即CPL(Current Privilege Level),它表示处理器正在执行的代码的特权级别。除了一致性代码段外,转移后的目标代码段的DPL是将来处理器的当前特权级CPL。
在任意时刻,当前特权级CPL保存在CS选择子的RPL部分。当前正在运行的代码所在的代码段的特权级DPL就是处理器的当前特权级。
当使用了一些改变程序执行流的指令,如int,call等,就会使CS和EIP的值改变,从而使CPU执行到了不同特权级的代码。在执行特权转移时,CPU要检查特权变换的条件。
当处理器特权级检查的条件通过后,新代码段的DPL就变成了处理器的CPL,也就是目标代码段描述符的DPL将保存在代码段寄存器CS中的RPL位。
特权级检查
特权级检查发生在访问者访问受访者的一瞬间,只检查一次,在检查过后,在该段上以后的执行过程中也不会在检查。
对于受访者为数据段(段描述符中type字段未有X可执行属性)来说,只有访问者的权限(CPL)大于等于该DPL表示的最低权限才能够继续访问。从数值上来说就是。CPL <= DPL.
对于受访者为代码段(段描述符中type字段有X可执行属性)来说,只有访问者的权限等有该DPL表示的最低权限才能够继续访问,即只能平级访问。即CPL=DPL;
唯一一种处理器会从高特权级降到低特权级的情况:处理器从中断处理程序中返回到用户态的时候。
中断处理都是0特权级级下进行的,因为中断发生很多是硬件中断,处理硬件需要在0特权级,这是因为eflag寄存器中的IOPL位的值通常被设置为0(该位的作用是限制访问IO端口的最低特权级)。
中断发生过程复习
用户进程是在3特权级,在运行用户程序时若发生了中断,CPU会暂停用户程序的执行,随后CPU会自动从3特权级进入0特权级,在0特权级下将执行用户程序时的现场环境(也就是上下文)保存起来(这个保存上下文的动作可以由CPU通过TSS完成,这是CPU在硬件上提供的功能,但其效率不高,大多数操作系统都是自己写代码手动保存上下文环境),待中断处理程序完成后,CPU会恢复用户程序的执行,会回到3特权级。
一致性代码段
执行一致性代码段的指令,可以即执行高特权级代码段上的指令又不提升特权级。在数值上,只要CPL>=一致性代码段的DPL,就可以跳转过去执行,而且不会改变CPL(CS.RPL).在段描述符中,如果该段为非系统段(段描述符的S字段为0),type字段中的C为为1就表示该段为一致性代码段,为0就不是。
代码段可以有一致性和非一致性之分,但所有数据段总是非一致的,即数据段不允许被本数据段特权级更低的代码段访问。
如何判断一段内存区域是代码段还是数据段?
当该内存段对应的段描述符中的S字段为0,type字段中的X位(可执行位)为1,就表示代码段。(我任务可以把代码段叫做非系统可执行段)
当该内存段对应的段描述符中的S字段为0,type字段中的X位(可执行位为)0,就表示数据段。(我认为可以把数据段叫做非系统不可执行段)
当该内存段对应的段描述符中的S字段为0,type字段中的X位为0,C,R位为1,就表示栈段(非系统,不可执行,向下扩展,可读写段)
段描述符中的type类型(非系统段)
内存段类型 | X | C | R | A | 说明 |
---|---|---|---|---|---|
代码段 | 1 | 0 | 0 | * | 只执行代码段 |
代码段 | 1 | 0 | 1 | * | 可执行,可读代码段 |
代码段 | 1 | 1 | 0 | * | 可执行,一致性代码段 |
代码段 | 1 | 1 | 1 | * | 可执行,可读,一致性代码段 |
内存段类型 | X | E | W | A | 说明 |
数据段 | 0 | 0 | 0 | * | 只读数据段 |
数据段 | 0 | 0 | 1 | * | 可读写数据段 |
数据段 | 0 | 1 | 0 | * | 只读,向下扩展的数据段 |
数据段 | 0 | 1 | 1 | * | 可读写,向下扩展的数据段 |
A | C | R | X | E | W |
---|---|---|---|---|---|
Accessed位,每当cpu访问过该段后,此位置1,新创建段描述符时应将此位置0. | 一致性代码段标志 | 可读 | 可执行 | Extend,标示段的扩展方向,0标示向上扩展,1标示向下扩展(栈段) | 可写 |
门,调用门与RPL序
门结构有4种,调用门,中断门,陷阱门,任务门。
调用门:call和jmp指令后接调用门选择子为参数,以调用函数的形式实现从低特权向高特权转移,可以用来实现系统调用。call指令使用调用门可以实现向高特权代码转移,jmp指令指令使用调用门只能向平级代码转移。
中断门:以int指令主动发中断的形式实现从低特权向高特权转移,linu系统调用使用中断门实现
陷阱门:以int3指令主动发中断的形式实现从低特权向高特权转移,一般是编译器调试时用。
任务门:任务以任务状态段TSS为单位,用来实现任务切换,它可以节奏中断或指令发起。当中断发生时,如果对应的中断向量号是任务门,则会发起任务切换。也可以像调用门那样,用call或jmp指令后接任务门的选择子或任务TSS的选择子。
处理器只有通过门结构才能由低特权级转移到高特权级。门结构就是记录一段程序起始地址的描述符。段描述符是用来描述内存段的,门描述符是用来描述一段程序的。同段描述符类似,大小也是8字节。
调用门一般在过去多段模型下使用,大多数情况下需要为调用门制定段选择子。现代操作系统为了方便,采用了平坦模型,所有用户进程共享几个选择子,比如用户代码段选择子和用户数据段选择子各一个,由所有用户进程共享,用户进程不在需要提供选择子。所以调用门可以用中断门替代了。
如何使用调用门使用内核中的函数?
31~16 | 15 | 14~13 | 12 | 11~8 | 7 | 6 | 5 | 4~0 |
---|---|---|---|---|---|---|---|---|
被调用函数在目标代码段内的偏移量的第31~16位 | P | DPL | S(0) | TYPE(D100) | 0 | 0 | 0 | 参数个数 |
31~16 | 15~0 |
---|---|
被调用函数所在代码段的描述符选择子 | 被调用函数在目标代码段内的偏移量的第15~0位 |
处理器在硬件上实现参数的自动复制,即用户进程压在3特权级栈中的参数自动复制到0特权级栈中(调用门)
1: call 调用门选择子。 (假设在GDT中,也可以在LDT中)CPU用门描述符选择子中的高13位索引位乘以8作为该描述符在GDT中的偏移量,在加上寄存器GDTR中的GDT基地址,最终找到门描述符的地址。
2:再用被调用函数所在代码段的描述符选择子重复上述步骤,找到对应的代码段描述符。
3:在该代码段描述符中找到内核代码段基址,用它加上调用门描述符中记录的内核函数在代码段中的偏移量,最终找到内核函数的起始地址。
以上为了讨论方便,执行过程未涉及分页机制。
ret 近返回指令。执行的时候,处理器从栈中弹出一个字到IP中。
retf 远返回指令。执行的时候,处理器先从栈中弹出一个字的IP,再弹出一个字到cs。
RPL
操作系统提供了一致性代码段和门结构来实现从低特权级到高特权级的代码段转移,这会给恶意攻击者用低特权级的程序访问高特权级资源,使攻击者有诸如篡改内核之类的危险操作的机会,因此必须在CPL和DPL的基础上增加条件。
访问者穿越特权屏障是因为操作系统允许通过特定方式实现从低特权级代码段到高特权级代码段的转移,即通过高特权级代码段来间接获得自身无法拥有的权限。由于真正的资源请求者是低特权级代码段,因此需要标识出资源请求者的真实身份,这就是请求特权级(Request Privilege Level,RPL)的作用。
RPL代表了真正资源请求者的特权级,因此在请求特权级为DPL的资源时,需要检查的不仅是CPL,还要加上RPL,CPL和RPL的特权级必须同时大于等于受访者的DPL。
RPL引入的目的是避免低特权级的程序访问高特权级的资源,有了RPL后,访问内存段的特权检查规则如下(不通过调用门):
·如果目标为非一致性代码段,要求数值上:CPL=RPL=目标代码段DPL
·如果目标为一致性代码段,要求数值上:CPL≤目标代码段DPL && RPL≥目标代码段DPL
·如果目标为数据段时,要求数值上:CPL≤目标数据段DPL && RPL ≤ 目标数据段DPL
栈段的特权级检查比较特殊,因为在各个特权级下处理器都有对应的栈,所以往段寄存器中赋予选择子时,要求CPL等于栈段选择子对应的数据段的DPL,即数值上CPL=RPL=用作栈的目标数据段DPL。