win-段寄存器
ES CS SS DS FS GS LDTR TR
段寄存器长度:96位
实模式
实际地址=段地址左移4位+偏移量
eg:
1 | mov ds:[si],ax |
保护模式:
保护模式下,cpu依旧通过短地址和偏移量寻址,但是段寄存器不直接保存偏移,下面是段寄存器在保护模式下的结构:
1 | struct SegMent//示意 |
段选择子:
002B:(前8位是0)00101 0 11
其中低2位为当前请求权限级别RPL
:从r0到r3,其中CS寄存器的低2位存着cs中eip的偏移,即cs指向当前代码段,它的RPL被称为CPL
,同理于指向堆栈的SS寄存器
第3位表示查询哪张表:一般有两张表:GDT和LDT表,0表示GDT,1表示LDT(LDT必须嵌套在GDT中,同时可以有多张LDT)
如果是1:会装载某一个ldt表,通过段选择子在gdt中找到ldt执行后续操作
前5位表示段描述符索引,指向GDT中存放段描述符的地址,即gdt[x],n为前五位的值,所以
1 | 段描述符位置 = gdt首地址 + x*8 |
其中段描述符存放了除了段选择子之外的属性
其中:
绿色表示段基址
蓝色表示段界限
黄色表示段属性
段属性中,先看P,DPL,S,TYPE这几段
1 | P占一位: |
其中一致位表示是不是一致代码段。
这里需要先看一下这个定义:
操作系统保护模式下把代码段分为一致代码段和非一致代码段的原因是:内核程序和用户程序要分开,内核程序不能被用户程序干扰。但是有时候用户程序也需要读取内核的某些数据,于是操作系统就从内核程序中分配一些可以供用户程序访问的段,但是不允许用户程序写入数据,用户程序访问这些段时遵循以下规则:
- 内核程序不知道用户程序的数据,不调用用户程序的数据,也不转移到用户程序中来
- 用户程序只能访问到内核的某些共享段,这些段称为
一致代码段
- 用户程序不能访问内核不共享的段
所以一致代码段就是操作系统内核拿出来的共享段:
性质是
- 特权级高的程序不允许访问特权级低的数据:即内核态不允许调用用户态的数据。
- 特权级低的程序可以访问到特权级高的程序,但是特权级不会改变,即不会从用户态切换到内核态。
所以如果是低特权级到高特权级,可以直接访问,且权限不变,如果是高特权级到低特权级,触发常规保护错误。
非一致代码段:为了避免低特权级的访问而被操作系统保护起来的系统代码
性质是
- 只允许同特权级访问。
- 绝对禁止不同特权级直接访问:内核态不去用户态,用户态也不使用内核态。
- 通常低特权级代码必须通过
门调用
来实现对高特权级代码段的访问和调用。
所以如果特权级不相等,触发常规保护错误
这里的特权等级检测是根据DPL和CPL的值来判断的
1 | DPL>=CPL |
1 | 如果S=0,系统段 |
另外4个属性
1 | AVL 占1位 |
那么又有一个问题:
为什么段寄存器一共有96位,这里不是一共才64(段描述符)+16(段选择子)=80位吗
这里会发现:段描述符中的limit段是3字节,最大值为ffffff,又有G可以表示最大单位为4KB,再加属性里面的保留的一(十六进制)位空位
(ffffff-1)*4096=ffffffff
(8+4)x4+8x4+16=96
这个不像数组,界限为FFFFFFFF那么它可以取到0~FFFFFFFF
参考资料
RPL保存在选择子里,那么CPL是保存在哪里的_cpl 在哪里-CSDN博客
x86保护模式——全局描述符表GDT详解_gdt全局描述符表 作用-CSDN博客
段选择器成员
FS地址不一定和图中相同
通过以下代码查看对应段寄存器的读写属性
(由于)
1 |
|
这个程序可以通过,且表明了ss可读,ds可写的属性
将其换为
1 |
|
发现调试状态可以执行,直接运行会在输出时报错
关于为什么不能直接用立即数给段寄存器赋值:
网上的解释:
不懂QAQ
在线程和进程不改变时,在写之后的读不会读gdt,在手工修改进程线程时,会读一次gdt