段寄存器和其他寄存器不同,类似EAX,EBX,EBP,ESP,都只有32位可读可写。而段寄存器只有16位可读,但是有96位可写,即除开可见的16位之外,还有隐藏的80位。以下是段寄存器的结构
1 | struct Segment{ |
段选子
Select
是段选择子(段选择符),关于段选子的构造如下
高13为是在GDT表或者是LDT表中查找内容的索引(index),公式为 offset = index*8
低两位是使用该段寄存器时候的的特权级(RPL)11(b)
=3 时为R3特权,00(b)
=0 时为R0特权
第3位(index为2)是指示位,为0的时候去GDT表中查,为1的时候去LDT表中查
段描述符
剩下的80位不可见的缓存就是根据段选子,在GDT or
LDT内查找8 byte的段描述符
得到的,以下是段描述符的构造
这个构造如此复杂,是因为这个结构是从16位系统拓展过来的(可以发现如果是16位,就只有``base和
limit`)
基地址
如图,Base 由高32位中的首尾各8位(共2 byte),加上低32位中的高16位(共2 byte)构成一个dword,储存在段寄存器的Base变量中。
Limit
如图,可以发现Limit拼凑起来只有20位,但是为了对齐补齐,使用32位的dword储存,放在段寄存器的Limit变量中
DPL 位
如图,高32位中的第13,14位是DPL位,该位描述了访问特权级,需要满足RPL<=DPL
才可以访问到该段(之后DPL,RPL,CPL的关系会单独说明)
P 位
表示该段是否可用,但是实验。。。(待做实验)
S 位
S位为0表示该段是系统段(调用门,任务门,陷阱门,中断门等),为1表示该段为普通段(一般是代码数据段)
TYPE 位
这里仅针对S位为1 即代码数据段做解释 S位为0时候的作为门描述符的后文再提
TYPE位一共有4位
第11位没有很大的用处,可以理解为:为0的时候是Data段,为1的时候是Code段(值小于8,或者大于等于8)
E位是扩展方向位,为0表示向上扩展—-Base~Limit
内部可访问,外部不可访问;为1表示向下扩展,Base~Limit
内部不可访问,外部可访问(左侧为向上扩展,右侧为向下扩展,红色为可以访问的部分)
W位是读写权限位,为0表示只读,为1表示可读可写
A位是访问位,如果访问过该段描述符,则把该位置为1
G 位
粒度(单位1)表示位,为0表示粒度为字节byte
,为1表示粒度为一个页4096byte
(2^12,加上Limit
2^20 刚好4GB,可以覆盖所有线性地址)
D/B 位
当该段是代码段的时候,是D位;当该段为数据段的时候,是B位
1.对于硬编码的操作宽度的影响:
D = 1 采用32位宽度;D = 0,采用16位宽度(比如push压栈,call寻址方式)
如果D = 1 ,想要采用16位宽度 需要在指令的操作数前面加上前缀67 比如push 67:0x1234
如果D = 0 ,想要采用32位宽度 需要在指令的操作数前面加上前缀66 比如push 66:0x12345678
2.对于向下扩展(E位为1)的数据段的影响:(这里可能写的有错误)
B = 1 ,向下扩展的上限为4GB,也就是说FS.Base + Limit之后的内容全部可以访问
B = 0 ,向下扩展的上限为64kb,也就是说只有FS.Base + Limit~ FS.Base + Limit + 64kb 的内容可以访问
RPL、DPL、CPL之间的关系
RPL:存在于每个段寄存器的可见部分的低两位,表示了当前段的权限
CPL:一般来说我们称CS段的RPL为CPL,也有说SS段的RPL也成为CPL,大概就是当前线程空间(代码空间和栈空间)的权限
DPL:当我们要申请一个段的时候(更新一个段寄存器),将要申请的目标段所具备的权限称为DPL(by 源哥)
如果要访问 / 申请另一个段,需要满足权限上RPL>=目标.DPL
即当前CS代码段需要有比目标段相同或者更高的权限,符合实际
程序在执行每一条汇编语句的时候都要检查 权限上 CS.CLP >= CS.DPL
相当于我代码段访问我自己,权限上 RPL>=目标.DPL