The protection of certain key kernel images is one of the more
critical aspects of PatchGuard's protection schemes. If a driver
were still able to hook functions in nt, ndis, or
any other key kernel components, then PatchGuard would be mostly
irrelevant. In order to address this concern, PatchGuard performs a
set of operations that are intended to ensure that system images
cannot be tampered with. The table in figure
shows which kernel images are
currently protected by this scheme.

Image Name

ntoskrnl.exe

hal.dll

ndis.sys

The approach taken to protect each of these images is the same. To
kick things off, the address of a symbol that resides within the image
is passed to a PatchGuard sub-routine that will be referred to as
nt!PgCreateImageSubContext. This routine is
prototyped as shown below:

For ntoskrnl.exe, the address of
nt!KiFilterFiberContext is passed in as the symbol address.
For hal.dll, the address of HalInitializeProcessor
is passed. Finally, the address passed for ndis.sys is its
entry point address which is obtained through a call to
nt!GetModuleEntryPoint.

Inside nt!PgCreateImageSubContext, the basic approach taken
to protect the images is through the generation of a few distinct
PatchGuard sub-contexts. The first sub-context is designed to hold
the checksum of an individual image's sections, with a few
exceptions. The second and third sub-contexts hold the checksum of
an image's Import Address Table (IAT) and Import
Directory, respectively. These routines all make use of a shared
routine that is responsible for generating a protection sub-context
that holds the checksum for a block of memory using the random XOR
key and random rotate bits stored in the parent PatchGuard context
structure. The prototype for this routine is shown below:

The block checksum sub-context stores the checksum state at the end of
the PATCHGUARD_CONTEXT. The checksum state is stored in a
BLOCK_CHECKSUM_STATE structure. The Unknown
attribute of the structure is initialized to the Unknown
parameter from nt!PgCreateBlockChecksumSubContext. The
purpose of this field was not deduced, but the value was set to zero
during debugging.

The checksum algorithm used by the routine is fairly simple. The
pseudo-code below shows how it works conceptually:

The end result is that Checksum32 holds the checksum of the
block which is subsequently stored in the Checksum attribute
of the checksum state structure along with the original block size and
block base address that were passed to the function.

For the purpose of initializing the checksum of image sections,
nt!PgCreateImageSubContext calls into
nt!PgCreateImageSectionSubContext which is prototyped as:

This routine first checks to see if
nt!KiOpPrefetchPatchCount is zero. If it is not, a block
checksum context is created that does not cover all of the sections
in the image3.2. Otherwise, the function appears to enumerate the various
sections included in the supplied image, calculating the checksum
across each. It appears to exclude checksums of sections named
INIT, PAGEVRFY, PAGESPEC, and
PAGEKD.

To account for an image's Import Address Table and
Import Directory, nt!PgCreateImageSubContext calls
nt!PgCreateBlockChecksumSubContext on the directory entries
for both, but only if the directory entries exist and are valid for
the supplied image.