要点回顾
- 前文提到,当写入一个段寄存器的时候,只给了一个16位的数值,但是段寄存器有96位。
- 那么剩下的80位从哪里来的?这个16位的数值是随便写的吗?
Windbg指令
指令 | 功能 |
---|---|
r | 查看寄存器 |
dd | 以四字节为一组读取并显示数据 |
dq | 以八字节为一组读取并显示数据 |
GDT与LDT
- GDT:全局描述符表
- LDT:局部描述符表
- 当执行类似 MOV DS, AX 指令的时候,CPU会进行一个查表的操作。
- 根据AX的值来决定查找GDT表还是LDT表,查找表的什么位置,查出多少数据。
- 也就得出了:AX的值是不能随便写的。
- 同时,LDT这张表一般没有使用,所以都是查找GDT这张表。
表有多大呢?
kd> r gdtr
gdtr=8003f00
kd> r gdtl
gdtl=000003ff
记住一个寄存器:GDTR。
该寄存器是48位的,不是96位。同时该表中存储了两个值。分别是:
- GDT表的位置。
- GDT表的大小。
段描述符
- 段描述符的结构
- 段寄存器有96位,但是在执行MOV指令时,只给了16位。
- 那么剩下的80位去哪里了呢?
- 当执行MOV指令时,CPU会从GDT表中查询数据,将查询出来的数据放进去。
- 从中查询出多少数据呢?8个字节。
- 在GDT表中存储的每一个元素,称为段描述符,每个段描述符有8个字节。
总结:GDT是一张表,该表中存储的每一个元素称为段描述符。
GDT表
一般查询GDT表使用dq指令进行查询比较方便,因为每个段描述符有8个字节。
使用dd指令查询的GDT表:
使用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:局部描述符表
- 当执行类似 MOV DS, AX 指令的时候,CPU会进行一个查表的操作。
- 根据AX的值来决定查找GDT表还是LDT表,查找表的什么位置,查出多少数据。
- 如果一旦查到了,它会将查出来的段描述符(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开始数)。
如下图中红色标记部分:
由此可得:
- 当使用 MOV DS, 1B。
- 对应的段描述符就是上图中被红色方框所框选的位置。
加载段描述符至段寄存器
- 除了MOV指令,还可以使用LES、LSS、LDS、LFS和LGS指令修改寄存器。
- 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:在段描述符的结构中
练习
- (必须)记住段描述符与段选择子的结构。
- 使用LES、LDS等指令修改段寄存器。
思考
- 段描述符共有64位,但需要填充的是80位,该怎么填写?