段描述符与段选择子
段寄存器有96位,其中80位不可见,包括Base,limit,attribute。16位可见,即段选择子selector。
对段寄存器进行写入时,CPU根据段选择子到GDT表中寻找段描述符,段描述符共8字节,64位,CPU根据段描述符填充段寄存器。
那么问题就是,如何用64位的段描述符来填充80位的段寄存器呢?为了解决该问题,这就需要段描述符的P,G位。
一、段描述符结构如图:
BASE: 段基址,由上图中的两部分(BASE 31-24 和 BASE 23-0)组成
G:LIMIT的单位,该位 0 表示单位是字节,1表示单位是 4KB
由于Intel考虑向前兼容,我们会发现段描述符的结构比较混乱,东一块西一块的。Attribute在段描述符高4字节的8-23位,Base在段描述符的低4字节的16-31位,高4字节的0-7位和24-31位。上述两个属性没有数据丢失,段寄存器和段描述符内的位数是一样多的。而Limit在段寄存器内占32位,在段描述符内只有20位。
当G=0时,Limit的范围是0x00000000-0x000FFFFF,其中,段描述符的20位在低位,高位补0.
当G=1时,Limit的范围是0x00000FFF-0xFFFFFFFF,其中,段描述符的20位在高位,低位补F。
D/B: 该位为 0 表示这是一个 16 位的段,1 表示这是一个 32 位段
AVL: 该位是用户位,可以被用户自由使用
LIMIT: 段的界限,单位由 G 位决定。数值上(经过单位换算后的值)等于段的长度(字节)- 1。
P: 当P=1时,段描述符有效;当P=0时,段描述符无效。
DPL:段权限
S: 该位为 1 表示这是一个数据段或者代码段。为 0 表示这是一个系统段(比如调用门,中断门等)
TYPE: 根据 S 位的结果,再次对段类型进行细分。
二、段选择子
段选择子是一个16位的段描述符,段选择子格式,如图:
INDEX:在GDT数组或LDT数组的索引号(3~15位)
TI:Table Indicator,这个值为0表示查找GDT,1则查找LDT
RPL:请求特权级。以什么样的权限去访问段。
三.练习:
给出下列3个段选择子,从GTD表中查找对应的段描述符,然后填充到段寄存器中。
将段描述符填写到段寄存器结构体中(段选择子:23, 2B ,30 )
0x23 拆分为二进制为 : 0010 0011
RPL :11 (低2位)
TI :0 (第3位)
INDEX : 100 (3~15位)
表明查找GDT表,Index 是4。对应GDT表项是00cff300`0000ffff
WORD Selector: 23
WORD Atrribute: cff3
DWORD Base: 00000000
DWORD Limit: ffffffff
0x2B 拆分为二进制为 :00101 0 11
RPL :11 (低2位)
TI :0 (第3位)
INDEX : 101 (3~15位)
表明查找GDT表,Index 是5。对应GDT表项是80008b04`200020ab
WORD Selector: 2B
WORD Atrribute: 008b
DWORD Base: 80042000
DWORD Limit:000020ab
0x30 拆分为二进制为 :00110 0 00
RPL :00 (低2位)
TI :0 (第3位)
INDEX : 110 (3~15位)
表明查找GDT表,Index 是6。对应GDT表项是ffc093df`f0000001
WORD Selector: 30
WORD Atrribute: c093
DWORD Base: ffdff000
DWORD Limit: 00001fff
四、加载段描述符至段寄存器
mov 指令修改段寄存器,例如:
mov ax, 0x20
mov ds, ax
除了MOV指令,我们还可以使用LES、LSS、LDS、LFS、LGS指令修改寄存器.
CS不能通过上述的指令进行修改,CS为代码段,CS的改变会导致EIP的改变,要改CS,必须要保证CS与EIP一起改。
int main()
{
char buffer[6] = {0};
__asm {
// 高 2 字节加载到 ES 寄存器,低 4 字节复制到 ecx 寄存器。fword 表示 6 字节。
// LDS/LSS/LFS/LGS 用法是类似的。没有LCS指令,要修改CS,需要使用其它指令,这里就不给出。
// 这行指令是有坑的,不一定可以执行成功,取决于buffer中的值。
les ecx, fword ptr ds:[buffer]
}
return 0;
}
注意:RPL<=DPL(在数值上)
五、分析 limit
limit 的含义是这个段的大小。 LIMIT 表示段描述符中的 20bit LIMIT。
如果粒度 G=0,LIMIT= 0x3ff,这意味着该段的大小是 0x3ff+1=0x400 字节。如果 G=1,那意味着该段的大小是(0x3ff+1)*4KB=0x400000字节,所以换算后的 limit = 0x400000-1=0x003fffff.
再举个例子。LIMIT=0xfffff, G=1,则该段的大小是 (0xfffff+1)*4KB=0x100000*0x1000=0x100000000字节,所以换算后的 limit=0x100000000-1=0xffffffff
limit 只有20个位,如何赋给32位呢 ?
当属性里的G位为0时,在limit 高位添加12个0,扩展为32位。比如 0x003ff,然后在前面补 0 至32bit,即 limit = 0x000003ff;
当G位为1时,在limit 低位添加12 个1(二进制),扩展成32 位。比如 0x003ff,然后在后面补 f 至 32bit, 即 LIMIT = 0x003fffff