在保护模式下,为了对程序加以保护。引入了页保护、段保护等机制。随着段保护也产生了新的寄存器,比如CS、DS、SS、ES、FS、GS、TR、GDTR、LDTR等 。

在IA-32模式下,程序的模型有段管理模式和平坦模式,Windows 32位环境为平坦模式。 段选择子CS、DS、SS所指向的段描述符的基址为0,G位为1(对齐粒度为PAGE 4K),段界限为0xFFFFF,所以地址空间为4G。

如图1所示,这是x32dbg附加到notepad.exe进程,显示CS、DS、SS寄存器的值分别为0x1B、0x23、0x23,当前CPL==3(segment selector的0、1位),TI==0(segment selector的第2位)。

解析这三个段寄存器:

1
2
3
4
5
CS Index:0x3        TI:0x0        CPL: 0x3

DS Index:0x4 TI:0x0 CPL: 0x3

SS Index:0x4 TI:0x0 CPL: 0x3

如果TI置位,则表示指向LDT(Local Descriptor Table),TI复位,指向GDT(Global Descriptor Table)

接着显示GDT表数据,CS所指向的Segment Descriptor的地址为GDTR Base + 8 3,DS、SS所指向的的Segment Descriptor地址为GDTR Base + 8 4。

如图2所示,CS所指向Segment Descriptor的值为0x00CFFB00 ~0000FFFF,DS、SS所指向的值为0x00CFF300~0000FFFF。

按照图3格式解析这两个段描述符。0x00CFFB00~0000FFFF中:

Segment Limit 15:00为 0xFFFF

Base Address 15:00 为 0x0000

Base Address 23:16 为 0x00

Type 为 0xB (具体含义如图4所示)

S为 1(不为system段,为CODE/DATA段)

DPL为 3

P(为1表示段存在于内存中)

Segment Limit 19:16 为 0xF

AVL为0

L 为0(不为Long Mode)

D/B 为 1 (32 bit segment)

Granularity 为 1(对齐粒度为PAGE)

Base Address 31:24 00

综上:段基址:0x00000000

段界限:0xFFFFF

对齐粒度:PAGE

该段的大小:Limit * PAGE = 4G (为平坦模式)

该段为CODE段,属性为可读可写可执行

这是一个32位程序使用的段,是一个Ring3代码段

除了CODE Segment和DATA Segment之外还有TSS Descriptor存在于GDT表中,TR寄存器存放的为TSS Segment Selector,通过Segment Selector在GDT表中索引到段描述符,TSS 的段描述符和上述有些许区别,32位模式的TSS段描述符如图5所示:

比起上面的描述符简单了一些,其中Type成员的9号位为Busy位,在使用中的任务段会置Busy标志位,其他的为0。如图6所示,图6中为Windows 7 7600 32位系统中TSS段的状态,有四个TSS Descriptor,但只有一个TSS Descriptor 的Busy位置位。

TSS Descriptor中的Base Address指向TSS(Task State Segment),结构如图7所示。

上述结构体在Task Switch后会更新所有字段。

Windows在IA-32e模式下,是强制平坦(segment limit 为0),64位寄存器已经足够索引所有地址,所以在IA-32e模式下的Task Switch机制已经不再需要TSS,但是一定要有一个TSS。如图8所示,图中位IA-32e模式下,64位程序Segment Selector的值。

该64位进程使用的CODE Segment为long mode,但Stack Segment 和DATA Segment 却不是Long mode,不过从图9可知,只有Code Segment有Long mode,说明在Windows在兼容32位程序时,64位程序和32位使用相同的DATA Segment和Stack Segment,Code Segment是不同的。观察图9可知,除了TSS,Long mode的描述符段界限都为0,而非Long mode的段界限为4G。在IA-32e中,TSS Descriptor描述符的长度扩展为128Bit,其他描述符长度和IA-32模式长度一致,都为64 Bit。这就导致了图9中TSS段解析错误,所以我们需要手动解析TSS描述符。如图9所示,为IA-32e模式TSS Descriptor。

该64位进程使用的CODE Segment为long mode,但Stack Segment 和DATA Segment 却不是Long mode,不过从图9可知,只有Code Segment有Long mode,说明在Windows在兼容32位程序时,64位程序和32位使用相同的DATA Segment和Stack Segment,Code Segment是不同的。观察图9可知,除了TSS,Long mode的描述符段界限都为0,而非Long mode的段界限为4G。在IA-32e中,TSS Descriptor描述符的长度扩展为128Bit,其他描述符长度和IA-32模式长度一致,都为64 Bit。这就导致了图9中TSS段解析错误,所以我们需要手动解析TSS描述符。如图9所示,为IA-32e模式TSS Descriptor。

读取GDT中TSS的值如图11所示:

1
2
3
4
5
Base Address : 0xFFFFF800~00B9B080

Segment Limit : 0x067

Granularity : 0x0

读取TSS的值,如图12所示:

图13为TSS Format:

1
2
3
4
5
6
7
8
9
10
11
RSP0:0xFFFFF800~00BA1C70

RSP1:0x0

RSP2:0x0

IST1:0xFFFFF800~00BAA000

IST2:0xFFFFF800~00BAE000

IST3:0xFFFFF800~00BAC000

然后我们读取IDT,图14为IDT的一部分,我们观察到2号不可屏蔽中断Stack = 0xFFFFF80000BAC000,8号Double fault abort Stack = 0xFFFFF80000BAA000,12号McheckAbort中断 Stack = 0xFFFFF80000BAE000

可以看出这些中断都是一些IRQL比较高的异常,而这个IST是在IDT Gate Descriptor中指定。如图15所示:

这是Windows环境下Segment register的使用,接下来一篇会介绍RTOS环境Segment register 的使用。


本文参考: Intel® 64 and IA-32 Architectures Software Developer’s Manual

学习过程中还有很多不足,还望朋友们指正!