avatar

Catalog
段描述符属性

之前介绍了,段描述符是用来填充段寄存器余下位置的,然而段寄存器余下位置有80位,而段描述符仅有64位,那到底是如何填充的呢?这篇就从这个问题开始,逐步探究段描述符的属性。首先,回顾一下段描述符的结构:

P位

P位,位于段描述符高4字节的第15位,是判断描述符是否有效的位置。

  • P = 1:该描述符有效

  • P = 0:该描述符无效

G位

在解析G位前,先来回顾下之前的问题,64位的段描述符到底是如何分配给段寄存器余下80位的。 首先回顾一下段寄存器的结构:

c
1
2
3
4
5
6
struct SegMent {
WORD selector;
WORD attribute;
DWORD base;
DWORD limit;
}
  • 段选择子: 由mov/les/lds/lss/lfs/lgs指令直接写入16位。

  • 属性:段描述符中高4字节的第8-23位,刚好16位,作为属性写入段寄存器。

  • 基址:将段描述符高4字节中第24-31位,与第0-7位拼接成作为高16位,低四字节的第16-31位作为低16位,拼接成32位,作为Base,写入到段寄存器里。 (这里没用~号是因为会转义成删除线)

  • 限长:这里就要用到G位了。首先观察段描述符结构,可以发现,在高4字节的第16-19位,与低4字节的第0-15位,都是段限长,将他们拼接起来,也就是20位,那这20位是如何扩展成32位呢?这里就要用到这部分的关键G位了,当G的值为0时,表示以字节单位,这时,假设Limit的值加起来为FFFFF(20位),则取0x000FFFFF作为Limit写入段寄存器;当G的值为1时,表示以4KB单位,这样去理解,如果一个段的大小为1KB,也就是1024B或0x400B,这时,实际上能取的范围是0-1023或0-0x3FF,所以此时的Limit应该为3FF。这样当以单位为4KB来计算一个段的Limit时,若Limit的值为1,说明可以取0和1两个值,也真正的大小实际上是2,所以用2*4KB=8192B=0x2000B,但是真是可以取到的值为0-1FFF,所以此时写入段寄存器Limit的值为1FFF。同理,若段描述符Limit的值为FFFFF,真正写入段寄存器的值为FFFFFFFF(32位)。

    具体的公式如下:

    Code
    1
    2
    3
    4
    5
    G = 0:  = Limit
    G = 1: (Limit + 1)*4KB - 1
    = Limit*4KB + 4KB - 1
    = (Limit<<12) + 0xFFF
    = (Limit<<12)|0xFFF

以上也就真正完成了段描述符拆分写入段寄存器的过程,值得注意的是,FS对应的段描述符较为特殊,拆分后的值与段寄存器中的值不符合,后续会再次说明。

S位

位于段描述符高4字节的第12位

  • S = 1:描述代码段或数据段
  • S = 0: 描述系统段

这里教大家一个小g巧,S位在第12位,P位在第15位,在Windows操作系统中,DPL只有可能是11或者00这两种情况,所以当第12~16位是值为1001(9)或1111(F)时,该段描述符一定是数据段或者代码段。此外系统段大多数情况下值为1000(8),因为代码段或者数据段很少拥有DPL值为0的权限。

Type域

Type域位于段描述符的第8~11位,不同段,每个位所含意义也不同

数据段

Code
1
S位 == 1 && 第11位 == 0

由图,数据段具有三个属性位,E,W,A

A:Accessed,表示该段描述符是否有过被加载到段寄存器。

W:Write,表示该描述符所描述的数据段,是否可写。

E:Expand,扩展方向。值为0时,正常向上扩展;值为1时,向下扩展。

由图,正常情况下,E位为0时,扩展方向是正常向上的,假设已知fs.Base和Limit,右图绿色部分即为有效部分(这样理解,Windows中堆栈都是由高到低,所以向上扩展的情况如右图绿色部分) 当E位为1时,扩展方向是向下的,此时,相同条件下,左图的绿色部分为有效范围。

代码段

Code
1
S位 == 1 && 第11位 == 1

由图,数据段具有三个属性位,E,W,A

A: Accessed,同数据段

R:Read,表示该描述符所描述的代码段,是否可读。

C:一致位:

  • C = 1:一致代码段
  • C = 0: 非一致代码段

具体如何区分,后面会详细说明

由于代码段和数据段的主要区别是第11位,所以只需要判断8~11位这个16进制数是否大于8就可以确定该段为代码段还是数据段。

系统段

当S=0时,该段描述符为系统描述符,具体分类如下:

DB位

  1. 对CS段的影响:
    • D = 0:采用32位寻址方式
    • D = 1:采用16位寻址方式
  2. 对SS段的影响:
    • D = 1:隐式堆栈访问指令(如:PUSH POP CALL) 使用32位堆栈指针寄存器ESP
    • D = 0:隐式堆栈访问指令(如:PUSH POP CALL) 使用16位堆栈指针寄存器SP
    • 隐式堆栈访问指令:例如push ebp,这句指令没有出现esp却修改了esp的值,就叫做隐式堆栈访问指令
  3. 向下扩展的数据段:
    • D = 1:段上线为4GB
    • D = 0:段上线为64KB
    • 实际上是限制扩展有效范围,大致如下

由于DB,对于64位的系统来说,理论上影响不大,所以这个位暂不深究,以后若是用到再返回讨论

参考教程:https://www.bilibili.com/video/av68700135?p=11

Author: cataLoc
Link: http://cataloc.gitee.io/blog/2020/03/09/%E6%AE%B5%E6%8F%8F%E8%BF%B0%E7%AC%A6%E5%B1%9E%E6%80%A7/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
Donate
  • 微信
    微信
  • 支付寶
    支付寶