以前的Intel 8086 是16位的CPU,它有着16位的寄存器,16位的数据总线,20位的地址总线以及1MB的寻址能力。一个地址是由段和偏移两部分组成的,物理地址遵循如下计算公式:
物理地址(Physical Address)= 段值(Segment)*16 + 偏移(Offset)
其中,段值和偏移都是16位的。
从80386开始,Intel家族的CPU进入32位时代。80386有32位地址线,所以寻址空间达到4GB。
当然,现在的计算机在实模式下,也是使用16位的寄存器,用“段:偏移”的方法寻址1MB的地址空间。
而在保护模式下,我们有了32位的寄存器,一个寄存器就可以寻址4GB的空间,是不是从此段值就被抛弃了呢?实际上并没有,新政策下的地址仍然使用“SEG:OFFSET”这样的形式来表示,只不过保护模式下“段”的概念发生了根本性的变化。段值仍然由原来16位的cs,ds等寄存器表示,但此时它仅仅变成了一个索引,这个索引指向一个数据结构的一个表项,表项中详细定义了段的起始地址,界限(段的最大长度),属性等内容。这个数据结构就是传说中的GDT(实际上还可能是LDT)。GDT中的表项也有一个专门的名字,叫做描述符。
也就是说,GDT的作用是用来提供段式存储机制,这种机制是通过段寄存器和GDT中的描述符共同提供的。
其中描述符的种类很多,他们的数据结构各有所不同,如下:
下面我们具体来看一个代码段和数据段描述符:
描述符是一个8字节的数据结构,代码段和数据段描述结构定义如下:
typedef struct _CODE_DATA_SEGMMENT_DESC{ unsigned char limit_0_7; unsigned char limit_8_15; unsigned char base_0_7; unsigned char base_8_15; unsigned char base_16_23; unsigned char type: 4; unsigned char system: 1; unsigned char dpl: 2; unsigned char present: 1; unsigned char limit_16_19: 4; unsigned char avl: 1; unsigned char l: 1; unsigned char d_b: 1; unsigned char granularity: 1; unsigned char base_24_31; } CODE_DATA_SEGMMENT_DESC, *PCODE_DATA_SEGMMENT_DESC;
根据这些域的名字就可以大概知道它的意思了
(1)limit 代表段的边界值(即最大值)
(2)base 代表段的基地址
(3)type域用来指明描述符的类型
(4)system域为1表示这个描述符是普通描述符,为0则为系统描述符
(5)dpl表明访问这个段需要的最低权限(即多少Ring)
(6)present表示描述符对应的段是否在物理内存中
(7)avl软件可利用位。80386对该位的使用未做规定,Intel公司也保证今后开发生产的处理器只要与80386兼容,就不会对该位的使用做任何定义或规定
(8)d_b对于可执行代码段,称为D标志。D=0 默认16位地址和16位或8位操作数,D=1 默认32位地址和32位操作数或8位操作数。(指令前缀0x66选择非默认值的操作数大小,0x67 选择非默认值的地址大小);
对于栈段,称为B标志。B=0 使用16位栈指针,B=1 使用32位栈指针。
对于下扩数据段,称为B标志。B=0 堆栈段上界限0xFFFF(64KB),B=1 堆栈段上界限0xFFFFFFFF(4GB)
(9)granularity确定段限长字段limit值的单位,G=0 单位为1Byte,G=1 单位为4KB
其中,Type域对应如下:
普通描述符:
系统描述符:
接着,我们来看下段寄存器中的值,即描述符在GDT中的相对偏移。实际上,它有一个专门的名称,叫做段选择子(Selector)。而且,它并不是一个简单的偏移,而是要稍微复杂些,结构定义如下:
typedef struct _SELECTOR{ unsigned short rpl: 2; unsigned short ti: 1; unsigned short index: 13; } SELECTOR, *PSELECTOR;
(1) 索引值(index):给出了描述符在GDT或LDT中的索引项号;
(2) 表指示标志TI(Table Index):TI=0 描述符在GDT中,TI=1 描述符在LDT
(3) 请求特权级RPL(Requested Privilege Level):0,1,2,3三个特权级。
保护模式下寻址示意图如下:
/* 描述:仿XT枚举GDT中的描述符 作者:莫灰灰 Blog:http://blog.csdn.net/hu3167343 */ #include <ntddk.h> typedef struct _SELECTOR{ unsigned short rpl: 2; unsigned short ti: 1; unsigned short index: 13; } SELECTOR, *PSELECTOR; typedef struct _CODE_DATA_SEGMMENT_DESC{ unsigned char limit_0_7; unsigned char limit_8_15; unsigned char base_0_7; unsigned char base_8_15; unsigned char base_16_23; unsigned char type: 4; unsigned char system: 1; unsigned char dpl: 2; unsigned char present: 1; unsigned char limit_16_19: 4; unsigned char avl: 1; unsigned char l: 1; unsigned char d_b: 1; unsigned char granularity: 1; unsigned char base_24_31; } CODE_DATA_SEGMMENT_DESC, *PCODE_DATA_SEGMMENT_DESC; typedef struct _INTER_TRAP_GATE_DESC{ unsigned short offset_0_15; unsigned short selector; unsigned char reserved; unsigned char type: 4; unsigned char system: 1; unsigned char dpl: 2; unsigned char present: 1; unsigned short offset_16_31; } INTER_TRAP_GATE_DESC, *PINTER_TRAP_GATE_DESC; typedef struct _CALL_GATE_DESC{ unsigned short offset_0_15; unsigned short selector; unsigned char param_count: 5; unsigned char some_bits: 3; unsigned char type: 4; unsigned char system: 1; unsigned char dpl: 2; unsigned char present: 1; unsigned short offset_16_31; } CALL_GATE_DESC, *PCALL_GATE_DESC; typedef struct _TASK_GATE_DESC{ unsigned short reserved1; unsigned short selector; unsigned char reserved2; unsigned char type: 4; unsigned char system: 1; unsigned char dpl: 2; unsigned char present: 1; unsigned short reserved3; } TASK_GATE_DESC, *PTASK_GATE_DESC; typedef struct _GENERAL_DESC{ unsigned int unknown1; unsigned char unknown2; unsigned char type: 4; unsigned char system: 1; unsigned char dpl: 2; unsigned char present: 1; unsigned short unknown3; } GENERAL_DESC, *PGENERAL_DESC; typedef struct _GDTR{ unsigned short GdtLimit; unsigned short LowGdtbase; unsigned short HighGdtbase; } GDTR, *PGDTR; #define MAKELONG(a, b) ((unsigned long) (((unsigned short) (a)) | ((unsigned long) ((unsigned short) (b))) << 16)) //IDT #define FOUR_BYTE_TO_DWORD( byte_0_7, byte_8_15, byte_16_23, byte_24_31 ) \ \ (unsigned int)byte_0_7 | \ (unsigned int)byte_8_15 << 8 | \ (unsigned int)byte_16_23 << 16 | \ (unsigned int)byte_24_31 << 24 #define TWENTY_BIT_TO_DWORD( bit_0_7, bit_8_15, bit_16_19 ) \ \ (unsigned int)bit_0_7 | \ (unsigned int)bit_8_15 << 8 | \ (unsigned int)bit_16_19 << 16 #define TWO_SHORT_TO_DWORD( short_0_15, short_16_31 ) \ \ (unsigned int)short_0_15 | \ (unsigned int)short_16_31 << 16 char* NonSystemType[16] = { "数据段:只读", // 00 "数据段:只读,访问", // 01 "数据段:读写", // 02 "数据段:读写,访问", // 03 "数据段:只读,向下扩展", // 04 "数据段:只读,向下扩展,访问", // 05 "数据段:读写,向下扩展", // 06 "数据段:读写,向下扩展,访问", // 07 "代码段:只执行", // 08 "代码段:只执行,访问", // 09 "代码段:执行/读", // 10 "代码段:执行/读,访问", // 11 "代码段:只执行,符合", // 12 "代码段:只执行,符合,访问", // 13 "代码段:执行/只读, 符合", // 14 "代码段:执行/只读, 符合,访问" // 15 }; char* SystemType[16] = { "保留", // 00 "16位:任务状态段(可用)", // 01 "LDT", // 02 "16位:任务状态段(忙)", // 03 "16位:调用门", // 04 "任务门", // 05 "16位:中断门", // 06 "16位:陷阱门", // 07 "保留", // 08 "32位:任务状态段(可用)", // 09 "保留", // 10 "32位:任务状态段(忙)", // 11 "32位:调用门", // 12 "保留", // 13 "32位:中断门", // 14 "32位:陷阱门" // 15 }; VOID FindGDT() { PGENERAL_DESC GDT; PCODE_DATA_SEGMMENT_DESC Code_Data_Desc; PCALL_GATE_DESC Call_Gate; GDTR Gdt_Base; ULONG GdtCount; ULONG i; ULONG BaseAddr; ULONG limit; ULONG offset; PUCHAR TypeName; ULONG DPL; PUCHAR SegmentSize; __asm sgdt Gdt_Base; GDT = (PGENERAL_DESC) MAKELONG( Gdt_Base.LowGdtbase, Gdt_Base.HighGdtbase ); GdtCount = (ULONG)((Gdt_Base.GdtLimit) >> 3); for(i = 0; i < GdtCount; i++) { Code_Data_Desc = (PCODE_DATA_SEGMMENT_DESC) &GDT[i]; BaseAddr = FOUR_BYTE_TO_DWORD( Code_Data_Desc->base_0_7,Code_Data_Desc->base_8_15, Code_Data_Desc->base_16_23,Code_Data_Desc->base_24_31); limit = TWENTY_BIT_TO_DWORD( Code_Data_Desc->limit_0_7, Code_Data_Desc->limit_8_15, Code_Data_Desc->limit_16_19 ); if ( Code_Data_Desc->granularity == 1 ) { SegmentSize = "Page";//4kb } else { SegmentSize = "Byte";//1byte } if ( GDT[i].system == 1 ) //代码和数据段描述符 { TypeName = NonSystemType[Code_Data_Desc->type]; } else { TypeName = SystemType[GDT[i].type]; } if (GDT[i].type == 12) //32位调用门 { Call_Gate = (PCALL_GATE_DESC) &GDT[i]; DPL = Call_Gate->dpl; } else { DPL = Code_Data_Desc->dpl; } if (strncmp(TypeName, "保留", strlen("保留")) != 0) { DbgPrint("GDT-ID:%04x 基址: 0x%08X 界限:0x%08X 段粒度:%s 段特权级:%d 类型:%s", i,BaseAddr,limit,SegmentSize,DPL,TypeName); } } } VOID DriverUnload(IN PDRIVER_OBJECT DriverObject) { KdPrint(("Unloading...")); } NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) { DriverObject->DriverUnload = DriverUnload; FindGDT(); return STATUS_SUCCESS; }