Uninformed: Informative Information for the Uninformed

Vol 4» 2006.Jun

Validating Pool Block Information

Kernel pool management appears to be slightly different from usermode heap management. However, if one assumes that the only concern is dealing with pool memory allocations which are less than PAGE_SIZE, it is fairly similar. Each call to ExAllocatePoolWithTag() returns a pre-buffer header as follows:

0: kd> dt _POOL_HEADER
   +0x000 PreviousSize     : Pos 0, 9 Bits
   +0x000 PoolIndex        : Pos 9, 7 Bits
   +0x002 BlockSize        : Pos 0, 9 Bits
   +0x002 PoolType         : Pos 9, 7 Bits
   +0x000 Ulong1           : Uint4B
   +0x004 ProcessBilled    : Ptr32 _EPROCESS
   +0x004 PoolTag          : Uint4B
   +0x004 AllocatorBackTraceIndex : Uint2B
   +0x006 PoolTagHash      : Uint2B

For the purposes of locating objects, the following is a breakdown of what could be useful. Again, static refers to fields common between similar executive objects and not all allocated POOL_HEADER structures.

\begin{tabular}{\vert l\vert c\vert l\vert}
...extbf{PoolTagHash} & NotCertain & Not certain \\

The POOL_HEADER contains several fields that appear to be common to similar objects which could be used to further verify the likelihood of locating an object of a specific type such as BlockSize, PoolType, and PoolTag.

In addition to the mentioned static fields, two other fields, PreviousSize and BlockSize, can be used to validate that the currently assumed POOL_HEADER appears to be a valid, allocated pool block and is in one of the pool managers maintained link lists. PreviousSize and BlockSize are multiples of the minimum pool alignment which is 8 bytes on a 32bit system and 16 bytes on a 64bit system. These two elements supply byte offsets to the neighboring pool blocks.

If PreviousSize equals 0, the current POOL_HEADER should be the first pool block in the pool's contiguous allocations. If it is not, it should be the same as the previous POOL_HEADERs BlockSize. The BlockSize should never equal 0 and should always be the same as the proceeding POOL_HEADERs PreviousSize.

The following code validates a POOL_HEADER of an allocated pool block.

// Assumes BlockOffset < PAGE_SIZE
// ASSERTS Flink == Flink->Blink && Blink == Blink->Flink
BOOLEAN ValidatePoolBlock (
    IN PPOOL_HEADER     pPoolHdr,
    IN VALIDATE_ADDR    pValidator
) {
    BOOLEAN bReturn = FALSE;

    PPOOL_HEADER    pPrev;
    PPOOL_HEADER    pNext;

    pPrev = (PPOOL_HEADER)((PUCHAR)pPoolHdr
                            - (pPoolHdr->PreviousSize * sizeof(POOL_HEADER)));
    pNext = (PPOOL_HEADER)((PUCHAR)pPoolHdr
                            + (pPoolHdr->BlockSize * sizeof(POOL_HEADER)));

      ( pPoolHdr == pNext )
    ||( pValidator( pNext + sizeof(POOL_HEADER) - 1 )
     && pPoolHdr->BlockSize == pNext->PreviousSize )
      ( pPoolHdr  != pPrev )
    ||( pValidator( pPrev )
     && pPoolHdr->PreviousSize == pPrev->BlockSize )
        bReturn = TRUE;

    return bReturn;