Uninformed: Informative Information for the Uninformed

Vol 3» 2006.Jan


GDT/IDT

The protection of the Global Descriptor Table (GDT) and the Interrupt Descriptor Table (IDT) is another important feature of PatchGuard. The GDT is used to describe memory segments that are used by the kernel. It is especially lucrative to malicious applications due to the fact that modifying certain key GDT entries could lead to non-privileged, user-mode applications being able to modify kernel memory. The IDT is also useful, both in a malicious context and in a legitimate context. In some cases, third parties may wish to intercept certain hardware or software interrupts before passing it off to the kernel. Unless done right, hooking IDT entries can be very dangerous due to the considerations that have to be made when running in the context of an interrupt request handler.

The actual implementation of GDT/IDT protection is accomplished through the use of the nt!PgCreateBlockChecksumSubContext function which is passed the contents of both descriptor tables. Since the registers that hold the GDT and IDT are relative to a given processor, PatchGuard creates a separate context for each table on each individual processor. To obtain the address of the GDT and the IDT for a given processor, PatchGuard first uses nt!KeSetAffinityThread to ensure that it's running on a specific processor. After that, it makes a call to nt!KiGetGdtIdt which stores the GDT and the IDT base addresses as output parameters as shown in the prototype below:

VOID KiGetGdtIdt(
    OUT PVOID *Gdt,
    OUT PVOID *Idt);

The actual protection of the GDT and the IDT is done in the context of two separate functions that have been labeled nt!PgCreateGdtSubContext and PgCreateIdtSubContext. These routines are prototyped as shown below:

PPATCHGUARD_SUB_CONTEXT PgCreateGdtSubContext(
    IN PPATCHGUARD_CONTEXT ParentContext,
    IN UCHAR ProcessorNumber);


PPATCHGUARD_SUB_CONTEXT PgCreateIdtSubContext(
    IN PPATCHGUARD_CONTEXT ParentContext,
    IN UCHAR ProcessorNumber);

Both routines are called in the context of a loop that iterates across all of the processors on the machine with respect to nt!KeNumberProcessors.