Friday, August 3, 2012

Windows Driver Development VM

I use the following software packages when investigating Windows kernel behavior:
Frank Rysanek's minimal driver for MinGW is especially appreciated.  It would be nice to have a similar driver framework using the Windows DDK (without other dependencies).

Volatility and the Idle Process on Windows

Volatile memory forensics uses images of physical memory.  These images are of two types:  debug images that include both the physical memory and the state of the processor, and raw images which include only the physical memory.  When raw images are used, Volatility must derive the DirectoryTableBase directly from the image.

When writing a physical memory imager, one can readily output the raw image, but the raw image can be much larger than physical memory, because physical memory is not contiguous.  While 32-bit Windows simply refuses to use addresses larger than 4GB, the 64-bit versions assign memory above 4GB even systems with less than 4GB of RAM because devices are assigned addresses below 4GB (I think this is because PCI uses 32-bit pointers).

It is preferable to use a crash dump format because it is compact and it includes a proper DirectoryTableBase.  Thus the burden of obtaining the DirectoryTableBase falls to the imager (it would be nice if Volatility went back to scanning if DTB was set to NULL in a crash dump image, but it does not).

How Volatility finds the DirectoryTableBase

Volatility needs the DirectoryTableBase variable of an address space assigned so that it can do physical to virtual address conversions.  It does this by scanning a base address space for the DTBSignature, which is actually a signature for an EPROCESS.  Volatility linearly scans memory for every DTBSignature and when found, looks for "Idle" at the offset for the ImageFileName field of EPROCESS (the offset differs by Windows Version).  The scanning code appears in /volatility/plugins/overlay/basic.py in the VolatilityDTB class:
def generate_suggestions(self):
    offset = 0
    data = self.obj_vm.read(offset, constants.SCAN_BLOCKSIZE)
    while data:
        found = data.find(str(self.obj_parent.DTBSignature), 0)
        while found >= 0:
            proc = obj.Object("_EPROCESS", offset = offset + found, vm = self.obj_vm)
            if 'Idle' in proc.ImageFileName.v():
               yield proc.Pcb.DirectoryTableBase.v()
            found = data.find(str(self.obj_parent.DTBSignature), found + 1)

        offset += len(data)
        data = self.obj_vm.read(offset, constants.SCAN_BLOCKSIZE)
Once this Idle process is found, then Volatility extracts the DTB from the first DirectoryTableBase field of the KPROCESS structure, which is the first field of EPROCESS.  (While the generate_suggestions method can return a list of pointer candidates, Volatility always uses the first one found).

Obtaining the Idle DTB from a Kernel Driver

From a kernel driver, calling PsGetCurrentProcess (an alias of IoGetCurrentProcess) returns a pointer to the EPROCESS structure of the process that called the driver.  If one scans the ActiveProcessLinks field of the structure, one can obtain all the processes of the system, but not the EPROCESS structure found by Volatility.  A mysterious EPROCESS structure is found, in which the parent process PID is 0 (from the InheritedUniqueProcessId field), but the UniqueProcessId is a large number consistent with a pointer.  The ImageFileName of the EPROCESS is not "Idle", but a non-ASCII string (0xFF, 0xFF, 0xFF, 0xFF in WinXP SP2).  Its Peb is NULL, as is Pcb.DirectoryTableBase[0].  A hint that UniqueProcessId is not just a ULONG can be seen from the EPROCESS type, which states that the field is a PVOID.  And UniqueProcessId is indeed a pointer to the proper EPROCESS structure that Volatility finds in its scanning.

The following code shows how we obtain the DirectoryTableBase for Idle:
PEPROCESS proc = IoGetCurrentProcess();
PEPROCESS next = proc;

do
{
    if( (next->ImageFileName[0] < 32   ||
         next->ImageFileName[0] > 126) &&
         IS_POINTER((ULONG)next->UniqueProcessId))
        break;

    next = CONTAINING_RECORD(next->ActiveProcessLinks.Flink, EPROCESS, ActiveProcessLinks);
}

PEPROCESS idle = (PEPROCESS)next->UniqueProcessId;
ULONG dtb = idle->Pcb.DirectoryTableBase[0];
There are alternative methods.  The global variable PsInitialSystemProcess seems like it might work, but it is not available to MinGW drivers (the build system I'm using), and may be the "System" process (PID 4), not "Idle".  Another possibility is to call IoGetCurrentProcess on DriverEntry().  This also seems to be System, not Idle.