【2021.03.10】段描述符与段选择子

要点回顾

  • 前文提到,当写入一个段寄存器的时候,只给了一个16位的数值,但是段寄存器有96位
  • 那么剩下的80位从哪里来的?这个16位的数值是随便写的吗?

Windbg指令

指令 功能
r 查看寄存器
dd 以四字节为一组读取并显示数据
dq 以八字节为一组读取并显示数据

GDT与LDT

  1. GDT:全局描述符表
  2. LDT:局部描述符表
  • 当执行类似 MOV DS, AX 指令的时候,CPU会进行一个查表的操作。
  • 根据AX的值来决定查找GDT表还是LDT表,查找表的什么位置,查出多少数据。
  • 也就得出了:AX的值是不能随便写的。
  • 同时,LDT这张表一般没有使用,所以都是查找GDT这张表。

表有多大呢?

kd> r gdtr

gdtr=8003f00

kd> r gdtl

gdtl=000003ff

记住一个寄存器:GDTR

该寄存器是48位的,不是96位。同时该表中存储了两个值。分别是:

  1. GDT表的位置。
  2. GDT表的大小。

段描述符

  • 段描述符的结构

  1. 段寄存器有96位,但是在执行MOV指令时,只给了16位。
  2. 那么剩下的80位去哪里了呢?
  3. 当执行MOV指令时,CPU会从GDT表中查询数据,将查询出来的数据放进去。
  • 从中查询出多少数据呢?8个字节。
  • 在GDT表中存储的每一个元素,称为段描述符,每个段描述符有8个字节。

总结:GDT是一张表,该表中存储的每一个元素称为段描述符。

GDT表

一般查询GDT表使用dq指令进行查询比较方便,因为每个段描述符有8个字节。

使用dd指令查询的GDT表:

扫描二维码关注公众号,回复: 12895662 查看本文章

使用dq指令查询的GDT表:

可以看到使用dq指令查询的GDT表,中间有一个符号,更便于观察。

举例说明

如:使用上图(使用dq指令查询的GDT表)中的 00cf9300`0000ffff 来举例说明段描述符。

00cf9300`0000ffff 有8个字节,而段描述符也是8个字节。

  • 上面的32位(上图第一行),也就是高4个字节,它对应的是8个字节中前面的四个字节,也就是00cf9300。
  • 下面的32位(上图第二行),也就是低4个字节,它对应的是8个字节中后面的四个字节,也就是0000ffff。

通过dq指令来查看某个地址的内存的时候,它会将高位放在前面,低位放在后面。

格式如下:

内存地址 |  高位`低位 | 高位`低位

内存地址 |  高位`低位 | 高位`低位

内存地址 |  高位`低位 | 高位`低位

查询的GDT表不完整

  • GDT表的长度是0x3ff,而上图无论是通过 dd 指令还是 dq 指令查询的表,都显得很短。
  • 说明该表查询的不完整,那么想要查询的多一些该怎么办呢?
  • 可以在使用 dq 指令查询GDT表的时候,在后面跟上地址+大写字母"L"+要显示几组(16进制)。
  • 无论是大写字母L还是小写字母L都可以。

如:

kd> dq 8003f000 L40

  • GDT:全局描述符表
  • LDT:局部描述符表
  1. 当执行类似 MOV DS, AX 指令的时候,CPU会进行一个查表的操作。
  2. 根据AX的值来决定查找GDT表还是LDT表,查找表的什么位置,查出多少数据。
  3. 如果一旦查到了,它会将查出来的段描述符(8字节),将这8字节的值取出来放到段寄存器中。

段选择子

  • 段选择子是一个16位的段描述符,该描述符指向了定义该段的段描述符。
  • 段选择子就是一个16位的数值,就仅仅是一个数字,它有16位。
  • 这16位决定了该去GDT表中查询哪一个段描述符。

段选择子的结构

  • rpl:请求特权级别。
  • ti:ti=0 查询GDT表、ti=1 查询LDT表,由于LDT表在Windows中没有使用,所以都是查询GDT表。
  • index:处理器将索引值乘以8,再加上GDT或LDT的基地址,就是要加载的段描述符。到GDT表中查询哪一个段描述符,就是由第3到第15位拼出来的数字决定的。

对index进行举例说明

  • 如提供了一个段选择子,它的值是:001B。
  • 将其转换为二进制:0000 0000 0001 1011。

对二进制进行拆分并带入上图:

  • 转换为十进制:3、0、3。
  • 如此可得,index为3,那么需要查询的GDT表中的段描述符就是第3个(从0开始数)。

如下图中红色标记部分:

由此可得:

  1. 当使用 MOV DS, 1B。
  2. 对应的段描述符就是上图中被红色方框所框选的位置。

加载段描述符至段寄存器

  • 除了MOV指令,还可以使用LESLSSLDSLFSLGS指令修改寄存器。
  • CS不能通过上述的指令进行修改,CS为代码段,CS的改变会导致EIP(EIP寄存器:CPU下一次将要执行的地址)发生变化。
  • 要修改CS,必须要保证CS与EIP一同修改。
char buffer[6];

__asm
{
    les ecx, fword ptr ds:[buffer]    //高2字节(段选择子)给es,低4字节给ecx
}

dword:4字节、fword:6字节、qword:8字节

注意:RPL<=DPL(在数值上),否则会因为段权限检查的问题而无法继续实验。

  • RPL:在段选择子的结构中
  • DPL:在段描述符的结构中

练习

  1. (必须)记住段描述符与段选择子的结构。
  2. 使用LES、LDS等指令修改段寄存器。

思考

  • 段描述符共有64位,但需要填充的是80位,该怎么填写?

猜你喜欢

转载自blog.csdn.net/qq_18120361/article/details/114638804