汇编学习笔记(17) - 运行权的转移与特权级
特权级的基本概念
CPL 当前代码的特权级 = CS寄存器中的RPL
RPL 选择子中要求的特权级
DLP 目标段中要求的特权级
好比如说我是ROOT 用户拥有root权限, 我是用读权限打开一个文件, 这个文件的权限是 777
CPL = ROOT 权限
RPL = 读权限
DLP = 777
数据段的装载
ss 寄存器
- 可读写数据段
- CPL=DL=RPL
其他数据段寄存器
- 可读可写数据段,可读代码段
- CPL<=DPL, RPL<=DPL
运行权的变更(CS的装载 )
指向代码段
JMP
CALL
要求
非一致代码,CPL = DPL, RPL<=DPL
一致代码,CPL >= DLP
结果
CS 的INDEX 和TI 被改变 而CPL不变
RET
要求
RPL = CPL
RET返回使用的是之前CS的选择子,因为在CALL的时候CPL是不变的,所以无意外的话RPL=CPL总是成立的之后就相当于CALL 和 JMP 了
提炼:
- 特权级永远不会变
- 对于转入非一致代码 要求 CPL = DPL,则强制规定了 非一致代码只能同级转入
- 对于转入一致代码要求CPL >= DLP, 则一致代码只能同级或者从外层向内转,但是无论哪种情况特权级不变
- RET 用的是压在堆栈中的之前的CS,所以 RPL=前 CPL, 因为CPL保持不变所以 前CPL=CPL总成立,所以RET的首要条件总是满足
- 由于 非一致代码要求 CPL = DPL 和 一致代码要求 CPL >= DPL 的要求看,而CPL不变,所以合法的调用链中 CPL >= DLP 是永远成立的
- 由4可知RPL就是CPL的压栈,而CPL不变,所以可知RPL = CPL, 所以得出结论,调用栈中 RPL=CPL>=DLP
- 一致代码不要求 RPL,所以从一致代码返回一致代码是可能出现的RPL > DLP的情况被忽略,而因为非一致代码都是CPL=DPL的,所以无论从何处返回非一致代码 情况都是 CPL=DPL=RPL(满足6中RPL=CPL>=DLP的情况),也能正常返回
小结
1. 使用CALL 和 JMP的时候根据转移目标代码的不同需要满足如下情况
目标是非一致代码:CPL = DPL, RPL<=DPL
目标是一致代码: CPL >= DLP
2. CPL 永远不变
3. 只要CALL满足执行条件 对应的RET永远也满足条件
指向调用门
首先指向门的调用中只有选择子有效,偏移地址是会被忽略的
其次调用门有自己DPL,所以想要使用调用门需要满足如下场景
CPL < 门DPL, RPL < 门 DPL
满足条件之后会从调用门中取出选择子和偏移地址,来替换掉转移指令中的选择子和偏移地址,在替换之前会将新选择子的RLP设为0,也就是说门中的选择子的RPL是没意义的始终会被替换成0.
之后和 直接指向段的转跳大致相当,只要当使用CALL指令转移到非一致代码的情况有特殊
当使用CALL指令指向的调用门指向了非一致代码段的时候
如果出现 CPL > DPL的情况 在直接指向的情况下是会出错的,但是在指向门的情况下 会出现 设置CPL = DPL,也就是说出现提权的情况。
对应的RET指令在发现RPL>CPL的时候,也会设置CPL = RPL 也是就说降权。
注意:权限切换的时候还涉及到堆栈的切换,这个将在任务门相关内容的时候解释。
小结
1. 指向门是只有选择子有效,偏移地址无意义
2. 调用的时候会有 CPL < 门DPL, RPL < 门 DLP 的验证
3. 实际调用的地址由门给出,同时实际调用的RPL总是0
4. 其他调用规则同直接指向一致,唯一的区别是当CALL实际指向的地址是非一致代码的时候如果出现 CPL > DPL 的情况不会调用失败,而是设置CPL = DPL,对应的RET会发现RPL>CPL的时候会设置CPL=RPL,这里的RPL实际就是CALL指令压栈的CS寄存器的CPL,也就是或恢复特权级。
指向TSS数据
要求权限,CPL <=DPL RPL<= DPL
调用指令中的偏移地址被忽略,CPU直接用TSS中的数据直接切换任务(线程)了。
指向任务门
和直接指向TSS的数据没啥区别,就是多了些权限检测
首先检测 CPL < 门DPL, RPL <= 门DPL
然后检测 CPL < TSS的DPL, 门RPL <= TSS的DPL
然后就使用TSS的数据切换任务,指令和门中的偏移都没意义。
注意这里用JMP和用CALL 是有一点区别的
CALL 会引起任务的嵌套,所以使用CALL进行任务门的调用的话是可以用RET返回原来的任务的
特权级切换时堆栈的切换
在特权级发生切换的时候会有堆栈的切换,每个特权级都有自己的堆栈,这些地址是被保存在TSS中的,而且 0 1 2 这三个特权级的堆栈,在每次进去时候都会被重置,也就是说不会保留上次进入时的痕迹,而为了在特权级变更的时候传递参数,都会发生内存的拷贝,至于拷贝的字节数,任务门是有任务门的双字节字段决定,中断门和陷阱门有中断或者异常类型自动决定。