Bitdefender Hypervisor Memory Introspection
winobj.c File Reference

This file contains the logic that parses the Windows Kernel object namespace in order to find an object of interest. More...

#include "winobj.h"
#include "guests.h"
#include "swapmem.h"
#include "windrvobj.h"
#include "winpe.h"

Go to the source code of this file.

Data Structures

struct  _ROOT_SEARCH_CTX
 A context structure used to pass information between the various callbacks that search for a Root Directory. More...
 
struct  _WINOBJ_SWAPCTX
 A context structure used to pass information between the various callbacks that search for an object. More...
 
struct  _ROOT_HINT
 Hint structure used to search for possible object namespace root directory entries. More...
 

Macros

#define WIN_POOL_TAG_DIRECTORY   0x65726944
 Allocation tag for the _OBJECT_DIRECTORY Windows kernel structure. More...
 
#define WIN_POOL_TAG_DIRECTORY_7   0xe5726944
 Allocation tag for the _OBJECT_DIRECTORY Windows 7 kernel structure. More...
 
#define WIN_POOL_TAG_OBJECT   0x546a624f
 Allocation tag for the _OBJECT_TYPE Windows kernel structure. More...
 
#define WIN_POOL_TAG_OBJECT_7   0xd46a624f
 Allocation tag for the _OBJECT_TYPE Windows 7 kernel structure. More...
 
#define HEADER_SIZE_CREATOR_INFO64   0x20
 32-bit _OBJECT_HEADER_CREATOR_INFO size. More...
 
#define HEADER_SIZE_CREATOR_INFO32   0x10
 64-bit _OBJECT_HEADER_CREATOR_INFO size. More...
 
#define HEADER_SIZE_CREATOR_INFO(is64)   (is64) ? HEADER_SIZE_CREATOR_INFO64 : HEADER_SIZE_CREATOR_INFO32
 
#define HEADER_SIZE_NAME_INFO64   0x20
 32-bit _OBJECT_HEADER_NAME_INFO size. More...
 
#define HEADER_SIZE_NAME_INFO32   0x10
 64-bit _OBJECT_HEADER_NAME_INFO size. More...
 
#define HEADER_SIZE_NAME_INFO(is64)   (is64) ? HEADER_SIZE_NAME_INFO64 : HEADER_SIZE_NAME_INFO32
 
#define HEADER_SIZE_HANDLE_INFO64   0x10
 32-bit _OBJECT_HEADER_HANDLE_INFO size. More...
 
#define HEADER_SIZE_HANDLE_INFO32   0x08
 64-bit _OBJECT_HEADER_HANDLE_INFO size. More...
 
#define HEADER_SIZE_HANDLE_INFO(is64)   (is64) ? HEADER_SIZE_HANDLE_INFO64 : HEADER_SIZE_HANDLE_INFO32
 
#define HEADER_SIZE_QUOTA_INFO64   0x20
 32-bit _OBJECT_HEADER_QUOTA_INFO size. More...
 
#define HEADER_SIZE_QUOTA_INFO32   0x10
 64-bit _OBJECT_HEADER_QUOTA_INFO size. More...
 
#define HEADER_SIZE_QUOTA_INFO(is64)   (is64) ? HEADER_SIZE_QUOTA_INFO64 : HEADER_SIZE_QUOTA_INFO32
 
#define HEADER_SIZE_PROC_INFO64   0x10
 32-bit _OBJECT_HEADER_PROCESS_INFO size. More...
 
#define HEADER_SIZE_PROC_INFO32   0x08
 64-bit _OBJECT_HEADER_PROCESS_INFO size. More...
 
#define HEADER_SIZE_PROC_INFO(is64)   (is64) ? HEADER_SIZE_PROC_INFO64 : HEADER_SIZE_PROC_INFO32
 
#define ROOT_DIR_POOL_HEADER_OFF64   0x60
 The size of the headers before a Root Directory allocation on 64-bit Windows. More...
 
#define ROOT_DIR_POOL_HEADER_OFF32   0x30
 The size of the headers before a Root Directory allocation on 32-bit Windows. More...
 
#define TYPE_IDX_TYPE   2
 The index of the type Type in the type arrays. More...
 
#define OBJECT_DIR_ENTRY_COUNT   37
 The maximum number of entries in an object directory. More...
 
#define ROOT_HINT_PTR_COUNT   3
 The number of hint pointers around a root candidate. More...
 

Typedefs

typedef struct _ROOT_SEARCH_CTX ROOT_SEARCH_CTX
 A context structure used to pass information between the various callbacks that search for a Root Directory. More...
 
typedef struct _ROOT_SEARCH_CTXPROOT_SEARCH_CTX
 
typedef struct _WINOBJ_SWAPCTX WINOBJ_SWAPCTX
 A context structure used to pass information between the various callbacks that search for an object. More...
 
typedef struct _WINOBJ_SWAPCTXPWINOBJ_SWAPCTX
 
typedef struct _ROOT_HINT ROOT_HINT
 Hint structure used to search for possible object namespace root directory entries. More...
 
typedef struct _ROOT_HINTPROOT_HINT
 

Enumerations

enum  IM_FLG {
  IM_FLG_CREATOR_INFO = 0x01, IM_FLG_NAME_INFO = 0x02, IM_FLG_HANDLE_INFO = 0x04, IM_FLG_QUOTA_INFO = 0x08,
  IM_FLG_PROCESS_INFO = 0x10
}
 Info Mask flags from the Object Header. More...
 

Functions

static void IntWinObjCheckDrvDirSearchState (void)
 Checks if the search is still going, or if it finished with success or with an error. More...
 
static BOOLEAN IntWinObjIsRootSearchOver (void)
 
static void IntWinObjCancelRootTransactions (void)
 Cancels any pending swap memory reads left for the root directory. More...
 
static INTSTATUS IntWinObjGetObjectNameInfo (QWORD ObjectGva, QWORD *BufferGva, WORD *Length, QWORD *ParentDirGva)
 Returns the name information for kernel objects that have one. More...
 
static INTSTATUS IntWinObjHandleDriverDirectoryEntryInMemory (WINOBJ_SWAPCTX *Context, QWORD Cr3, QWORD Gva, QWORD Gpa, void *Data, DWORD DataSize, DWORD Flags)
 This callback is invoked for namespace directory entries that may represent driver objects. More...
 
static INTSTATUS IntWinObjParseDriverDirectory (WINOBJ_SWAPCTX *Context, QWORD Cr3, QWORD Gva, QWORD Gpa, void *Data, DWORD DataSize, DWORD Flags)
 This callback is invoked for namespace entries that may represent driver directories. More...
 
static INTSTATUS IntWinObjHandleObjectInMemory (WINOBJ_SWAPCTX *Context, QWORD Cr3, QWORD Gva, QWORD Gpa, void *Data, DWORD DataSize, DWORD Flags)
 This callback is invoked for each object in an object directory entries list. More...
 
static INTSTATUS IntWinObjHandleDirectoryEntryInMemory (WINOBJ_SWAPCTX *Context, QWORD Cr3, QWORD Gva, QWORD Gpa, void *Data, DWORD DataSize, DWORD Flags)
 This callback is invoked for each object contained in the root namespace. More...
 
static INTSTATUS IntWinObjHandleRootDirTagInMemory (ROOT_SEARCH_CTX *Context, QWORD Cr3, QWORD Gva, QWORD Gpa, void *Data, DWORD DataSize, DWORD Flags)
 This callback is invoked for every candidate root directory namespace object. More...
 
INTSTATUS IntWinObjFindRootDirectory (PROOT_HINT Hint, QWORD *PossibleRoot)
 Returns a possible object namespace root directory. More...
 
INTSTATUS IntWinObjIsTypeObject (QWORD Gva)
 Checks if the supplied guest memory location holds a valid type object. More...
 
INTSTATUS IntWinObjGetPoolHeaderForObject (QWORD ObjectGva, POOL_HEADER *PoolHeader)
 Reads the _POOL_HEADER structure for a given kernel object. More...
 
static void IntWinObjReinitGlobalState (void)
 Resets the global search state. More...
 
static INTSTATUS IntWinGuestFindDriversNamespaceNoBuffer (void)
 Runs the driver object namespace search ignoring the gGuest.KernelBuffer and reading the data directly from the guest memory. More...
 
INTSTATUS IntWinGuestFindDriversNamespace (void)
 Runs the driver object namespace search. More...
 
void IntWinObjCleanup (void)
 Cleans up any resources allocated by the object search. More...
 

Variables

static ROOT_SEARCH_CTX gPossibleRootGvas [32] = {0}
 The possible addresses at which the root directory may be located. More...
 
static DWORD gRootCount = 0
 The number of valid entries inside the gPossibleRootGvas array. More...
 
static DWORD gDirEntriesToCheck = OBJECT_DIR_ENTRY_COUNT
 The number of directory entries left to check. More...
 
static DWORD gPendingDrivers = 0
 The count of pending driver objects to be checked. More...
 
static DWORD gFoundDrivers = 0
 The number of found driver objects. More...
 
static BOOLEAN gStop = FALSE
 Set to True when the search must be aborted. More...
 
static LIST_ENTRY gSwapHandles
 List of all the swap handles used by the namespace parser. More...
 

Detailed Description

This file contains the logic that parses the Windows Kernel object namespace in order to find an object of interest.

Currently, it is needed only for discovering driver objects, so it is a bit specialized for that task.

The namespace is organized in directories, starting from the root directory ("\", kernel global: ObpRootDirectoryObject). Each directory contains other directories and/or objects.

Each object is preceded in memory by an object header. This is mandatory. Additionally, one or more optional headers may be present.

An allocation looks like this:

--------------------------------
| Pool Header |
================================
| Process Info Header | ^
-------------------------------- |
| Quota Info Header | |
-------------------------------- |
| Handle Info Header | | Optional Headers (always in this order)
-------------------------------- | (Presence is controlled by bits in the InfoMask field from the Object Header)
| Name Info Header | |
-------------------------------- |
| Creator Info Header | V
================================
| Object Header |
================================
| Object | <-- Overlaps Body field from the Object Header
--------------------------------

The InfoMask field from the Object Header tells us which Optional Headers are present:


Value Type
0x01 _OBJECT_HEADER_CREATOR_INFO
0x02 _OBJECT_HEADER_NAME_INFO
0x04 _OBJECT_HEADER_HANDLE_INFO
0x08 _OBJECT_HEADER_QUOTA_INFO
0x10 _OBJECT_HEADER_PROCESS_INFO

Each Directory has an array of 37 entries; each entry is a linked list of _OBJECT_DIRECTORY_ENTRY structures

Let's look at an example from a Windows memory dump, to see how the namespace is organized. First, we can look at the root of the namespace (not all entries are listed): (note that the name of the object shouldn't actually be put in double quotes, but I want to avoid a GCC warning, and for that I have to make sure no comment line ends with a back slash)

0: kd> !object "\"
Object: ffff970b5d219ea0 Type: (ffff8702eaa86520) Directory
ObjectHeader: ffff970b5d219e70 (new version)
HandleCount: 0 PointerCount: 48
Directory Object: 00000000 Name: "\"
Hash Address Type Name
---- ------- ---- ----
01 ffff8702ec9e3750 Mutant PendingRenameMutex
...
23 ffff8702eb59e790 Device Ntfs
ffff970b5d7e7960 Directory FileSystem
ffff970b5d217c00 Directory KernelObjects
...
36 ffff8702ee011270 Event SAM_SERVICE_STARTED
ffff970b5d7e7b20 Directory Driver

We can see the two directories that interest us: FileSystem (hash 23), and Driver (hash 36). We can also see that the root is located at address 0xffff970b5d219ea0 inside the kernel and that its type is Directory. We can dump it:

0: kd> dt nt!_OBJECT_DIRECTORY -v -b ffff970b5d219ea0
struct _OBJECT_DIRECTORY, 8 elements, 0x160 bytes
+0x000 HashBuckets : (37 elements)
[00] (null)
[01] 0xffff970b`5d8cb060
...
[23] 0xffff970b`5dcacd00
...
[35] (null)
[36] 0xffff970b`652be730
+0x128 Lock : struct _EX_PUSH_LOCK, 7 elements, 0x8 bytes
+0x130 DeviceMap : (null)
+0x138 ShadowDirectory : (null)
+0x140 SessionId : 0xffffffff
+0x148 NamespaceEntry : (null)
+0x150 SessionObject : (null)
+0x158 Flags : 0

The HashBuckets fields contains the objects listed above with the "!object" command (not all entries are shown, to keep the list short). We can further look at entries 23 and 36:

0: kd> dt nt!_OBJECT_DIRECTORY_ENTRY 0xffff970b`5dcacd00
+0x000 ChainLink : 0xffff970b`5d7e4720 _OBJECT_DIRECTORY_ENTRY
+0x008 Object : 0xffff8702`eb59e790 Void
+0x010 HashValue : 0xa70094
0: kd> !object 0xffff8702`eb59e790
Object: ffff8702eb59e790 Type: (ffff8702eaa7f940) Device
ObjectHeader: ffff8702eb59e760 (new version)
HandleCount: 0 PointerCount: 2
Directory Object: ffff970b5d219ea0 Name: Ntfs
0: kd> dt nt!_OBJECT_DIRECTORY_ENTRY 0xffff970b`5d7e4720
+0x000 ChainLink : 0xffff970b`5d2167c0 _OBJECT_DIRECTORY_ENTRY
+0x008 Object : 0xffff970b`5d7e7960 Void
+0x010 HashValue : 0x200fa1a2
0: kd> !object 0xffff970b`5d7e7960
Object: ffff970b5d7e7960 Type: (ffff8702eaa86520) Directory
ObjectHeader: ffff970b5d7e7930 (new version)
HandleCount: 0 PointerCount: 34
Directory Object: ffff970b5d219ea0 Name: FileSystem
Hash Address Type Name
---- ------- ---- ----
02 ffff8702ee2ae060 Driver mrxsmb10
...
ffff8702edafd060 Driver wcifs
ffff970b5d7e8ba0 Directory Filters
...
35 ffff8702eb5a3340 Device UdfsCdRomRecognizer

Each pointer in the HashBuckets array points to a _OBJECT_DIRECTORY_ENTRY structure. The Object field of this structure points to the actual object. Dumping the structure for index 23 we see that it is a device object, which does not interest us, but the ChainLink field will point to another _OBJECT_DIRECTORY_ENTRY structure. Dumping that we get our entry for the Driver entry, which is also an _OBJECT_DIRECTORY structure. It can contain driver objects, device objects or other directories. FileSystem is similar. We can manually dump each individual driver object using the same method and obtain _OBJECT_DIRECTORY_ENTRIES like this, which will point to a _DRIVER_OBJECT:

0: kd> dt nt!_OBJECT_DIRECTORY_ENTRY 0xffff970b`6600c9a0
+0x000 ChainLink : 0xffff970b`65fec5b0 _OBJECT_DIRECTORY_ENTRY
+0x008 Object : 0xffff8702`ee2ae060 Void
+0x010 HashValue : 0x293829f
0: kd> !object 0xffff8702`ee2ae060
Object: ffff8702ee2ae060 Type: (ffff8702eaa779e0) Driver
ObjectHeader: ffff8702ee2ae030 (new version)
HandleCount: 0 PointerCount: 2
Directory Object: ffff970b5d7e7960 Name: mrxsmb10

What we haven't seen so far is the name of the objects, which is not included inside the objects. We know that before the object itself there is an _OBJECT_HEADER structure, which has its Body field (at offset 0x30 in our case) overlapping the object, so we start by looking at the InfoMask field in that structure:

0 : kd > dt nt !_OBJECT_HEADER InfoMask (0xffff970b5d7e7b20 - 0x30)
+ 0x01a InfoMask : 0x2 ''

This means that only the _OBJECT_HEADER_NAME_INFO optional header is present:

0: kd> dt nt!_OBJECT_HEADER_NAME_INFO (ffff970b5d7e7b20 - 0x50)
+0x000 Directory : 0xffff970b`5d219ea0 _OBJECT_DIRECTORY
+0x008 Name : _UNICODE_STRING "Driver"
+0x018 ReferenceCount : 0n0
+0x01c Reserved : 0

As for the pool tag, we can now go one step further and look at the _POOL_HEADER:

0: kd> dt nt!_POOL_HEADER PoolTag (ffff970b5d7e7b20 - 0x60)
+0x004 PoolTag : 0x65726944
0: kd> db ffff970b5d7e7b20 - 0x5c L4
ffff970b`5d7e7ac4 44 69 72 65 Dire

IntWinGuestFindDriversNamespace follows a similar strategy during the search, but it needs to account for the structures being in paged memory, so it will use IntSwapMemRead to force page faults for any pages that may not be present during the search.

Definition in file winobj.c.

Macro Definition Documentation

◆ HEADER_SIZE_CREATOR_INFO

#define HEADER_SIZE_CREATOR_INFO (   is64)    (is64) ? HEADER_SIZE_CREATOR_INFO64 : HEADER_SIZE_CREATOR_INFO32

Definition at line 221 of file winobj.c.

◆ HEADER_SIZE_CREATOR_INFO32

#define HEADER_SIZE_CREATOR_INFO32   0x10

64-bit _OBJECT_HEADER_CREATOR_INFO size.

Definition at line 220 of file winobj.c.

Referenced by IntWinObjGetObjectNameInfo(), and IntWinObjGetPoolHeaderForObject().

◆ HEADER_SIZE_CREATOR_INFO64

#define HEADER_SIZE_CREATOR_INFO64   0x20

32-bit _OBJECT_HEADER_CREATOR_INFO size.

Definition at line 219 of file winobj.c.

Referenced by IntWinObjGetObjectNameInfo(), and IntWinObjGetPoolHeaderForObject().

◆ HEADER_SIZE_HANDLE_INFO

#define HEADER_SIZE_HANDLE_INFO (   is64)    (is64) ? HEADER_SIZE_HANDLE_INFO64 : HEADER_SIZE_HANDLE_INFO32

Definition at line 229 of file winobj.c.

◆ HEADER_SIZE_HANDLE_INFO32

#define HEADER_SIZE_HANDLE_INFO32   0x08

64-bit _OBJECT_HEADER_HANDLE_INFO size.

Definition at line 228 of file winobj.c.

◆ HEADER_SIZE_HANDLE_INFO64

#define HEADER_SIZE_HANDLE_INFO64   0x10

32-bit _OBJECT_HEADER_HANDLE_INFO size.

Definition at line 227 of file winobj.c.

◆ HEADER_SIZE_NAME_INFO

#define HEADER_SIZE_NAME_INFO (   is64)    (is64) ? HEADER_SIZE_NAME_INFO64 : HEADER_SIZE_NAME_INFO32

Definition at line 225 of file winobj.c.

◆ HEADER_SIZE_NAME_INFO32

#define HEADER_SIZE_NAME_INFO32   0x10

64-bit _OBJECT_HEADER_NAME_INFO size.

Definition at line 224 of file winobj.c.

Referenced by IntWinObjGetObjectNameInfo(), and IntWinObjGetPoolHeaderForObject().

◆ HEADER_SIZE_NAME_INFO64

#define HEADER_SIZE_NAME_INFO64   0x20

32-bit _OBJECT_HEADER_NAME_INFO size.

Definition at line 223 of file winobj.c.

Referenced by IntWinObjGetObjectNameInfo(), and IntWinObjGetPoolHeaderForObject().

◆ HEADER_SIZE_PROC_INFO

#define HEADER_SIZE_PROC_INFO (   is64)    (is64) ? HEADER_SIZE_PROC_INFO64 : HEADER_SIZE_PROC_INFO32

Definition at line 237 of file winobj.c.

◆ HEADER_SIZE_PROC_INFO32

#define HEADER_SIZE_PROC_INFO32   0x08

64-bit _OBJECT_HEADER_PROCESS_INFO size.

Definition at line 236 of file winobj.c.

◆ HEADER_SIZE_PROC_INFO64

#define HEADER_SIZE_PROC_INFO64   0x10

32-bit _OBJECT_HEADER_PROCESS_INFO size.

Definition at line 235 of file winobj.c.

◆ HEADER_SIZE_QUOTA_INFO

#define HEADER_SIZE_QUOTA_INFO (   is64)    (is64) ? HEADER_SIZE_QUOTA_INFO64 : HEADER_SIZE_QUOTA_INFO32

Definition at line 233 of file winobj.c.

◆ HEADER_SIZE_QUOTA_INFO32

#define HEADER_SIZE_QUOTA_INFO32   0x10

64-bit _OBJECT_HEADER_QUOTA_INFO size.

Definition at line 232 of file winobj.c.

◆ HEADER_SIZE_QUOTA_INFO64

#define HEADER_SIZE_QUOTA_INFO64   0x20

32-bit _OBJECT_HEADER_QUOTA_INFO size.

Definition at line 231 of file winobj.c.

◆ OBJECT_DIR_ENTRY_COUNT

#define OBJECT_DIR_ENTRY_COUNT   37

The maximum number of entries in an object directory.

Definition at line 250 of file winobj.c.

Referenced by IntWinObjHandleRootDirTagInMemory(), IntWinObjParseDriverDirectory(), and IntWinObjReinitGlobalState().

◆ ROOT_DIR_POOL_HEADER_OFF32

#define ROOT_DIR_POOL_HEADER_OFF32   0x30

The size of the headers before a Root Directory allocation on 32-bit Windows.

Definition at line 243 of file winobj.c.

Referenced by IntWinGuestFindDriversNamespace(), and IntWinObjFindRootDirectory().

◆ ROOT_DIR_POOL_HEADER_OFF64

#define ROOT_DIR_POOL_HEADER_OFF64   0x60

The size of the headers before a Root Directory allocation on 64-bit Windows.

Definition at line 241 of file winobj.c.

Referenced by IntWinGuestFindDriversNamespace(), and IntWinObjFindRootDirectory().

◆ ROOT_HINT_PTR_COUNT

#define ROOT_HINT_PTR_COUNT   3

The number of hint pointers around a root candidate.

Definition at line 276 of file winobj.c.

Referenced by IntWinGuestFindDriversNamespace(), IntWinGuestFindDriversNamespaceNoBuffer(), and IntWinObjFindRootDirectory().

◆ TYPE_IDX_TYPE

#define TYPE_IDX_TYPE   2

The index of the type Type in the type arrays.

Definition at line 247 of file winobj.c.

◆ WIN_POOL_TAG_DIRECTORY

#define WIN_POOL_TAG_DIRECTORY   0x65726944

Allocation tag for the _OBJECT_DIRECTORY Windows kernel structure.

Stands for 'Dire'

Definition at line 186 of file winobj.c.

Referenced by IntWinObjHandleRootDirTagInMemory().

◆ WIN_POOL_TAG_DIRECTORY_7

#define WIN_POOL_TAG_DIRECTORY_7   0xe5726944

Allocation tag for the _OBJECT_DIRECTORY Windows 7 kernel structure.

Almost the same as WIN_POOL_TAG_DIRECTORY

Definition at line 191 of file winobj.c.

Referenced by IntWinObjHandleRootDirTagInMemory().

◆ WIN_POOL_TAG_OBJECT

#define WIN_POOL_TAG_OBJECT   0x546a624f

Allocation tag for the _OBJECT_TYPE Windows kernel structure.

Stands for 'ObjT'

Definition at line 196 of file winobj.c.

Referenced by IntWinObjIsTypeObject().

◆ WIN_POOL_TAG_OBJECT_7

#define WIN_POOL_TAG_OBJECT_7   0xd46a624f

Allocation tag for the _OBJECT_TYPE Windows 7 kernel structure.

Almost the same as WIN_POOL_TAG_OBJECT

Definition at line 201 of file winobj.c.

Referenced by IntWinObjIsTypeObject().

Typedef Documentation

◆ PROOT_HINT

typedef struct _ROOT_HINT * PROOT_HINT

◆ PROOT_SEARCH_CTX

◆ PWINOBJ_SWAPCTX

typedef struct _WINOBJ_SWAPCTX * PWINOBJ_SWAPCTX

◆ ROOT_HINT

typedef struct _ROOT_HINT ROOT_HINT

Hint structure used to search for possible object namespace root directory entries.

◆ ROOT_SEARCH_CTX

A context structure used to pass information between the various callbacks that search for a Root Directory.

◆ WINOBJ_SWAPCTX

A context structure used to pass information between the various callbacks that search for an object.

Enumeration Type Documentation

◆ IM_FLG

enum IM_FLG

Info Mask flags from the Object Header.

If one of these bits is set, the corresponding header is present.

Enumerator
IM_FLG_CREATOR_INFO 

Set if _OBJECT_HEADER_CREATOR_INFO is present.

IM_FLG_NAME_INFO 

Set if _OBJECT_HEADER_NAME_INFO is present.

IM_FLG_HANDLE_INFO 

Set if _OBJECT_HEADER_HANDLE_INFO is present.

IM_FLG_QUOTA_INFO 

Set if _OBJECT_HEADER_QUOTA_INFO is present.

IM_FLG_PROCESS_INFO 

Set if _OBJECT_HEADER_PROCESS_INFO is present.

Definition at line 208 of file winobj.c.

Function Documentation

◆ IntWinGuestFindDriversNamespace()

INTSTATUS IntWinGuestFindDriversNamespace ( void  )

Runs the driver object namespace search.

This starts the search for kernel driver objects, but when this function returns not all objects are found yet, as a big part of the search can (and will, in most situations) be executed asynchronously. The search starts by looking for the root of the object namespace. This is an _OBJECT_DIRECTORY structure used by the kernel with the name "\".

There is not a clear and easy way of figuring out where this exists inside the kernel, especially if Introcore is started long before the guest has booted. In order to obtain it, we look for a series of pointers inside the kernel .data section, as the pointer to this object will be saved in a global inside the kernel. This is based on some weak heuristics and invariants check which will produce a series of candidate pointers. Then, more serious checks are done on those candidates. We do this in order to reduce the search area, as most of the checks need to read from paged memory, and are done by a series of IntSwapMemRead calls that will page-in the needed memory pages, if they are not present. Doing this for all the pointers in the .data section may impact performance and will introduce a large window in which no driver objects are protected. If no candidates are found, the search is tried again, this time ignoring the gGuest.KernelBuffer contents, using the IntWinGuestFindDriversNamespaceNoBuffer function. If the search for candidates fails again, an error of type intErrGuestStructureNotFound is reported and the search stops.

The search starts by reading the allocation tag for each of the candidates and checking it against known-good values. This phase is done by IntWinObjHandleRootDirTagInMemory. Once a valid candidate is found, all the others are ignored and their subsequent IntWinObjHandleRootDirTagInMemory invocations are canceled. If no good allocation tag is found, an error of type intErrGuestStructureNotFound is reported and the search stops.

Next, every entry in the root's HashBuckets fields is parsed. This is an array of OBJECT_DIR_ENTRY_COUNT pointers to _OBJECT_DIRECTORY_ENTRY structures. Some may be NULL and are ignored. These may reside in paged pool, so they might be swapped out, so another round of swap-ins is scheduled using IntWinObjHandleDirectoryEntryInMemory as a handler.

There, we want to obtain the name of the object, but since the the name may also be swapped out, we simply register a new swap-in request, with IntWinObjHandleObjectInMemory as the handler. Also, since every _OBJECT_DIRECTORY_ENTRY may point to another _OBJECT_DIRECTORY_ENTRY, another swap-in is scheduled for the ChainLink member of the _OBJECT_DIRECTORY_ENTRY structure, having the same target, IntWinObjHandleDirectoryEntryInMemory.

Once the name information is obtained, another IntSwapMemRead is issued for the buffer itself, using IntWinObjParseDriverDirectory as the callback.

Once that callback gets invoked, the name is checked against the two directories that contain driver objects: "Driver" and "FileSystem". If the name matches, we iterate the HashBuckets entries of this directory and issue another IntSwapMemRead, this time for the driver object, setting the handler to IntWinObjHandleDriverDirectoryEntryInMemory.

Inside IntWinObjHandleDriverDirectoryEntryInMemory, a _OBJECT_DIRECTORY_ENTRY structure is obtained. The Object field of this structure can point to a _DRIVER_OBJECT or a _DEVICE_OBJECT structure. The object is validated to be a valid driver object using IntWinDrvObjIsValidDriverObject, and if it is it is added to introcore list of driver objects; otherwise, it is ignored. Similar to the other functions that parse a _OBJECT_DIRECTORY_ENTRY. this function sets up another swap-in, for the next entry in the object list, setting itself as the handler for that read.

At any point, any of the IntSwapMemRead callbacks may be the last callback that is invoked in a synchronous manner, so all of the callbacks check for this. If it happens, they finalize the search and check for errors. If errors were encountered, they are reported to the integrator and the introspection engine will be unloaded.

Note, however, that there is no way of ensuring that all driver objects allocated before introcore started are successfully detected. Doing this requires access to the list of all the currently used driver objects, and this is exactly what we are trying to obtain here.

Return values
INT_STATUS_SUCCESSif successful.
INT_STATUS_NOT_FOUNDif an error was encountered early in the search process.
Remarks
Even if this function exits with success, errors may be encountered while the search is conducted, in which case those will be reported with the GLUE_IFACE.NotifyIntrospectionErrorState mechanism and introcore will be unloaded.

Definition at line 1855 of file winobj.c.

Referenced by IntWinProcCreateProcessObject().

◆ IntWinGuestFindDriversNamespaceNoBuffer()

static INTSTATUS IntWinGuestFindDriversNamespaceNoBuffer ( void  )
static

Runs the driver object namespace search ignoring the gGuest.KernelBuffer and reading the data directly from the guest memory.

In certain initialization scenarios the buffer may not contain all the information needed for a successful search (for example, on a resume from hibernate, the buffer may contain old information, because we can read certain parts of the kernel before Windows updates them). These situations are rare, but if a search fails it can be retried in this way. This function behaves in a similar way to IntWinGuestFindDriversNamespace.

Returns
INT_STATUS_SUCCESS in case of success, or an error INTSTATUS value.

Definition at line 1727 of file winobj.c.

Referenced by IntWinGuestFindDriversNamespace().

◆ IntWinObjCancelRootTransactions()

static void IntWinObjCancelRootTransactions ( void  )
static

Cancels any pending swap memory reads left for the root directory.

Definition at line 388 of file winobj.c.

Referenced by IntWinObjCleanup(), and IntWinObjHandleRootDirTagInMemory().

◆ IntWinObjCheckDrvDirSearchState()

static void IntWinObjCheckDrvDirSearchState ( void  )
static

Checks if the search is still going, or if it finished with success or with an error.

If a stop has not been requested, it looks at the number of directory entries left to check for the root directory. If there are no more entries left to check, and if there are no other callbacks waiting to be invoked then the search is over. If we reached that point without finding both the "Driver" and the "FileSystem" namespace entries, then the search failed, and we set an appropriate error state and stop Introcore. If both entries were found, there may still be driver objects pending to be swapped in (gPendingDrivers > 0). If that's the case, we do nothing, and wait some more. If there are no more pending driver objects we can finalize the search.

Definition at line 322 of file winobj.c.

Referenced by IntWinGuestFindDriversNamespace(), IntWinObjHandleDirectoryEntryInMemory(), IntWinObjHandleDriverDirectoryEntryInMemory(), IntWinObjHandleObjectInMemory(), IntWinObjHandleRootDirTagInMemory(), and IntWinObjParseDriverDirectory().

◆ IntWinObjCleanup()

void IntWinObjCleanup ( void  )

Cleans up any resources allocated by the object search.

This will cancel any pending swap memory transactions and will free any memory allocated for the contexts used by those reads.

Definition at line 2080 of file winobj.c.

Referenced by IntGuestPrepareUninit(), and IntWinObjCheckDrvDirSearchState().

◆ IntWinObjFindRootDirectory()

INTSTATUS IntWinObjFindRootDirectory ( PROOT_HINT  Hint,
QWORD PossibleRoot 
)

Returns a possible object namespace root directory.

This is based on some heuristic checks, the address returned by this function needs extended validation, but that involves memory that may be paged out, so it is not done by this function. Instead, the caller needs to do those checks on the possible values obtained.

Parameters
[in]HintSearch area from which to extract a possible root pointer.
[out]PossibleRootThe address of a possible root pointer.
Return values
INT_SUCCESSin case of success.
INT_STATUS_NOT_NEEDED_HINTif a root pointer was already found or if the supplied hint those not point to a valid object.
INT_STATUS_INVALID_PARAMETER_1if no hint is provided.
INT_STATUS_INVALID_PARAMETER_2if no valid output buffer is provided.

Definition at line 1397 of file winobj.c.

Referenced by IntWinGuestFindDriversNamespace(), and IntWinGuestFindDriversNamespaceNoBuffer().

◆ IntWinObjGetObjectNameInfo()

static INTSTATUS IntWinObjGetObjectNameInfo ( QWORD  ObjectGva,
QWORD BufferGva,
WORD Length,
QWORD ParentDirGva 
)
static

Returns the name information for kernel objects that have one.

This information is available for objects that have the _OBJECT_HEADER_NAME_INFO header This function assumes that it can read the object header(s).

Parameters
[in]ObjectGvaThe address at which the object is found inside the guest kernel.
[out]BufferGvaThe address of the object name inside the guest kernel.
[out]LengthThe length of the object name, in characters. May be NULL.
[out]ParentDirGvaThe address of the object's parent directory inside the guest kernel. May be NULL.
Return values
INT_STATUS_SUCCESSin case of success.
INT_STATUS_NOT_FOUNDif the object does not have the header present.

Definition at line 417 of file winobj.c.

Referenced by IntWinObjHandleObjectInMemory(), and IntWinObjHandleRootDirTagInMemory().

◆ IntWinObjGetPoolHeaderForObject()

INTSTATUS IntWinObjGetPoolHeaderForObject ( QWORD  ObjectGva,
POOL_HEADER PoolHeader 
)

Reads the _POOL_HEADER structure for a given kernel object.

This function assumes that the object comes from the non paged pool, or it is readable at the moment.

Parameters
[in]ObjectGvaThe guest virtual address at which the object is located.
[out]PoolHeaderOn success, will contain the contents of the _POOL_HEADER. The caller must allocate a large enough buffer for this information.
Return values
INT_STATUS_SUCCESSin case of success.
INT_STATUS_INVALID_PARAMETER_1if ObjectGva is not a valid kernel address.
INT_STATUS_INVALID_PARAMETER_2if PoolHeader is NULL
INT_STATUS_NOT_FOUNDif the address at which the pool header should be located is not a kernel address.

Definition at line 1648 of file winobj.c.

Referenced by IntWinObjIsTypeObject().

◆ IntWinObjHandleDirectoryEntryInMemory()

static INTSTATUS IntWinObjHandleDirectoryEntryInMemory ( WINOBJ_SWAPCTX Context,
QWORD  Cr3,
QWORD  Gva,
QWORD  Gpa,
void *  Data,
DWORD  DataSize,
DWORD  Flags 
)
static

This callback is invoked for each object contained in the root namespace.

This callback will be invoked for every object directory inside the root namespace, the read is initiated by IntWinObjHandleRootDirTagInMemory. It will set up a IntSwapMemRead call that will allow us to read the address of the name buffer inside the _OBJECT_HEADER_NAME_INFO header of this object, with IntWinObjHandleObjectInMemory as the target. It will also set up another IntSwapMemRead call with itself as the target, in order to parse the next entry in this chain of entries. Context will be removed from the gSwapHandles list and any resources held by it will be freed. If this is the last entry in the current bucket (or if an error prevents us from reading the next entry), the gDirEntriesToCheck will be decremented. In case of error we don't stop the search right away because we may not actually need the next entry and it is easier to let the search finish on its own than stopping it here. If the search ends without all the expected results, the error will be reported.

Parameters
[in]ContextThe search context, as passed to IntSwapMemRead.
[in]Cr3Ignored.
[in]GvaIgnored. This information is already present in Context.
[in]GpaIgnored
[in]DataThe data read from the guest. This will be a _OBJECT_DIRECTORY_ENTRY kernel structure.
[in]DataSizeIgnored.
[in]FlagsIgnored.

Definition at line 1015 of file winobj.c.

Referenced by IntWinObjHandleRootDirTagInMemory().

◆ IntWinObjHandleDriverDirectoryEntryInMemory()

static INTSTATUS IntWinObjHandleDriverDirectoryEntryInMemory ( WINOBJ_SWAPCTX Context,
QWORD  Cr3,
QWORD  Gva,
QWORD  Gpa,
void *  Data,
DWORD  DataSize,
DWORD  Flags 
)
static

This callback is invoked for namespace directory entries that may represent driver objects.

Since the Driver and FileSystem directories may contain both driver and device objects, this function still needs to validate the entries before treating them as driver objects. If an object is indeed a driver object, it will be saved to the Introcore list of driver objects and the gFoundDrivers counter will be incremented; if not, it will be ignored. Once this is done, the Context is removed from the gSwapHandles list and any resources held up by it are freed. This is a IntSwapMemRead callback for reads initiated by IntWinObjParseDriverDirectory and IntWinObjHandleDriverDirectoryEntryInMemory itself. This recursion is needed as each directory entry is in fact a linked list, and after parsing the first entry we need to move to the next and so on and this function has the best context for reading the next entry. For cases in which the page is not present and the read will be done asynchronously, this function also increments the gPendingDrivers counter, to account for the fact that a swap-in is still pending.

Parameters
[in]ContextThe search context, as passed to IntSwapMemRead.
[in]Cr3Ignored.
[in]GvaIgnored. This information is already present in Context.
[in]GpaIgnored
[in]DataThe data taken from the guest. This will be a _OBJECT_DIRECTORY_ENTRY kernel structure. This pointer is only valid until this function returns.
[in]DataSizeIgnored. This information is already present in Context.
[in]FlagsA combination of flags describing the way in which the data was read. This function checks only for the SWAPMEM_FLAG_ASYNC_CALL flag. If it is present, it means that it was invoked asynchronously, in which case it may be the last callback in the chain of callbacks used in the search, so it must also finalize the search. This check is done even if this function sets up another swap memory read, as that one may be done for a page that is already present, in which case it will be invoked synchronously.

Definition at line 529 of file winobj.c.

Referenced by IntWinObjParseDriverDirectory().

◆ IntWinObjHandleObjectInMemory()

static INTSTATUS IntWinObjHandleObjectInMemory ( WINOBJ_SWAPCTX Context,
QWORD  Cr3,
QWORD  Gva,
QWORD  Gpa,
void *  Data,
DWORD  DataSize,
DWORD  Flags 
)
static

This callback is invoked for each object in an object directory entries list.

This callback will be invoked for every object inside an object directory list of _OBJECT_DIRECTORY_ENTRY structures. The read is initiated by IntWinObjHandleDirectoryEntryInMemory. It simply obtains the name information for that object and sets up another IntSwapMemRead call for the buffer that represents the object name, with IntWinObjParseDriverDirectory as the callback. It also removed Context from the gSwapHandles list and frees any resources held by it.

Parameters
[in]ContextThe search context, as passed to IntSwapMemRead.
[in]Cr3Ignored.
[in]GvaIgnored. This information is already present in Context.
[in]GpaIgnored.
[in]DataIgnored. We only want to bring the page in memory.
[in]DataSizeIgnored.
[in]FlagsA combination of flags describing the way in which the data was read. This function checks only for the SWAPMEM_FLAG_ASYNC_CALL flag. If it is present, it means that it was invoked asynchronously, in which case it may be the last callback in the chain of callbacks used in the search, so it must also finalize the search. Even if this function sets up another IntSwapMemRead call, we can not let only that callback do this check, as it is indeed possible for this to be the last swap memory callback invoked if the page we are trying to read is already present.

Definition at line 903 of file winobj.c.

Referenced by IntWinObjHandleDirectoryEntryInMemory().

◆ IntWinObjHandleRootDirTagInMemory()

static INTSTATUS IntWinObjHandleRootDirTagInMemory ( ROOT_SEARCH_CTX Context,
QWORD  Cr3,
QWORD  Gva,
QWORD  Gpa,
void *  Data,
DWORD  DataSize,
DWORD  Flags 
)
static

This callback is invoked for every candidate root directory namespace object.

This is a IntSwapMemRead callback for a read initiated by IntWinGuestFindDriversNamespace. It checks if the candidate is indeed the root of the object namespace. It if is, it cancels all the other root directory checks that are pending and starts the next phase of the search, looking for specific top-level directories. If this is the last entry in the current bucket (or if an error prevents us from reading the next entry), the gDirEntriesToCheck will be decremented. In case of error we don't stop the search right away because we may not actually need the next entry and it is easier to let the search finish on its own than stopping it here. If the search ends without all the expected results, the error will be reported.

Parameters
[in]ContextThe search context, as passed to IntSwapMemRead.
[in]Cr3Ignored.
[in]GvaThe guest virtual address at which the Data was read from.
[in]GpaIgnored.
[in]DataThe data read from the guest. This will be a DWORD with the allocation tag. This pointer is valid until this function returns.
[in]DataSizeIgnored.
[in]FlagsIgnored.

Definition at line 1173 of file winobj.c.

Referenced by IntWinGuestFindDriversNamespace().

◆ IntWinObjIsRootSearchOver()

static BOOLEAN IntWinObjIsRootSearchOver ( void  )
static

Definition at line 372 of file winobj.c.

Referenced by IntWinGuestFindDriversNamespace(), and IntWinObjHandleRootDirTagInMemory().

◆ IntWinObjIsTypeObject()

INTSTATUS IntWinObjIsTypeObject ( QWORD  Gva)

Checks if the supplied guest memory location holds a valid type object.

Gva must point to a valid _OBJECT_TYPE structure. In order to ensure this the following invariants are checked:

  • the DefaultObject field must be a valid kernel pointer
  • the Name.Buffer field must be a valid kernel pointer
  • the TypeList.Blink field must be a valid kernel pointer
  • the TypeList.Flink must be a valid kernel pointer
  • the pool tag of the allocation must be WIN_POOL_TAG_OBJECT_7 for Windows 7 and WIN_POOL_TAG_OBJECT for newer version
  • the allocation must be from the non paged pool with the type NonPagedPoolMustSucceed
  • the PoolType field of the _POOL_HEADER can not be DontUseThisType
  • the PoolType field of the _POOL_HEADER can not be DontUseThisTypeSession

Since the allocation must be done from the non paged pool, we must be able to read the object and its headers at any time, so if any of the IntKernVirtMemRead calls fails, this can not be a valid type object.

Parameters
[in]GvaGuest virtual address to check.
Return values
INT_STATUS_SUCCESSif this is a valid kernel object.
INT_STATUS_NOT_FOUNDif this is not a valid kernel object.

Definition at line 1503 of file winobj.c.

Referenced by IntWinObjFindRootDirectory().

◆ IntWinObjParseDriverDirectory()

static INTSTATUS IntWinObjParseDriverDirectory ( WINOBJ_SWAPCTX Context,
QWORD  Cr3,
QWORD  Gva,
QWORD  Gpa,
void *  Data,
DWORD  DataSize,
DWORD  Flags 
)
static

This callback is invoked for namespace entries that may represent driver directories.

This callback will be invoked for every top level directory in the root namespace. The read is initiated by IntWinObjHandleObjectInMemory. The first thing it does is to check if the directory it was invoked for is one that contains driver objects. There are two such directories: "Driver" and "FileSystem". This check is done by name. If this is not one of the directories we are looking for, it is ignored. Once the check is done, Context is removed from the gSwapHandles list and any resources held by it are freed. If this is one of the directories we are looking for, we begin to parse it. Since the name information and the object itself are part of the same allocation, we can safely access the entire object in this callback, so we can read the HashBuckets array of the _OBJECT_DIRECTORY structure and begin parsing each entry one by one. The entries in the array may not be present in memory, so one IntSwapMemRead call is used for both of them, passing IntWinObjHandleDriverDirectoryEntryInMemory as the callback. For cases in which the page is not present and the read will be done asynchronously, this function also increments the gPendingDrivers counter, to account for the fact that a swap-in is still pending. Note that not all entries are valid, some may be NULL.

Parameters
[in]ContextThe search context, as passed to IntSwapMemRead.
[in]Cr3Ignored.
[in]GvaIgnored. This information is already present in Context.
[in]GpaIgnored.
[in]DataThe data taken from the guest. This will be a _OBJECT_DIRECTORY kernel structure. This pointer is only valid until this function returns.
[in]DataSizeIgnored. This information is already present in Context.
[in]FlagsA combination of flags describing the way in which the data was read. This function checks only for the SWAPMEM_FLAG_ASYNC_CALL flag. If it is present, it means that it was invoked asynchronously, in which case it may be the last callback in the chain of callbacks used in the search, so it must also finalize the search. Even if this function sets up another IntSwapMemRead call, we can not let only that callback do this check, as it is indeed possible for this to be the last swap memory callback invoked if all the directory entries for which we set up a swap memory read are already present in memory, so they are invoked synchronously.

Definition at line 716 of file winobj.c.

Referenced by IntWinObjHandleObjectInMemory().

◆ IntWinObjReinitGlobalState()

static void IntWinObjReinitGlobalState ( void  )
static

Resets the global search state.

Definition at line 1711 of file winobj.c.

Referenced by IntWinGuestFindDriversNamespace(), and IntWinGuestFindDriversNamespaceNoBuffer().

Variable Documentation

◆ gDirEntriesToCheck

DWORD gDirEntriesToCheck = OBJECT_DIR_ENTRY_COUNT
static

The number of directory entries left to check.

Definition at line 299 of file winobj.c.

Referenced by IntWinObjCheckDrvDirSearchState(), IntWinObjHandleDirectoryEntryInMemory(), IntWinObjHandleRootDirTagInMemory(), and IntWinObjReinitGlobalState().

◆ gFoundDrivers

DWORD gFoundDrivers = 0
static

The number of found driver objects.

Definition at line 308 of file winobj.c.

Referenced by IntWinGuestFindDriversNamespace(), IntWinObjCleanup(), and IntWinObjHandleDriverDirectoryEntryInMemory().

◆ gPendingDrivers

DWORD gPendingDrivers = 0
static

The count of pending driver objects to be checked.

As long as this is above 0, there are still IntWinObjHandleDriverDirectoryEntryInMemory callbacks waiting to be invoked by the swap memory read mechanism.

Definition at line 305 of file winobj.c.

Referenced by IntWinGuestFindDriversNamespace(), IntWinObjCheckDrvDirSearchState(), IntWinObjCleanup(), IntWinObjHandleDriverDirectoryEntryInMemory(), and IntWinObjParseDriverDirectory().

◆ gPossibleRootGvas

ROOT_SEARCH_CTX gPossibleRootGvas[32] = {0}
static

The possible addresses at which the root directory may be located.

At the start of the search, this list is populated with possible addresses inside the guest kernel at which the root directory of the namespace is possible to be located. Every address in this list is then checked against a series of invariants in order to find the actual entry.

Definition at line 293 of file winobj.c.

◆ gRootCount

DWORD gRootCount = 0
static

◆ gStop

BOOLEAN gStop = FALSE
static

Set to True when the search must be aborted.

This allows introcore to stop the search if an error forces it to stop.

Definition at line 313 of file winobj.c.

Referenced by IntWinObjCheckDrvDirSearchState(), and IntWinObjReinitGlobalState().

◆ gSwapHandles

LIST_ENTRY gSwapHandles
static

List of all the swap handles used by the namespace parser.

The swap handled used by gPossibleRootGvas are not listed here.

Definition at line 318 of file winobj.c.