Bitdefender Hypervisor Memory Introspection
winpool.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 Bitdefender
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 #include "winpool.h"
6 #include "introcore.h"
7 #include "windrvobj.h"
8 #include "detours.h"
9 #include "guests.h"
10 
11 
14  _In_ void *Detour
15  )
31 {
32  INTSTATUS status;
33  DWORD tag;
34  DWORD size;
35  // DWORD poolType;
36  QWORD args[3];
37 
38  if (NULL == Detour)
39  {
41  }
42 
43  status = IntDetGetArguments(Detour, 3, args);
44  if (!INT_SUCCESS(status))
45  {
46  ERROR("[ERROR] IntDetGetArguments failed: 0x%08x\n", status);
47  goto cleanup_and_exit;
48  }
49 
50  // poolType = (DWORD)args[0];
51  size = (DWORD)args[1];
52  tag = (DWORD)args[2];
53 
54  // The allocator will always assign a BlockSize to the given pool header, representing the sum of the
55  // allocation size and the size of POOL_HEADER, divided by 0x10 on x64 guests, respectively by 0x8 on x86
56  // guests. Note that, if we force an allocation of size 0x1000 - sizeof(POOL_HEADER), which means that
57  // both the allocation and the pool header should reside in the same page, the allocator will notice
58  // that this is >= 0x1000, thus overflowing the BlockSize (8 bits on x64, 9 bits on x86) when divided,
59  // thus making the current allocation a big pool allocation. As we don't desire this, we will, instead
60  // make a subtraction of 2 * sizeof(POOL_HEADER). This will ensure both that the given allocation will
61  // not be a big pool allocation, and that there won't be any other allocations in the given page, as
62  // there will be space on the current page for just a POOL_HEADER, hence an allocation with size 0.
63  // Notice that this will always result in an allocation with BlockSize = 0xFF on x64 guests and
64  // BlockSize = 0x1FF on x86 guests respectively.
65 
66  // The size is 472 bytes on Windows 7 x64, 488 bytes on Windows 8 x64 & 236 bytes on Windows 7 x32.
67  if (((tag == WIN_POOL_TAG_DRIV) || (tag == WIN_POOL_TAG_DRIV2)))
68  {
69  // This is a _DRIVER_OBJECT
70  if (size < PAGE_SIZE - 2 * WIN_POOL_HEADER_SIZE)
71  {
72  size = PAGE_SIZE - 2 * WIN_POOL_HEADER_SIZE;
73  }
74  }
75  else if (tag == WIN_POOL_TAG_FMFI)
76  {
77  // This is a _FAST_IO_DISPATCH
78  if (size < PAGE_SIZE - 2 * WIN_POOL_HEADER_SIZE)
79  {
80  size = PAGE_SIZE - 2 * WIN_POOL_HEADER_SIZE;
81  }
82  }
83  else if (tag == WIN_POOL_TAG_TOKE || tag == WIN_POOL_TAG_TOKE2)
84  {
85  // This is a _TOKEN
86  if (size < PAGE_SIZE - 2 * WIN_POOL_HEADER_SIZE)
87  {
88  size = PAGE_SIZE - 2 * WIN_POOL_HEADER_SIZE;
89  }
90  }
91 
92  // Patch back the size
93  status = IntDetPatchArgument(Detour, 1, size);
94  if (!INT_SUCCESS(status))
95  {
96  ERROR("[ERROR] IntDetPatchArgument failed: 0x%08x\n", status);
97  goto cleanup_and_exit;
98  }
99 
100 cleanup_and_exit:
101  return status;
102 }
103 
104 
105 INTSTATUS
107  _In_ void *Detour
108  )
121 {
122  INTSTATUS status;
123  DWORD tag;
124  QWORD address;
125  QWORD args[2];
126 
127  if (NULL == Detour)
128  {
130  }
131 
132  status = IntDetGetArguments(Detour, 2, args);
133  if (!INT_SUCCESS(status))
134  {
135  ERROR("[ERROR] IntDetGetArguments failed: 0x%08x\n", status);
136  goto cleanup_and_exit;
137  }
138 
139  tag = (DWORD)args[0];
140  address = args[1];
141 
142  // Handle the free, if it's a tag that interests us.
143  if ((tag == 0xF6697244) || (tag == 0x76697244) || (tag == 0x69664d46)) // Driver object or fast I/O dispatch
144  {
145  status = IntWinDrvObjRemoveFromAddress(address);
146  if (!INT_SUCCESS(status) && (INT_STATUS_NOT_FOUND != status))
147  {
148  ERROR("[ERROR] IntWinDrvObjRemove failed: 0x%08x\n", status);
149  }
150  }
151 
152 cleanup_and_exit:
153  return status;
154 }
155 
156 //
157 // IntWinPoolGetPoolHeaderInPage
158 //
159 const POOL_HEADER*
161  _In_ const void* Page,
162  _In_ DWORD StartOffset,
163  _In_ DWORD Tag
164  )
178 {
179  const POOL_HEADER *phs;
180  int i;
181 
182  if (NULL == Page)
183  {
184  return NULL;
185  }
186 
187  if ((QWORD)StartOffset + sizeof(POOL_HEADER) > PAGE_SIZE)
188  {
189  return NULL;
190  }
191 
192  i = StartOffset;
193 
194  while (i >= 0)
195  {
196  phs = (const POOL_HEADER*)((size_t)Page + i);
197 
198  if (Tag == (gGuest.Guest64 ? phs->Header64.PoolTag : phs->Header32.PoolTag))
199  {
200  return phs;
201  }
202 
204  }
205 
206  return NULL;
207 }
#define WIN_POOL_TAG_TOKE2
Definition: winpool.h:18
DWORD PoolTag
Definition: wddefs.h:420
#define _In_
Definition: intro_sal.h:21
#define WIN_POOL_TAG_FMFI
Definition: winpool.h:16
POOL_HEADER32 Header32
Definition: wddefs.h:462
#define INT_SUCCESS(Status)
Definition: introstatus.h:42
#define ERROR(fmt,...)
Definition: glue.h:62
int INTSTATUS
The status data type.
Definition: introstatus.h:24
#define INT_STATUS_NOT_FOUND
Definition: introstatus.h:284
const POOL_HEADER * IntWinPoolGetPoolHeaderInPage(const void *Page, DWORD StartOffset, DWORD Tag)
Search for a pool header with given tag in a buffer.
Definition: winpool.c:160
#define WIN_POOL_HEADER_SIZE
Definition: wddefs.h:469
The guest detour API.
INTSTATUS IntWinPoolHandleAlloc(void *Detour)
Detour callback for ExAllocatePoolWithTag.Handles allocations within a Windows guest, executed using the ExAllocatePoolWithTag API. Basically, it will check the tag of the allocation, and if it identifies an allocation for a driver object or a fast I/O dispatch, it will patch the Size argument of the call so that it&#39;s almost a page. This ensures us that critical structures protected by the introspection will be allocated alone in each page, which gives us an enormous performance boost.
Definition: winpool.c:13
#define WIN_POOL_TAG_TOKE
Definition: winpool.h:17
POOL_HEADER64 Header64
Definition: wddefs.h:463
BOOLEAN Guest64
True if this is a 64-bit guest, False if it is a 32-bit guest.
Definition: guests.h:290
unsigned long long QWORD
Definition: intro_types.h:53
#define WIN_POOL_TAG_DRIV
Definition: winpool.h:14
#define WIN_POOL_TAG_DRIV2
Definition: winpool.h:15
INTSTATUS IntWinPoolHandleFree(void *Detour)
Detour callback for ExFreePoolWithTag.This function handles de-allocation requests executed by the gu...
Definition: winpool.c:106
DWORD PoolTag
Definition: wddefs.h:447
#define PAGE_SIZE
Definition: common.h:70
INTSTATUS IntDetPatchArgument(void const *Detour, DWORD Index, QWORD Value)
Modifies the value of a detour argument.
Definition: detours.c:2410
uint32_t DWORD
Definition: intro_types.h:49
INTSTATUS IntDetGetArguments(void const *Detour, DWORD Argc, QWORD *Argv)
Reads multiple arguments from a detour.
Definition: detours.c:2296
GUEST_STATE gGuest
The current guest state.
Definition: guests.c:50
#define INT_STATUS_INVALID_PARAMETER_1
Definition: introstatus.h:62
VE_CACHE_LINE * Page
Mapped page inside Introspection virtual address space.
Definition: vecore.c:120
INTSTATUS IntWinDrvObjRemoveFromAddress(QWORD DriverObjectAddress)
Frees and removes protection for a driver object by its address.
Definition: windrvobj.c:1246