Bitdefender Hypervisor Memory Introspection
hook_object.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 Bitdefender
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 #include "hook.h"
6 #include "hook_object.h"
7 
8 
9 static INTSTATUS
12  _In_ DWORD Flags
13  )
29 {
31 
32  if (Region->Header.Flags & (HOOK_FLG_REMOVE))
33  {
35  }
36 
37  for (DWORD i = 0; i < Region->HooksCount; i++)
38  {
39  if (NULL != Region->Hooks[i])
40  {
41  INTSTATUS status;
42 
43  status = IntHookGvaRemoveHook((HOOK_GVA **)&Region->Hooks[i], HOOK_FLG_CHAIN_DELETE);
44  if (!INT_SUCCESS(status))
45  {
46  ERROR("[ERROR] IntHookGvaRemoveHook failed: 0x%08x\n", status);
47  }
48  }
49  }
50 
51  Region->Header.Flags |= (HOOK_FLG_DISABLED | HOOK_FLG_REMOVE);
52 
53  RemoveEntryList(&Region->Link);
54 
55  // IMPORTANT: Write hooks must be inserted at the end of the remove list. The reason is that the EPT does not allow
56  // certain combinations of RWX flags; for example, one cannot have a W page which is not R. Therefore, if we have
57  // two regions - one which contains R hooks and one which contains W hooks, we must make sure we remove the W hook
58  // last, in order to have the R flag restored by the time we also restore the W flag - otherwise, we'll restore
59  // the W flag first, which is invalid (as the entry is not yet R, because we didn't remove the R hook), and we will
60  // either get an error from the HV (best case scenario) or trigger EPT misconfiguration (worst case scenario).
61  if (Region->Header.EptHookType == IG_EPT_HOOK_WRITE)
62  {
63  InsertTailList(&Region->Object->RemovedRegions, &Region->Link);
64  }
65  else
66  {
67  InsertHeadList(&Region->Object->RemovedRegions, &Region->Link);
68  }
69 
70  Region->Object->RegionsRemoved = TRUE;
71 
73 
74  gHooks->Dirty = TRUE;
75 
76  return INT_STATUS_SUCCESS;
77 }
78 
79 
82  _In_ DWORD ObjectType,
83  _In_ QWORD Cr3,
84  _Out_ void **Object
85  )
101 {
103 
104  if (NULL == Object)
105  {
107  }
108 
109  pObj = HpAllocWithTag(sizeof(*pObj), IC_TAG_HKOBJ);
110  if (NULL == pObj)
111  {
113  }
114 
115  pObj->ObjectType = ObjectType;
116  pObj->Cr3 = Cr3;
117  pObj->RegionsRemoved = FALSE;
118 
119  InitializeListHead(&pObj->Regions);
120 
122 
124 
125  *Object = pObj;
126 
127  return INT_STATUS_SUCCESS;
128 }
129 
130 
131 INTSTATUS
133  _In_ void *Object,
134  _In_ QWORD Cr3,
135  _In_ QWORD Gla,
136  _In_ SIZE_T Length,
137  _In_ BYTE Type,
138  _In_ void *Callback,
139  _In_opt_ void *Context,
140  _In_opt_ DWORD Flags,
142  )
166 {
167  INTSTATUS status;
170 
171  if (NULL == Object)
172  {
174  }
175 
176  if (NULL == Callback)
177  {
179  }
180 
181  if (Length > ONE_GIGABYTE)
182  {
183  ERROR("[ERROR] Trying to hook (%d) [%llx - %llx] with callback %p\n",
184  Type, Gla, Gla + Length, Callback);
185  }
186 
187  pObj = Object;
188 
189  Flags &= HOOK_FLG_GLOBAL_MASK;
190 
191  pReg = HpAllocWithTag(sizeof(*pReg), IC_TAG_REGD);
192  if (NULL == pReg)
193  {
195  }
196 
197  pReg->Header.Context = Context;
198  pReg->Header.Flags = Flags;
199  pReg->Header.ParentHook = NULL;
201  pReg->Header.EptHookType = Type;
202 
203  pReg->HookLength = Length;
204  pReg->HookStart = Gla;
205  pReg->Object = pObj;
206 
207  InsertTailList(&pObj->Regions, &pReg->Link);
208 
209  pReg->Hooks = HpAllocWithTag(sizeof(void *) * ((Length / PAGE_SIZE) + 2), IC_TAG_HKAR);
210  if (NULL == pReg->Hooks)
211  {
213  goto cleanup_and_exit;
214  }
215 
216  for (QWORD gva = Gla; gva < Gla + Length; )
217  {
218  QWORD left = Gla + Length - gva;
219  DWORD length = (DWORD)MIN(PAGE_SIZE, PAGE_REMAINING(gva));
220  length = (DWORD)MIN(left, length);
221 
222  status = IntHookGvaSetHook(Cr3,
223  gva,
224  length,
225  Type,
226  Callback,
227  Context,
228  pReg,
229  Flags,
230  (HOOK_GVA **)&pReg->Hooks[pReg->HooksCount]);
231  if (!INT_SUCCESS(status))
232  {
233  ERROR("[ERROR] IntHookGvaSetHook failed for GVA 0x%016llx: 0x%08x\n", gva, status);
234  goto cleanup_and_exit;
235  }
236 
237  pReg->HooksCount++;
238  gva += PAGE_REMAINING(gva);
239  }
240 
241  if (NULL != Region)
242  {
243  *Region = pReg;
244  }
245 
246  status = INT_STATUS_SUCCESS;
247 
248 cleanup_and_exit:
249  if (!INT_SUCCESS(status) && (NULL != pReg))
250  {
251  INTSTATUS status2 = IntHookObjectRemoveRegionInternal(pReg, 0);
252  if (!INT_SUCCESS(status2))
253  {
254  ERROR("[ERROR] IntHookObjectRemoveRegionInternal failed: 0x%08x\n", status2);
255  }
256  }
257 
258  gHooks->Dirty = TRUE;
259 
260  return status;
261 }
262 
263 
264 static INTSTATUS
267  _In_ DWORD Flags
268  )
280 {
281  INTSTATUS status = INT_STATUS_SUCCESS;
282 
283  UNREFERENCED_PARAMETER(Flags);
284 
285  for (DWORD i = 0; i < Region->HooksCount; i++)
286  {
287  if (NULL != Region->Hooks[i])
288  {
289  INTSTATUS failStatus = IntHookGvaDeleteHook((HOOK_GVA **)&Region->Hooks[i], 0);
290  if (!INT_SUCCESS(failStatus))
291  {
292  ERROR("[ERROR] IntHookGvaDeleteHook failed: 0x%08x\n", failStatus);
293  status = failStatus;
294  }
295  }
296  }
297 
298  RemoveEntryList(&Region->Link);
299 
300  HpFreeAndNullWithTag((void **)&Region->Hooks, IC_TAG_HKAR);
301 
303 
304  return status;
305 }
306 
307 
308 INTSTATUS
311  _In_ DWORD Flags
312  )
328 {
329  INTSTATUS status;
330 
331  if (NULL == Region)
332  {
334  }
335 
336  if (NULL == *Region)
337  {
339  }
340 
341  Flags &= HOOK_FLG_GLOBAL_MASK;
342 
343  status = IntHookObjectRemoveRegionInternal(*Region, Flags);
344  if (!INT_SUCCESS(status))
345  {
346  ERROR("[ERROR] IntHookObjectRemoveRegionInternal failed: 0x%08x\n", status);
347  }
348 
349  *Region = NULL;
350 
351  return status;
352 
353 }
354 
355 
356 INTSTATUS
359  _In_ DWORD Flags
360  )
374 {
375  INTSTATUS status;
376  LIST_ENTRY *list;
377 
378  if (NULL == Object)
379  {
381  }
382 
383  if (NULL == *Object)
384  {
386  }
387 
388  Flags &= HOOK_FLG_GLOBAL_MASK;
389 
390  if ((*Object)->Flags & HOOK_FLG_REMOVE)
391  {
392  return INT_STATUS_SUCCESS;
393  }
394 
395  list = (*Object)->Regions.Flink;
396  while (list != &(*Object)->Regions)
397  {
399 
400  list = list->Flink;
401 
402  status = IntHookObjectRemoveRegionInternal(pReg, Flags);
403  if (!INT_SUCCESS(status))
404  {
405  ERROR("[ERROR] IntHookObjectRemoveRegionInternal failed: 0x%08x\n", status);
406  }
407  }
408 
409  (*Object)->Flags |= (HOOK_FLG_DISABLED | HOOK_FLG_REMOVE);
410 
411  (*Object)->RegionsRemoved = TRUE;
412 
413  *Object = NULL;
414 
416 
417  gHooks->Dirty = TRUE;
418 
419  return INT_STATUS_SUCCESS;
420 }
421 
422 
423 void *
425  _In_ QWORD Gva,
426  _In_ void *HookObject,
427  _In_ BYTE HookType
428  )
438 {
439  LIST_ENTRY *list = NULL;
440  HOOK_OBJECT_DESCRIPTOR *pObject = NULL;
441 
442  if (HookObject == NULL)
443  {
444  return NULL;
445  }
446 
447  pObject = (HOOK_OBJECT_DESCRIPTOR *)(HookObject);
448 
449  list = pObject->Regions.Flink;
450  while (list != &pObject->Regions)
451  {
453 
454  list = list->Flink;
455  if (IN_RANGE_LEN(Gva, pRegion->HookStart, pRegion->HookLength) &&
456  ((pRegion->Header.EptHookType & HookType) == pRegion->Header.EptHookType))
457  {
458  return pRegion;
459  }
460  }
461 
462  return NULL;
463 }
464 
465 
466 static INTSTATUS
468  _In_ DWORD Flags
469  )
477 {
478  INTSTATUS status;
479  LIST_ENTRY *list;
480 
481  Flags &= HOOK_FLG_GLOBAL_MASK;
482 
483  list = gHooks->Objects.Objects.Flink;
484  while (list != &gHooks->Objects.Objects)
485  {
487 
488  list = list->Flink;
489 
490  if (0 == (pObj->Flags & HOOK_FLG_REMOVE))
491  {
492  LIST_ENTRY *list2 = pObj->Regions.Flink;
493 
494  LOG("[ERROR] There should be no hook objects remaining... Got one: (%llx, %d)!\n",
495  pObj->Cr3, pObj->ObjectType);
496 
497  while (list2 != &pObj->Regions)
498  {
500 
501  list2 = list2->Flink;
502 
503  status = IntHookObjectRemoveRegionInternal(pReg, Flags);
504  if (!INT_SUCCESS(status))
505  {
506  ERROR("[ERROR] IntHookObjectRemoveRegionInternal failed: 0x%08x\n", status);
507  }
508  }
509 
511 
512  pObj->RegionsRemoved = TRUE;
513  }
514  }
515 
517 
518  gHooks->Dirty = TRUE;
519 
520  return INT_STATUS_SUCCESS;
521 }
522 
523 
524 INTSTATUS
526  void
527  )
535 {
536  INTSTATUS status;
537  LIST_ENTRY *list;
538 
539  // Check if any objects have been actually removed.
541  {
542  return INT_STATUS_SUCCESS;
543  }
544 
545  list = gHooks->Objects.Objects.Flink;
546  while (list != &gHooks->Objects.Objects)
547  {
549 
550  list = list->Flink;
551 
552  if ((pObj->RegionsRemoved) || (0 != (pObj->Flags & (HOOK_FLG_REMOVE))))
553  {
554  LIST_ENTRY *list2 = pObj->RemovedRegions.Flink;
555  while (list2 != &pObj->RemovedRegions)
556  {
558 
559  list2 = list2->Flink;
560 
561  if (0 != (pReg->Header.Flags & (HOOK_FLG_REMOVE)))
562  {
564 
565  status = IntHookObjectDeleteRegion(pReg, 0);
566 
568 
569  if (!INT_SUCCESS(status))
570  {
571  ERROR("[ERROR] IntHookObjectDeleteRegion failed: 0x%08x\n", status);
572  }
573  }
574  }
575  }
576 
577  // Reset the regions-removal indicator.
578  pObj->RegionsRemoved = FALSE;
579 
580 
581  // Remove the master object structure now. There is no HookObjectDelete function, because we don't
582  // need one; this is the only place where objects can be deleted.
583  if (0 != (pObj->Flags & (HOOK_FLG_REMOVE)))
584  {
585  RemoveEntryList(&pObj->Link);
586 
588  }
589  }
590 
592 
593  return INT_STATUS_SUCCESS;
594 }
595 
596 
597 INTSTATUS
599  void
600  )
606 {
608 
609  return INT_STATUS_SUCCESS;
610 }
611 
612 
613 INTSTATUS
615  void
616  )
623 {
624  if (NULL == gHooks)
625  {
627  }
628 
630  if (!INT_SUCCESS(status))
631  {
632  ERROR("[ERROR] IntHookObjectDestroyAll failed: 0x%08x\n", status);
633  }
634 
635  return status;
636 }
LIST_HEAD RemovedRegions
All the removed regions are inserted here. The regions must be committed in the exact same order they...
Definition: hook_object.h:22
#define _In_opt_
Definition: intro_sal.h:16
INTSTATUS IntHookObjectCommit(void)
Commit removed hook objects and regions.
Definition: hook_object.c:525
#define _Out_
Definition: intro_sal.h:22
#define CONTAINING_RECORD(List, Type, Member)
Definition: introlists.h:36
BOOLEAN Dirty
Set whenever hooks are added or removed.
Definition: hook.h:97
INTSTATUS IntHookObjectHookRegion(void *Object, QWORD Cr3, QWORD Gla, SIZE_T Length, BYTE Type, void *Callback, void *Context, DWORD Flags, HOOK_REGION_DESCRIPTOR **Region)
Hook a contiguous region of virtual memory inside the provided virtual address space.
Definition: hook_object.c:132
void * Context
User-defined data that will be supplied to the callback.
Definition: hook.h:74
static void InsertHeadList(LIST_ENTRY *ListHead, LIST_ENTRY *Entry)
Definition: introlists.h:152
uint8_t BYTE
Definition: intro_types.h:47
void ** Hooks
Array of hooks. They will usually be HOOK_GVA objects.
Definition: hook_object.h:43
#define _In_
Definition: intro_sal.h:21
#define INT_STATUS_SUCCESS
Definition: introstatus.h:54
#define PAGE_REMAINING(addr)
Definition: pgtable.h:163
INTSTATUS IntHookGvaDeleteHook(HOOK_GVA **Hook, DWORD Flags)
Completely delete a GVA hook.
Definition: hook_gva.c:612
void * IntHookObjectFindRegion(QWORD Gva, void *HookObject, BYTE HookType)
Searches for a region of hooked memory inside the provided hook object.
Definition: hook_object.c:424
#define STATS_EXIT(id)
Definition: stats.h:160
struct _LIST_ENTRY * Flink
Definition: introlists.h:20
static INTSTATUS IntHookObjectDestroyAll(DWORD Flags)
Destroy all existing hook objects.
Definition: hook_object.c:467
#define INT_SUCCESS(Status)
Definition: introstatus.h:42
#define HOOK_FLG_DISABLED
If flag is set, the hook is disabled, therefore ignored on EPT violations.
Definition: hook.h:46
INTSTATUS IntHookObjectCreate(DWORD ObjectType, QWORD Cr3, void **Object)
Create a new hook object.
Definition: hook_object.c:81
#define IC_TAG_HKAR
Hooks array in object region descriptor.
Definition: memtags.h:38
BYTE EptHookType
The type of the hook in EPT (see IG_EPT_HOOK_TYPE)
Definition: hook.h:69
PHOOK_OBJECT_DESCRIPTOR Object
Parent object. Optional, but it is strongly recommended to link a region to an object.
Definition: hook_object.h:46
DWORD ObjectType
One of the INTRO_OBJECT_TYPE values.
Definition: hook_object.h:23
#define INT_STATUS_NOT_NEEDED_HINT
Definition: introstatus.h:317
#define ERROR(fmt,...)
Definition: glue.h:62
INTSTATUS IntHookObjectRemoveRegion(HOOK_REGION_DESCRIPTOR **Region, DWORD Flags)
Remove a hooked region of memory.
Definition: hook_object.c:309
An entire hook region, consisting of multiple GVA hooks.
Definition: hook.h:23
#define HpAllocWithTag(Len, Tag)
Definition: glue.h:516
int INTSTATUS
The status data type.
Definition: introstatus.h:24
HOOK_STATE * gHooks
Global hooks state.
Definition: hook.c:8
BOOLEAN ObjectsRemoved
True whenever an object has been removed.
Definition: hook_object.h:56
INTSTATUS IntHookObjectUninit(void)
Uninit the hook object system.
Definition: hook_object.c:614
#define MIN(a, b)
Definition: introdefs.h:146
QWORD HookStart
Guest virtual address of the hooked region.
Definition: hook_object.h:40
#define LOG(fmt,...)
Definition: glue.h:61
INTSTATUS IntHookGvaSetHook(QWORD Cr3, QWORD Gva, DWORD Length, BYTE Type, void *Callback, void *Context, void *ParentHook, DWORD Flags, HOOK_GVA **GvaHook)
Set a read, write, execute or swap hook on a guest virtual address.
Definition: hook_gva.c:345
BYTE HookType
The type of the hook structure (see _HOOK_TYPE)
Definition: hook.h:68
#define _Inout_
Definition: intro_sal.h:20
#define HOOK_FLG_GLOBAL_MASK
Global flags must be defined here and must be handled by each hooks layer (even if it ignores them...
Definition: hook.h:35
#define _Out_opt_
Definition: intro_sal.h:30
QWORD HookLength
Length of the hooked region. May span multiple pages.
Definition: hook_object.h:41
#define STATS_ENTER(id)
Definition: stats.h:153
static BOOLEAN RemoveEntryList(LIST_ENTRY *Entry)
Definition: introlists.h:87
BOOLEAN RegionsRemoved
True if regions have been removed from this object (used by the commit function). ...
Definition: hook_object.h:28
unsigned long long QWORD
Definition: intro_types.h:53
DWORD Flags
Hook flags.
Definition: hook_object.h:25
void * ParentHook
The parent hook. For a GPA hook, for example, a GVA hook or a PagedHook will be the parent hook...
Definition: hook.h:73
#define HOOK_FLG_CHAIN_DELETE
If flag is set, then we won&#39;t remove the hook on commit phase; we&#39;ll let the parent hook handle the d...
Definition: hook.h:48
Measures the deletion of HOOK_REGION_DESCRIPTOR objects.
Definition: stats.h:39
LIST_ENTRY Link
The list entry element.
Definition: hook_object.h:18
#define IN_RANGE_LEN(x, start, len)
Definition: introdefs.h:175
HOOK_HEADER Header
The hook header.
Definition: hook_object.h:38
static INTSTATUS IntHookObjectDeleteRegion(HOOK_REGION_DESCRIPTOR *Region, DWORD Flags)
Permanently deletes the indicated region.
Definition: hook_object.c:265
#define TRUE
Definition: intro_types.h:30
#define HpFreeAndNullWithTag(Add, Tag)
Definition: glue.h:517
#define IC_TAG_REGD
Object region descriptor.
Definition: memtags.h:37
static void InsertTailList(LIST_ENTRY *ListHead, LIST_ENTRY *Entry)
Definition: introlists.h:135
INTSTATUS IntHookGvaRemoveHook(HOOK_GVA **Hook, DWORD Flags)
Remove a GVA hook.
Definition: hook_gva.c:507
static void InitializeListHead(LIST_ENTRY *ListHead)
Definition: introlists.h:69
#define PAGE_SIZE
Definition: common.h:70
#define UNREFERENCED_PARAMETER(P)
Definition: introdefs.h:29
uint32_t DWORD
Definition: intro_types.h:49
HOOK_OBJECT_STATE Objects
Object hooks state.
Definition: hook.h:96
LIST_HEAD Objects
List of objects.
Definition: hook_object.h:55
#define INT_STATUS_NOT_INITIALIZED_HINT
Definition: introstatus.h:320
QWORD Cr3
The CR3 of the object. If this is a kernel object, Cr3 must be 0.
Definition: hook_object.h:24
#define INT_STATUS_INVALID_PARAMETER_1
Definition: introstatus.h:62
INTSTATUS IntHookObjectDestroy(HOOK_OBJECT_DESCRIPTOR **Object, DWORD Flags)
Destroy an entire hook object. All regions belonging to this object will be removed.
Definition: hook_object.c:357
#define ONE_GIGABYTE
Definition: introdefs.h:91
DWORD Flags
Generic flags. Check out EPT Hook flags.
Definition: hook.h:67
static INTSTATUS IntHookObjectRemoveRegionInternal(HOOK_REGION_DESCRIPTOR *Region, DWORD Flags)
Remove a hooked region of memory.
Definition: hook_object.c:10
LIST_ENTRY Link
The list entry element.
Definition: hook_object.h:39
INTSTATUS IntHookObjectInit(void)
Initialize the hook object system.
Definition: hook_object.c:598
Write-access hook.
Definition: glueiface.h:299
DWORD HooksCount
Number of hooks set for this region of memory.
Definition: hook_object.h:42
#define IC_TAG_HKOBJ
Hook-object descriptor.
Definition: memtags.h:33
#define HOOK_FLG_REMOVE
If flag is set, the hook has been removed, and waits the next commit to be actually deleted...
Definition: hook.h:44
#define INT_STATUS_INVALID_PARAMETER_7
Definition: introstatus.h:80
size_t SIZE_T
Definition: intro_types.h:60
#define FALSE
Definition: intro_types.h:34
#define INT_STATUS_INSUFFICIENT_RESOURCES
Definition: introstatus.h:281
#define INT_STATUS_INVALID_PARAMETER_3
Definition: introstatus.h:68