Windows内核的表学习总结

来源:互联网 发布:法兰克加工中心编程 编辑:程序博客网 时间:2024/05/17 04:37

SSDT - 系统服务描述符表



SSDT(System Services Descriptor Table),系统服务描述符表。这个表就是一个把ring3的Win32 


API和ring0的内核函数联系起来。SSDT并不仅仅只包含一个庞大的地址索引表,它还包含着一些其它有


用的信息,诸如地址索引的基地址、服务函数个数等。
中文名系统服务描述符表 外文名 System Services Descriptor Table 别    称 SSDT 包    含 服务


函数个数
目录
1 简介
2 SSDT的结构
简介
通过修改此表的函数地址可以对常用windows函数进行hook,从而实现对一些关心的系统动作进行过滤、


监控的目的。一些HIPS、防毒软件、系统监控、注册表监控软件往往会采用此接口来实现自己的监控模


块,
SSDT到底是什么呢?打一个比方,SSDT相当于系统内部API的指向标,作用就是告诉系统,需要调用的


API在什么地方。
SSDT的结构

引用:
typedef struct _SYSTEM_SERVICE_TABLE
{
PVOID ServiceTableBase; //这个指向系统服务函数地址表
PULONG ServiceCounterTableBase;
ULONG NumberOfService; //服务函数的个数
ULONG ParamTableBase;
}SYSTEM_SERVICE_TABLE,*PSYSTEM_SERVICE_TABLE;
typedef struct _SERVICE_DESCRIPTOR_TABLE
{
SYSTEM_SERVICE_TABLE ntoskrnel; //ntoskrnl.exe的服务函数
SYSTEM_SERVICE_TABLE win32k; //win32k.sys的服务函数,(gdi.dll/user.dll的内核支持)
SYSTEM_SERVICE_TABLE NotUsed1;
SYSTEM_SERVICE_TABLE NotUsed2;
}SYSTEM_DESCRIPTOR_TABLE,*PSYSTEM_DESCRIPTOR_TABLE;
实际上内核中存在两个系统服务描述符表,一个是KeServiceDescriptorTable(由ntoskrnl.exe导出)


,一个是KeServieDescriptorTableShadow(没有导出)。
从上述结构中,我们可以看出,KeServieDescriptorTableShadow不但包含了ntoskrnel项,而且还包含


了win32k项,而KeServiceDescriptorTable仅仅包含一个ntoskrnel项。
我们用WinDbg看看,到底这个KeServiceDescriptorTable是什么东西。
引用:
lkd> dd KeServiceDescriptorTable
80553380 81f61008 00000000 0000013d 81e62358
80553390 00000000 00000000 00000000 00000000
805533a0 00000000 00000000 00000000 00000000
805533b0 00000000 00000000 00000000 00000000
805533c0 00002710 bf80c217 00000000 00000000
805533d0 f8951a80 f80839e0 81f433a8 806e0f40
805533e0 00000000 00000000 00000000 00000000
805533f0 83740440 01c8eac3 00000000 00000000
我们可以看出,函数表基地址为81f61008,存在0000013d个服务项
我们继续看看81f61008地址的内存块内,到底放了些什么。
引用:
lkd> dd 81f61008
81f61008 80599746 805e6914 805ea15a 805e6946
81f61018 805ea194 805e697c 805ea1d8 805ea21c
81f61028 8060b880 8060c5d2 805e1cac 805e1904
81f61038 805ca928 805ca8d8 8060bea6 805ab334
81f61048 8060b4be 8059dbbc 805a5786 f8807de8
81f61058 804ffed0 8060c5c4 8056be64 805353f2
81f61068 80604b90 805b19c0 805ea694 80619a56
81f61078 805eeb86 80599e34 80619caa 805996e6
这里面的数据到底表示什么呢?实际上,这表示的是一个入口地址。我们对其中一个地址进行反汇编看



引用:
lkd> u 80599746
nt!NtConnectPort+0x60:
80599746 689c000000 push 9Ch
8059974b 6820a14d80 push offset nt!FsRtlLegalAnsiCharacterArray+0x1680 (804da120)
80599750 e8abebf9ff call nt!wctomb+0x45 (80538300)
80599755 64a124010000 mov eax,dword ptr fs:[00000124h]
8059975b 8a8040010000 mov al,byte ptr [eax+140h]
80599761 884590 mov byte ptr [ebp-70h],al
80599764 84c0 test al,al
80599766 0f84b9010000 je nt!NtConnectPort+0x23f (80599925)
========

中断描述符表

中断描述符表(Interrupt Descriptor Table,IDT)将每个异常或中断向量分别与它们的处理过程联
系起来。与GDT和LDT表类似,IDT也是由8字节长描述符组成的一个数组。与GDT不同的是,表中第1项
可以包含描述符。为了构成IDT表中的一个索引值,处理器把异常或中断的向量号乘以8。因为最多只
有256个中断或异常向量,所以IDT无需包含多于256个描述符。IDT中可以含有少于256个描述符,因为
只有可能发生的异常或中断才需要描述符。不过IDT中所有空描述符项应该设置其存在位(标志)为0

IDT表可以驻留在线性地址空间的任何地方,处理器使用IDTR寄存器来定位IDT表的位置。这个寄存器
中含有IDT表32位的基地址和16位的长度(限长)值,如图4-26所示。IDT表基地址应该对齐在8字节边
界上以提高处理器的访问效率。限长值是以字节为单位的IDT表的长度。
中断描述符表IDT和寄存器IDTR
LIDT和SIDT指令分别用于加载和保存IDTR寄存器的内容。LIDT指令用于把内存中的限长值和基地址操
作数加载到IDTR寄存器中。该指令仅能由当前特权级CPL是0的代码执行,通常被用于创建IDT时的操作
系统初始化代码中。SIDT指令用于把IDTR中的基地址和限长内容复制到内存中。该指令可在任何特权
级上执行。
如果中断或异常向量引用的描述符超过了IDT的界限,处理器会产生一个一般保护性异常
在实地址模式中,CPU把内存中从0开始的1K字节作为一个中断向量表。表中的每个表项占四个字节,由


两个字节的段地址和两个字节的偏移量组成,这样构成的地址便是相应中断处理程序的入口地址。但是


,在保护模式下,由四字节的表项构成的中断向量表显然满足不了要求。这是因为,?除了两个字节的段


描述符,偏移量必用四字节来表示;?要有反映模式切换的信息。因此,在保护模式下,中断向量表中的


表项由8个字节组成,中断向量表也改叫做中断描述符表IDT(InterruptDescriptor Table)。其中的每


个表项叫做一个门描述符(gate descriptor),“门”的含义是当中断发生时必须先通过这些门,然后


才能进入相应的处理程序。


门描述符
· 中断门(Interrupt gate)
其类型码为110,中断门包含了一个中断或异常处理程序所在段的选择符和段内偏移量。当控制权通过中


断门进入中断处理程序时,处理器清IF标志,即关中断,以避免嵌套中断的发生。中断门中的DPL


(Descriptor Privilege Level)为0,因此,用户态的进程不能访问Intel的中断门。所有的中断处理


程序都由中断门激活,并全部限制在内核态。
· 陷阱门(Trap gate)
其类型码为111,与中断门类似,其唯一的区别是,控制权通过陷阱门进入处理程序时维持IF标志位不变


,也就是说,不关中断。
· 系统门(System gate)
这是Linux内核特别设置的,用来让用户态的进程访问Intel的陷阱门,因此,门描述符的DPL为3。通过


系统门来激活4个Linux异常处理程序,它们的向量是3、4、5及128,也就是说,在用户态下,可以使用


int3、into、bound 及int0x80四条汇编指令。
最后,在保护模式下,中断描述符表在内存的位置不再限于从地址0开始的地方,而是可以放在内存的任


何地方。为此,CPU中增设了一个中断描述符表寄存器IDTR,用来存放中断描述符表在内存的起始地址。


中断描述符表寄存器IDTR是一个48位的寄存器,其低16位保存中断描述符表的大小,高32位保存IDT的基


址.


========

GDT 与 LDT

http://www.cnblogs.com/hicjiajia/archive/2012/05/25/2518684.html
很长时间没碰组成原理和操作系统,忘得差不多了,今天学内核需要一些基础知识,搜了些文章补习一


下,这篇讲描述符表的文章比较不错:


原文地址:http://blog.csdn.net/billpig/article/details/5833980
另外,在oldlinux上有一篇帖子也是讨论这个的,看完上面的文章再看这篇帖子就比较明了了:
原文地址:http://www.oldlinux.org/oldlinux/viewthread.php?tid=9420
保护模式下的段寄存器 由 16位的选择器 与 64位的段描述符寄存器 构成


段描述符寄存器 : 存储段描述符
选择器            : 存储段描述符的索引


PS:原先实模式下的各个段寄存器作为保护模式下的段选择器,80486中有6个(即CS,SS,DS,ES,FS,GS)80


位的段寄存器,同时提供6个段左右机器当前运行的地址空间。由选择器CS对应表示的段仍为代码段,选


择器SS对应表示的段仍为堆栈段


 (1)全局描述符表GDT(Global Descriptor Table)在整个系统中,全局描述符表GDT只有一张(一个


处理器对应一个GDT),GDT可以被放在内存的任何位置,但CPU必须知道GDT的入口,也就是基地址放在哪


里,Intel的设计者门提供了一个寄存器GDTR用来存放GDT的入口地址,程序员将GDT设定在内存中某个位


置之后,可以通过LGDT指令将GDT的入口地址装入此积存器,从此以后,CPU就根据此寄存器中的内容作


为GDT的入口来访问GDT了。GDTR中存放的是GDT在内存中的基地址和其表长界限。


GDTR结构


(2)段选择子(Selector)由GDTR访问全局描述符表是通过“段选择子”(实模式下的段寄存器)来完


成的,如图三①步。段选择子是一个16位的寄存器(同实模式下的段寄存器相同)如图四


selector
段选择子包括三部分:描述符索引(index)、TI、请求特权级(RPL)。他的index(描述符索引)部分


表示所需要的段的描述符在描述符表的位置,由这个位置再根据在GDTR中存储的描述符表基址就可以找


到相应的描述符(如图三①步)。然后用描述符表中的段基址加上逻辑地址(SEL:OFFSET)的OFFSET就


可以转换成线性地址(如图三②步),段选择子中的TI值只有一位0或1,0代表选择子是在GDT选择,1代


表选择子是在LDT选择。请求特权级(RPL)则代表选择子的特权级,共有4个特权级(0级、1级、2级、3


级)。


关于特权级的说明:任务中的每一个段都有一个特定的级别。每当一个程序试图访问某一个段时,就将


该程序所拥有的特权级与要访问的特权级进行比较,以决定能否访问该段。系统约定,CPU只能访问同一


特权级或级别较低特权级的段。


例如给出逻辑地址:21h:12345678h转换为线性地址
a. 选择子SEL=21h=0000000000100 0 01b 他代表的意思是:选择子的index=4即100b选择GDT中的第4个


描述符;TI=0代表选择子是在GDT选择;左后的01b代表特权级RPL=1
b. OFFSET=12345678h若此时GDT第四个描述符中描述的段基址(Base)为11111111h,则线性地址


=11111111h+12345678h=23456789h


(3)局部描述符表LDT(Local Descriptor Table)局部描述符表可以有若干张,每个任务可以有一张


。我们可以这样理解GDT和LDT:GDT为一级描述符表,LDT为二级描述符表。如图


gdt寻址


LDT和GDT从本质上说是相同的,只是LDT嵌套在GDT之中。LDTR记录局部描述符表的起始位置,与GDTR不


同LDTR的内容是一个段选择子。由于LDT本身同样是一段内存,也是一个段,所以它也有个描述符描述它


,这个描述符就存储在GDT中,对应这个表述符也会有一个选择子,LDTR装载的就是这样一个选择子。


LDTR可以在程序中随时改变,通过使用lldt指令。如上图,如果装载的是Selector 2则LDTR指向的是表


LDT2。举个例子:如果我们想在表LDT2中选择第三个描述符所描述的段的地址12345678h。
1. 首先需要装载LDTR使它指向LDT2 使用指令lldt将Select2装载到LDTR
2. 通过逻辑地址(SEL:OFFSET)访问时SEL的index=3代表选择第三个描述符;TI=1代表选择子是在LDT


选择,此时LDTR指向的是LDT2,所以是在LDT2中选择,此时的SEL值为1Ch(二进制为11 1 00b)。


OFFSET=12345678h。逻辑地址为1C:12345678h
3. 由SEL选择出描述符,由描述符中的基址(Base)加上OFFSET可得到线性地址,例如基址是11111111h


,则线性地址=11111111h+12345678h=23456789h
4. 此时若再想访问LDT1中的第三个描述符,只要使用lldt指令将选择子Selector 1装入再执行2、3两步


就可以了(因为此时LDTR又指向了LDT1)
由于每个进程都有自己的一套程序段、数据段、堆栈段,有了局部描述符表则可以将每个进程的程序段


、数据段、堆栈段封装在一起,只要改变LDTR就可以实现对不同进程的段进行访问。


段描述符:


P,present位,1表示所描述的段存在(有效),为0表示所描述的段无效,使用该描述符会引起异常 
DPL,Descriptor privilege,描述符特权级别,说明所描述段的特权级别 
DT,描述符类型位,1说明当前描述符为存储段描述符,0为系统描述符或门描述符. 


TYPE: 
位0:A(accessed)位,表明描述符是否已被访问;把选择子装入段寄存器时,该位被标记为1 
位3:E(EXECUTABLE?)位,0说明所描述段为数据段;1为可执行段(代码段) 


当为数据段时, 
   位1为W位,说明该数据段是否可写(0只读,1可写) 
   位2为ED位,说明该段的扩展方向(0向高位扩展,1向低位扩展) 
当为可执行段是, 
   位1为R位,说明该执行段是否可读(0只执行,1可读) 
   位2为C位,0说明该段不是一致码段(普通代码段),1为一致码段 


G为粒度位,0说明LIMIT粒度为字节,1为4K字节. 


D位: 
   1.在可执行段中,D为1,表示使用32位地址,32/8位操作数;为0表示使用16位地址,16/8位操作数 
   2.在由SS寻址的段描述符(堆栈段?)中,D为1表示隐含操作(如PUSH/POP)使用ESP为堆栈指针,/
     为0使用SP(隐含操作:未明确定义段属性类型USE16/USE32?66H,67H?) 
   3.在向低扩展的存储段中,D为1,表示段的上限为4G;为0上限为64K
 
存储段描述符的结构表示:


分段管理可以把虚拟地址转换成线性地址,而分页管理可以进一步将线性地址转换成物理地址。当CR0中


的PG位置1时,启动分页管理功能,为0时,这禁止启动分页管理功能,并且把线性地址作物理地址使用





虚拟地址转为线性地址:


线性地址= 段基指 + 偏移地址


32位线性地址转为物理地址:
32位分为:
页目录索引:占最高10位,指示页目录表中第几个页表描述符
页表索引:占12位到21位,也是10位。指示这页表中第几个页描述符
页描述符:线性地址的低12位为页内偏移量。
========

GDT、GDTR、LDT、LDTR的学习

http://blog.csdn.net/yeruby/article/details/39718119#t3  
目录(?)[+]
GDT的由来:


在Protected Mode下,一个重要的必不可少的数据结构就是GDT(Global Descriptor Table)。
为什么要有GDT?我们首先考虑一下在Real Mode下的编程模型:
在Real Mode下,我们对一个内存地址的访问是通过Segment:Offset的方式来进行的,其中Segment是一


个段的Base Address,一个Segment的最大长度是64 KB,这是16-bit系统所能表示的最大长度。而


Offset则是相对于此Segment Base Address的偏移量。Base Address+Offset就是一个内存绝对地址。由


此,我们可以看出,一个段具备两个因素:Base Address和Limit(段的最大长度),而对一个内存地址


的访问,则是需要指出:使用哪个段?以及相对于这个段Base Address的Offset,这个Offset应该小于


此段的Limit。当然对于16-bit系统,Limit不要指定,默认为最大长度64KB,而 16-bit的Offset也永远


不可能大于此Limit。我们在实际编程的时候,使用16-bit段寄存器CS(Code Segment),DS(Data 


Segment),SS(Stack Segment)来指定Segment,CPU将段积存器中的数值向左偏移4-bit,放到20-bit


的地址线上就成为20-bit的Base Address。
到了Protected Mode,内存的管理模式分为两种,段模式和页模式,其中页模式也是基于段模式的。也


就是说,Protected Mode的内存管理模式事实上是:纯段模式和段页式。进一步说,段模式是必不可少


的,而页模式则是可选的——如果使用页模式,则是段页式;否则这是纯段模式。
既然是这样,我们就先不去考虑页模式。对于段模式来讲,访问一个内存地址仍然使用Segment:Offset


的方式,这是很自然的。由于 Protected Mode运行在32-bit系统上,那么Segment的两个因素:Base 


Address和Limit也都是32位的。IA-32允许将一个段的Base Address设为32-bit所能表示的任何值


(Limit则可以被设为32-bit所能表示的,以2^12为倍数的任何指),而不像Real Mode下,一个段的


Base Address只能是16的倍数(因为其低4-bit是通过左移运算得来的,只能为0,从而达到使用16-bit


段寄存器表示20-bit Base Address的目的),而一个段的Limit只能为固定值64 KB。另外,Protected 


Mode,顾名思义,又为段模式提供了保护机制,也就说一个段的描述符需要规定对自身的访问权限


(Access)。所以,在Protected Mode下,对一个段的描述则包括3方面因素:【Base Address, Limit, 


Access】,它们加在一起被放在一个64-bit长的数据结构中,被称为段描述符。这种情况下,如果我们


直接通过一个64-bit段描述符来引用一个段的时候,就必须使用一个64-bit长的段寄存器装入这个段描


述符。但Intel为了保持向后兼容,将段寄存器仍然规定为16-bit(尽管每个段寄存器事实上有一个64-


bit长的不可见部分,但对于程序员来说,段寄存器就是16-bit的),那么很明显,我们无法通过16-bit


长度的段寄存器来直接引用64-bit的段描述符。
怎么办?解决的方法就是把这些长度为64-bit的段描述符放入一个数组中,而将段寄存器中的值作为下


标索引来间接引用(事实上,是将段寄存器中的高13 -bit的内容作为索引)。这个全局的数组就是GDT


。事实上,在GDT中存放的不仅仅是段描述符,还有其它描述符,它们都是64-bit长。GDT是Protected 


Mode所必须的数据结构,也是唯一的。另外,正像它的名字(Global Descriptor Table)所揭示的,它


是全局可见的,对任何一个任务而言都是这样。


GDT的构成:


GDT的结构图如下:(GDT表相当于一个64bit的数组)
1、G:
(1)、G=0时,段限长的20位为实际段限长,最大限长为2^20=1MB
(2)、G=1时,则实际段限长为20位段限长乘以2^12=4KB,最大限长达到4GB


2、D/B:
当描述符指向的是可执行代码段时,这一位叫做D位,D=1使用32位地址和32/8位操作数,D=0使用16位地


址和16/8位操作数。如果指向的是向下扩展的数据段,这一位叫做B位,B=1时段的上界为4GB,B=0时段


的上界为64KB。如果指向的是堆栈段,这一位叫做B位,B=1使用32位操作数,堆栈指针用ESP,B=0时使


用16位操作数,堆栈指针用SP。


3、DPL:特权级,0为最高特权级,3为最低,表示访问该段时CPU所需处于的最低特权级


4、type : 类型
(1)、type<8时:数据段


(2)、type>=8时:代码段


GDTR是什么?
GDT可以被放在内存的任何位置,那么当程序员通过段寄存器来引用一个段描述符时,CPU必须知道GDT的


入口,也就是基地址放在哪里,所以Intel的设计者门提供了一个寄存器GDTR用来存放GDT的入口地址,


程序员将GDT设定在内存中某个位置之后,可以通过LGDT指令将GDT的入口地址装入此寄存器,从此以后


,CPU就根据此寄存器中的内容作为GDT的入口来访问GDT了。


LDT是什么?
除了GDT之外,IA-32还允许程序员构建与GDT类似的数据结构,它们被称作LDT(Local Descriptor 


Table,局部描述符表),但与GDT不同的是,LDT在系统中可以存在多个,并且从LDT的名字可以得知,


LDT不是全局可见的,它们只对引用它们的任务可见,每个任务最多可以拥有一个LDT。另外,每一个LDT


自身作为一个段存在,它们的段描述符被放在GDT中。
 LDT只是一个可选的数据结构,你完全可以不用它。使用它或许可以带来一些方便性,但同时也带来复


杂性,如果你想让你的OS内核保持简洁性,以及可移植性,则最好不要使用它。


LDTR是什么?


IA-32为LDT的入口地址也提供了一个寄存器LDTR,因为在任何时刻只能有一个任务在运行,所以LDT寄存


器全局也只需要有一个。如果一个任务拥有自身的LDT,那么当它需要引用自身的LDT时,它需要通过


lldt指令将其LDT的段描述符装入此寄存器。lldt指令与lgdt指令不同的时,lgdt指令的操作数是一个


32-bit的内存地址,这个内存地址处存放的是一个32-bit GDT的入口地址,以及16-bit的GDT Limit。而


lldt指令的操作数是一个16-bit的选择子,这个选择子主要内容是:被装入的LDT的段描述符在GDT中的


索引值。


至此,我们可以这样理解GDT和LDT:GDT为一级描述符表,LDT为二级描述符表。如图:

例如:如果我们想在表LDT2中选择第三个描述符所描述的段的地址12345678h。


1. 首先需要装载LDTR使它指向LDT2:使用指令lldt将Select2装载到LDTR。
2. 通过逻辑地址(SEL:OFFSET)访问时SEL的index=3代表选择第三个描述符;TI=1代表选择子是在LDT


选择,此时LDTR指向的是LDT2,所以是在LDT2中选择,此时的SEL值为1C h(二进制为11 1 00b),


OFFSET=12345678h。逻辑地址为1C:12345678h。
3. 由SEL选择出描述符,由描述符中的基址(Base)加上OFFSET可得到线性地址,例如基址是11111111h


,则线性地址=11111111h+12345678h=23456789h。
4. 此时若再想访问LDT1中的第三个描述符,只要使用lldt指令将选择子Selector 1装入再执行2、3两步


就可以了(因为此时LDTR又指向了LDT1)。


由于每个进程都有自己的一套程序段、数据段、堆栈段,有了局部描述符表则可以将每个进程的程序段


、数据段、堆栈段封装在一起,只要改变LDTR就可以实现对不同进程的段进行访问。




段选择子是什么?
引用GDT和LDT中的段描述符所描述的段,是通过一个16-bit的数据结构来实现的,这个数据结构叫做


Segment Selector——段选择子。它的高13位作为被引用的段描述符在GDT/LDT中的下标索引,bit 2用


来指定被引用段描述符被放在GDT中还是到LDT中,bit 0和bit 1是RPL——请求特权等级,被用来做保护


目的。如图所示:


前面所讨论的装入段寄存器中作为GDT/LDT索引的就是Segment Selector,当需要引用一个内存地址时,


使用的仍然是Segment:Offset模式,具体操作是:在相应的段寄存器装入Segment Selector,按照这个


Segment Selector可以到GDT或LDT中找到相应的Segment Descriptor,这个Segment Descriptor中记录


了此段的Base Address,然后加上Offset,就得到了最后的内存地址。


段选择子包括三部分:描述符索引(index)、TI、请求特权级(RPL)。它的index(描述符索引)部分


表示所需要的段的描述符在描述符表的位置,由这个位置再根据在GDTR中存储的描述符表基址就可以找


到相应的描述符。然后用描述符表中的段基址加上逻辑地址(SEL:OFFSET)的OFFSET就可以转换成线性


地址,段选择子中的TI值只有一位0或1,0代表选择子是在GDT选择,1代表选择子是在LDT选择。请求特


权级(RPL)则代表选择子的特权级,共有4个特权级(0级、1级、2级、3级)。
关于特权级的说明:任务中的每一个段都有一个特定的级别。每当一个程序试图访问某一个段时,就将


该程序所拥有的特权级与要访问的特权级进行比较,以决定能否访问该段。系统约定,CPU只能访问同一


特权级或级别较低特权级的段。
例如给出逻辑地址:21h:12345678h 转换为线性地址的步骤如下:
(1)、选择子SEL=21h=0000000000100 0 01b 它代表的意思是:选择子的index=4即选择GDT中的第4个


描述符;TI=0代表选择子是在GDT选择;最后的01代表特权级RPL=1
(2)、OFFSET=12345678h若此时GDT第四个描述符中描述的段基址(Base)为11111111h,则线性地址


=11111111h+12345678h=23456789h


关系:
计算机由实模式进入到保护模式要加载gdt,保护模式下的段寄存器由16位的选择器与64位的段描述符寄


存器构成。
段描述符寄存器: 存储段描述符;
选择器:存储段描述符的索引;
原先实模式下的各个段寄存器作为保护模式下的段选择器,80486中有6个(即CS,SS,DS,ES,FS,GS)16位的


段寄存器


GDT可以被放在内存的任何位置,但CPU必须知道GDT的入口,也就是基地址放在哪里,Intel的设计者门


提供了一个寄存器GDTR用来存放GDT的入口地址,程序员将GDT设定在内存中某个位置之后,可以通过


LGDT指令将GDT的入口地址装入此积存器,从此以后,CPU就根据此寄存器中的内容作为GDT的入口来访问


GDT了。GDTR中存放的是GDT在内存中的基地址和其表长界限。
48bit的GDTR的结构:


由GDTR访问GDT是通过“段选择子”(实模式下的段寄存器)来完成的,即:
GDTR(48bit)+Segment Selector(由实模式下的段寄存器充当,16bit)= GDT中的某一项 (64bit)


Tips:
(1)、GDT中的所有描述符(除0项外,包括LDT描述符这种类型的描述符)都指定一个段,GDT中的LDT描


述符指定的是一个特殊的段, 这个段中只能存放LDT表,之所以说GDT中一个描述符是一个LDT段的描述符


,是因为这个描述符中有属性说明这是一个LDT段.
(2)、
GDTR是一个48位的全局描述符寄存器,高32位存放GDT的基址,低16位存放GDT限长。
LDTR是一个16位的局部描述符寄存器,高13位存放LDT在GDT中的索引值。
GDT类似于一个 “数组” ,“数组元素” 可以是段描述符,也可以是LDT描述符。


例如:
若给定一个逻辑地址是 a:b ,根据逻辑地址的a(段选择符)的T1位确定是选择GDT还是LDT。
a、若是T1位选择GDT,根据GDTR找到GDT的基址,根据a的 3~15位确定它的段描述符X在GDT中的位置


(GDTR即基址+a的3-15bit即相对位置):确定段描述符X,再根据段描述符提取出其中包含的段基址信


息,段基址+b(段内偏移),最终确定线性地址。
2)若是T1位选择LDT,根据GDTR找到GDT的基址,根据LDTR的高13位确定它的LDTX描述符在GDT中的位置


(GDTR基址+LDTR13bit即相对位置):确定LDTX描述符。LDTX描述符可以确定LDT的基址(LDTX描述符确


定LDT表在内存中的起始位置),再根据段选择符a确定的相对位置,可以确定LDT中的私有段描述符Y。


接下来同上面的:再根据段描述符提取出其中包含的段基址信息,段基址+b(段内偏移),最终确定线


性地址。


GDT中包含的段描述符X和LDT中包含的私有段描述符Y,所占空间相同。
GDT中包含的段描述符X和GDT中包含的LDT描述符,所占用空间相同。


结论是:LDT不包含在GDT中。GDT中只是包含了LDT描述符(一个指向LDT起始地址的指针)。


本文转载自:《GDT与LDT》、《GDT详解》和linux的GDT和LDT
========
0 0
原创粉丝点击