目录
1. GDT表
现在思考一个问题,当你写一个段寄存器的时候,只给了一个16位的数,但段寄存器有96位,那剩下的80位是怎么来的?这个16位的数是随便写的吗?
事实上是,通常在执行MOV DS,AX这些指令时,CPU会查表,并根据AX的值来决定查找GDT(全局描述符表)还是LDT(局部描述符表),查找表的什么位置,查出多少数据。
有同学可能会好奇GDT表在哪里,表的大小有多大呢?这就要说到gdtr寄存器了,gdtr是一个48位的寄存器,其中32位存储了GDT表的开始位置,剩下的16位存储了GDT表的大小,打开winDbg以命令方式查看,如下图所示:
r gdtr命令是用于查看GDT表的开始位置,r gdtl是用于查看GDT表大小,dd 8003f000可以查看GDT表里面的数据。
2. 段描述符
BASE: 段基址,由上图中的两部分(BASE 31-24 和 BASE 23-16)组成
G:LIMIT的单位,该位 0表示单位是字节,1表示单位是 4KB
D/B: 该位为 0 表示这是一个 16 位的段,1 表示这是一个 32 位段
AVL: 该位是用户位,可以被用户自由使用
LIMIT: 段的界限,单位由 G 位决定。数值上(经过单位换算后的值)等于段的长度(字节)- 1
P: 段存在位,该位为 0 表示该段不存在,为 1 表示存在
DPL:段权限
S: 该位为 1 表示这是一个数据段或者代码段。为 0 表示这是一个系统段(比如调用门,中断门等)
TYPE: 根据 S 位的结果,再次对段类型进行细分
简单来说,段描述符就是GDT表中存储的一个元素,GDT表存储了很多这样的元素,段描述符的大小为8字节。
还是回到原先的问题,当执行MOV DS,AX这些指令时就会去查GDT表中的段描述符,AX寄存器指定了查找GDT表的哪一个段描述符,然后把查找到的数据存放到DS段寄存器中,为此我们还需要了解段选择子。
3. 段选择子
段选择子是一个16位的整数(段寄存器的16位可见部分),段选择子的形式如下图所示:
段选择子(16位的整数)记录了GDT表的段描述符的具体位置,总的来说,段选择子总共分为以下三部分:
0-1位是RPL位,2位是TI位,3-15位是Index位
RPL:请求特权级,以什么样的权限去访问段
TI:Table Indicator,当TI=0表示查找GDT表,TI=1则查找LDT表
INDEX:在GDT表或LDT表中的索引号,查找哪个段描述符
4. 段描述符属性
除了MOV指令,我们还可以使用LES、LSS、LDS、LFS、LGS指令修改寄存器。需要注意的是:CS不能通过上述的指令进行修改,因为CS是代码段,CS的改变会导致EIP的改变,要改CS,必须要保证CS与EIP一起改。
int main(){
char buffer[6];
__asm
{
//这条指令的意思是将高2个字节给es寄存器,低四个字节给ecx寄存器
//注意,这条指令不一定能执行成功,这取决于buffer的值
les ecx,fword ptr ds:[buffer]
}
return 0;
}
段寄存器的值是通过查找GDT表中的段描述符进行填充的,但是段描述符是64位的,段寄存器是96位的,那么要如何将段描述符填充到段寄存器呢?还要用到段描述符的属性。
4.1 段描述符属性P位和G位
段描述符的属性如上所示,其中P位在15位,G位在23位,CPU在执行汇编指令时会首先检测P位,当P = 1时,说明段描述符有效,P=0则说明段描述符无效,如果P=0则后面的检测就不再继续。
4.2 段描述符属性G位
段寄存器的结构如下:
struct SegMent {
WORD selector; //16位
WORD attribute; //16位
DWORD base; //32位
DWORD limit; //32位
}
段描述符结构如下:
首先我们明白一个事实,段寄存器有96位,段描述符只有64位,段描述符填充到段寄存器还差32位,那么段描述符和段寄存器之间的对应关系是如何的呢?
段寄存器的selector很好理解,其实就是16位的段选择子。
attribute对应的就是段描述符高4字节的8-23位。
base对应的有高4字节的24-31位和0-7位,还有低4字节的16-31位,这三部分构成了一个32位的地址。
limit对应的有高4字节的16-19位,低4字节的0-15位,也就是说limit总共也就20位,转成16进制最大为0xFFFFF,剩下的12位是从哪里来呢?没错,就是段描述符的G位。如果G=0,在limit前面补000,最大可以表示0x000FFFFF;如果G=1,在limit后面补FFF,最大可以表示0xFFFFFFFF。
5. 段描述符填充到段寄存器
现在有0x1B和0x23两个段选择子,我们要将其对应的段描述符填充到段寄存器中,GDT表的数据如下所示:
分析段选择子0x1B:
0x1B = 0000 0000 0001 1011
RPL = 3
TI = 0 查找GDT表
INDEX = 3 , 查找GDT表的段描述符 = 00cffb00`0000ffff
Selector = 0x1B
Attribute = 0xcffb
Base = 0x00000000
Limit = 0xFFFFFFFF
分析段选择子0x23:
0x23 = 0000 0000 0010 0011
RPL = 3
TI = 0
INDEX = 4 , 查找GDT表的段描述符 = 00cff300`0000ffff
Selector = 0x23
Attribute = 0xcff3
Base = 0x00000000
Limit = 0xFFFFFFFF