Global Descriptor Table

 

The logical address or virtual address of a memory location has a selector that points to a descriptor table. The address of this table is  got from a register called the Global Descriptor Table Register. The windows os sets this register to a value. The sgdt instruction gives us the address of where this table starts like in the case of idt. This table is made up of a convoluted series of 8 byte structures. Windows 2000 stores this table at 80036000 and in XP Sp2 it is at 8003f000.

 

This table can be of size 64K which allows it to store 8192 entries. The same rules also apply to the LDT and the IDT.

 

#include "ntddk.h"

#include <stdio.h>

#define MAKELONG(a, b) ((unsigned long) (((unsigned short) (a)) | ((unsigned long) ((unsigned short) (b))) << 16))

#define MAKEBASE(a, b , c) ((unsigned long) (((unsigned short) (a)) | ((unsigned long) ((unsigned short) (b))) << 16 ) | ((unsigned long) ((unsigned short) (c))) << 24 )

#define MAKELIMIT(a, b) ((unsigned long) (((unsigned short) (a)) | ((unsigned long) ((unsigned short) (b))) << 16))

#define MAKEGRANULAR(a, b) ((unsigned long) ((((unsigned short) (a)) << 12) | ((unsigned long) ((unsigned short) (b)))))

//#pragma pack(1)

struct GDT

{

unsigned Limit1  : 16; // bits 15..00

unsigned Base1  : 16; // bits 15..00

unsigned Base2  : 8; // bits 23..16

unsigned Type   : 4; // segment type

unsigned S    : 1; // type (0=system, 1=code/data)

unsigned DPL   : 2; // descriptor privilege level

unsigned P    : 1; // segment present

unsigned Limit2  : 4; // bits 19..16

unsigned AVL   : 1; // available to programmer

unsigned Reserved : 1;

unsigned DB    : 1; // 0=16-bit, 1=32-bit

unsigned G    : 1; // granularity (1=4KB)

unsigned Base3  : 8; // bits 31..24

};

struct GDTINFO

{

unsigned short IDTLimit , LowIDTbase , HiIDTbase;

};

//#pragma pack()

VOID OnUnload(PDRIVER_OBJECT DriverObject )

{         

DbgPrint("OnUnload");

}

NTSTATUS DriverEntry(PDRIVER_OBJECT d , PUNICODE_STRING r)

{

char a[100];

struct GDTINFO gdt_info;

struct GDT *g;

int count,addr , limit;

d->DriverUnload  = OnUnload;

__asm sgdt gdt_info

g = (struct GDT*) MAKELONG(gdt_info.LowIDTbase,gdt_info.HiIDTbase);

DbgPrint("g=%x",g);

for(count=0;count < 12 ; count++)

{

a[0] = 0;

if ( g->Type == 3)

strcpy(a,"DATA32");

if ( g->Type == 0x0b)

strcpy(a,"CODE32");

if ( g->Type == 0x09)

strcpy(a,"TSS32");

if ( g->Type == 0x02)

strcpy(a,"LDT");

limit = MAKELIMIT(g->Limit1,g->Limit2);

if ( g->G == 1)

limit = MAKEGRANULAR(limit, 0xfff);

DbgPrint("%x %s %x %x %d %s G=%d", count*8,a , MAKEBASE(g->Base1,g->Base2,g->Base3),limit, g->DPL , g->P == 1 ? "P" : "NP" , g->G);

//DbgPrint("Base %x %x %x Limit %x %x Type=%x",g->Base1,g->Base2, g->Base3,g->Limit1,g->Limit2,g->Type);

g++;

}

return STATUS_SUCCESS;

}

 

g=8003f000

0  0 0 0 NP G=0

8 CODE32 0 fffffff 0 P G=1

10 DATA32 0 fffffff 0 P G=1

18 CODE32 0 fffffff 3 P G=1

20 DATA32 0 fffffff 3 P G=1

28 CODE32 80042000 20ab 0 P G=0

30 DATA32 ffdff000 1fff 0 P G=1

38 DATA32 0 fff 3 P G=0

40 LDT 400 ffff 3 P G=0

48  0 0 0 NP G=0

50 TSS32 80550480 68 0 P G=0

58 TSS32 805504e8 68 0 P G=0

OnUnload

 

We start with the assembler instruction sgdt which fills up a 6 bytes structure gdtinfo. This structure has two shorts that will give us the low and high bytes of where the gdt table starts in memory. We use the macro MAKELONG to give us the actual address. We then store this value in g which is a array of gdt structures. We display the first 12 selectors only.

 

The Type member tells us what type this selector represents 3 means DATA, b means code etc. The Limit field is stored in two places and hence we have to use our own macro to get at the value. When the granuality field is 1, this value has to start at a page boundary and hence we have to left shift it by 12 bits. The base of the memory where the selector points to is stored at three different places and hence we use one more macro. The Present bit is simply one bit.