-
-
[原创]5.滴水中级班(内核驱动)——段描述符属性之S位、TYPE域
-
发表于: 2025-9-20 00:26 465
-

S位是段描述符中高四字节的第12位,Type域是高四字节里的第8~11位。海哥强调——段描述符的属性一定要有层次地学习,在整个GDT表中的段描述符分为两大类,一类是数据段或者代码段的描述符,还有一类称为系统段描述符。我们真正想拆分一个段描述符的属性的时候,它的顺序是这样:①先判断P位,因为P位决定段描述符是否有效。②看S位,根据S位将其分为两大类,当S位为1的时候,要么是一个代码段的描述符、要么是一个数据段的描述符;当S位为0的时候,就是另外一大类也就是系统段的描述符。
补充:
S位(英文:Descriptor Type,或System bit)是x86段描述符高四字节的第12位。它的英文全称是Descriptor Type bit,有时候也被称为System bit。
S=1:该段描述符描述的是代码段或数据段(即普通的程序代码段或数据/栈段)。此时,段描述符的Type域(高四字节的位8~11)具体定义该段是代码段还是数据段,以及可读、可写、可执行等属性。
S=0:该段描述符描述的是系统段,包括任务状态段(TSS)、局部描述符表(LDT)、调用门、中断门等,这些不是普通程序段,而是用于操作系统管理的特殊数据结构。
为什么要有数据段和代码段?
代码段(Code Segment)和数据段(Data Segment)的区分源于处理器对内存保护和安全的需求:
代码段:用于存储可执行指令。CPU执行指令时,会自动从代码段(CS段寄存器所指向的段)读取指令。代码段通常设置为“可执行、可读”,但在x86设计上可以禁止写入,从而防止程序指令被意外或恶意修改,提高安全性。
数据段:用于存储程序数据(如变量、数组等)。数据段通常设置为“可读、可写”,允许程序读写数据,但通常不允许执行(防止数据区被当做代码执行,减少漏洞风险)。
设计目的:
隔离与保护:通过区分代码段与数据段,硬件可以在运行时检测非法操作(如向代码段写入数据,或将数据段当做代码执行),防止程序出错或恶意利用。
权限控制:可以分别设置代码段和数据段的访问权限(如只读、可写、可执行等),实现更细粒度的内存保护。
兼容与扩展:早期x86架构只有“段”的概念,后来为了支持保护模式、多任务和现代操作系统,逐步增加了更复杂的段属性(如S位、Type域等)以区分不同类型的内存区域。
总结
S位(Descriptor Type/System bit):决定段描述符是普通代码/数据段(S=1)还是系统段(S=0)。
Type域:在S=1时,进一步区分是代码段还是数据段,并定义具体的访问权限。

S位拆分完了以后,在S位的基础上再去了解Type域是什么含义,因为Type域里面的四位,在S位为1或者为0的时候是完全不一样的。

这张表是用来说明 x86 架构下**段描述符中 Type 域(类型域)**的编码方式以及每个编码所代表的段类型和具体权限。理解这张表,你需要先清楚三点:
1. “S位”为代码/数据还是系统段的分界
S = 1:类型为普通段(代码段或数据段),即程序运行时用来保存代码或者数据。
S = 0:系统段描述符,比如TSS、中断门、调用门等,和执行代码/数据读写无直接关系。
这张表只展示了S = 1(即普通段,代码或数据段)的Type域类型;系统段的Type编码另有一张表(Intel手册3.5节/3-2图)。
2. TYPE域的4位含义(E/W/C/R/A)
TYPE域(4位,通常记作 E/W/C/R/A,分别在高四字节的8~11位)具体是:
A (Accessed):是否访问过,CPU第一次加载该描述符时会置1。
W (Writable)/R (Readable):对于数据段是可写/只读,对于代码段是可读/只读。
E (Expand Down)/C (Conforming):数据段用Expand Down表示向下扩展(常见于栈段),代码段用Conforming表示特权级别可转移。
Descriptor Type:指定当前到底是代码段还是数据段(这一位由S决定,表里已拆开)。
3. 表头、表内容怎么对应
表头每列分别对应4位二进制值,十进制一列是那4位的组合。
上半部分是数据段(type 0~7):描述该段是否只读可写(W),是否expand down(E),是否访问过(A)等。
下半部分是代码段(type 8~15):描述该段是否可执行,可读(R),是否conforming(C),是否访问过(A)。
典型例子:
数据段(Data)
Type = 2 (0010):Read/Write(可读写但未访问)
Type = 7 (0111):Read/Write、expand-down、accessed
代码段(Code)
Type = 10 (1010):Execute/Read(可执行且可读,但未访问)
Type = 15 (1111):Execute/Read、conforming、accessed
4. 具体描述说明
| Type十进制 | 二进制 | 描述符类型 | 描述说明 |
|---|---|---|---|
| 0 | 0000 | Data | 只读 |
| 2 | 0010 | Data | 可读可写(最常用数据段类型) |
| 8 | 1000 | Code | 只执行,不可读 |
| 10 | 1010 | Code | 可执行可读(常用代码段类型) |
| 13 | 1101 | Code | 可执行、conforming、已访问(跨特权级调用常用) |
5. 用法重点
编写内核/驱动/逆向分析时,最常见的数据段是 type=2(可读可写,data),代码段是 type=10(可执行可读,code)。
类型的选择直接影响段权限和保护模式下的特权级访问。
CPU只有在段选择子装载到段寄存器时,才会自动修改A位,其他位由操作系统或应用程序初始化。
图的总结归纳
第一列(Decimal)是Type域编码,直接写段描述符时要用到的数值。
后面E/W/C/R列对应段属性;Data行和Code行根据S=1时不同解释。
最右侧的Description是段的行为和访问权限。
你不懂的点其实就是:这些位都是用来描述内存段的读写、访问、扩展行为的,S决定是代码/数据还是系统段,Type的四位具体再控制段细节权限。编写描述符时,根据需要选择合适的Type编码即可。
如果你以后遇到 type 字段是 0xA,就是代码段,可执行可读。如果是 0x2,就是数据段,可读可写,只要对照表填属性即可。


另外这里还额外提了一下DPL位,说是让记忆一下,DPL位也就是13、14位,在Windows里面只能出现两种情况,或者都是0、或者都是1。如果我们想分析代码段和数据段的Type域的属性,那么S位必然为1(S位性质),P位也必然为1(为1时才有效),DPL如果都为0,那么这几位的值就是1001也就是9,反之如果DPL位都为1,那么就是1111也就是F,所以当我们把GDT表拿出来的时候,如果我们想分析代码段或者数据段的时候,只需要看十六进制的从左往右数,索引从1开始,第五位是不是9或者f即可。其他的就不用管了,因为其他的一定不是代码段或者数据段。
代码段还是数据段该如何确认?就是看Type域,Type域的第11位如果为0那就是一个数据段,如果为1那就是一个代码段。所以从左往右数,索引从1开始,第六位可以判断到底是代码段还是数据段。这里我们可以举例00cf9b00和00cffb00,这两个位置的第六位都是b,而b是大于8的,所以根据第十一位均为1可知这两个都是代码段的。

所以段描述符的属性学习的时候,一定要有层次感,先看P位,然后看S位,通过S位区分两大类,S=1的时候是代码段或者数据段,S=0是系统段。这个拆分完后我们却根据第六位去看到底是数据段还是代码段,第六位的值大于等于8就是代码段,小于8就是数据段。
如果是代码段的话,那么有三个值非常重要,就是E、W、A,A表示是否访问过,比如操作系统在启动的时候,段描述符是没有被加载过的,那么A位一定是0,当它被使用、也就是被访问过时,这个位会被置1,这就是是否访问的意思了;这个W表示是否可写,如果为0代表这个段虽然是数据段但是不可以写,为1的时候代表可写。这里海哥指出——这四位11、E、W、A在代码段(数据段)或系统段中是有不同含义的,所以要了解Type语句,就要先用S来对其进行拆分,拆分完了以后,再通过第十一位进一步拆分来看到底是代码段还是数据段。刚才讲的A和W是针对数据段的时候的描述。来看一下第十位E,E是拓展位,当第十位为0的时候表示向上拓展,如果位1就表示向下拓展。

这里来举例一张图来说明什么叫向上拓展和向下拓展,选择FS段寄存器,FS有Base、有Limit,注意这里的Base不是绿色的方块,而是那一条黑色的线,也就是Limit的顶端那条黑线。Base指的是这个段描述的数据从哪里开始,Limit指的是描述的段有多长。向上拓展指的是你描述的地址的有效范围是Base开始到Base+Limit结束,也就是图中左侧的红色部分是向上拓展的时候,是有效的;如果当前的段是向下拓展的,那么有效地址指的是除去Base+Limit以外的部分。


接下来依旧是看Type域,当S位为1的时候,说明是数据段或代码段,但如果此时是一个代码段的话,也就是第六位十六进制数大于8的时候,那当前的段描述符其含义就是一个代码段的描述符。同样是这个Type域的性质发生了变化,A位和数据段的性质是一样的,都是表示当前这个段是否被访问过;R位是可读位,表示是否可读;C位是一致位,表示当前代码段是一致代码段还是非一致代码段,一致代码段和非一致代码段的区别后续会介绍,这节课不用管,只需要知道谁是数据段谁是代码段就可以,在代码段中哪一个是一致代码段、哪一个是非一致代码段即可。
系统段描述符里面分为很多个小类,如下表所示。

举例来说,S=0的时候我们知道是系统描述符,那么如果Type域的值是1011的话,那么这是一个32位的TSS,并且是处于繁忙的状态,什么是TSS、什么是中断门、什么是陷阱门,现在先不用管,只需要知道拆分就可以了。
