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.