以前的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;
}