Bitdefender Hypervisor Memory Introspection
unpacker.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 Bitdefender
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 #include "unpacker.h"
6 #include "decoder.h"
7 #include "hook.h"
8 
9 
24 
25 
26 #define UNPACK_STATE_NONE 0x00
27 #define UNPACK_STATE_DIRTY 0x01
28 #define UNPACK_STATE_EXEC 0x02
29 
30 
34 typedef struct _UNPACK_PAGE
35 {
44  void *WriteHook;
45  void *ExecHook;
47 
48 
49 static LIST_HEAD gUnpckPages = LIST_HEAD_INIT(gUnpckPages);
50 
51 
52 
53 static PUNPACK_PAGE
55  _In_ QWORD Cr3,
57  )
66 {
67  LIST_ENTRY *list;
68 
69  list = gUnpckPages.Flink;
70  while (list != &gUnpckPages)
71  {
72  PUNPACK_PAGE pPage = CONTAINING_RECORD(list, UNPACK_PAGE, Link);
73  list = list->Flink;
74 
75  if ((pPage->VirtualAddress == VirtualAddress) && (pPage->Cr3 == Cr3))
76  {
77  return pPage;
78  }
79  }
80 
81  return NULL;
82 }
83 
84 
85 static INTSTATUS
87  _In_ PUNPACK_PAGE Page
88  )
97 {
98  INTSTATUS status;
99 
100  if (NULL == Page)
101  {
103  }
104 
105  if (NULL != Page->WriteHook)
106  {
107  status = IntHookGvaRemoveHook((HOOK_GVA **)&Page->WriteHook, 0);
108  if (!INT_SUCCESS(status))
109  {
110  ERROR("[ERROR] IntHookGvaRemoveHook failed: 0x%08x\n", status);
111  }
112  }
113 
114  if (NULL != Page->ExecHook)
115  {
116  status = IntHookGvaRemoveHook((HOOK_GVA **)&Page->ExecHook, 0);
117  if (!INT_SUCCESS(status))
118  {
119  ERROR("[ERROR] IntHookGvaRemoveHook failed: 0x%08x\n", status);
120  }
121  }
122 
123  RemoveEntryList(&Page->Link);
124 
126 
127  return INT_STATUS_SUCCESS;
128 }
129 
130 
131 static INTSTATUS
133  _In_opt_ void *Context,
134  _In_ void *Hook,
135  _In_ QWORD Address,
136  _Out_ INTRO_ACTION *Action
137  )
150 {
151  INTSTATUS status;
152  PUNPACK_PAGE pPage;
154  QWORD cr3, va;
155  void *cbkCtxt;
156  INSTRUX instrux;
157 
159 
160  if (NULL == Context)
161  {
163  }
164 
165  if (NULL == Action)
166  {
168  }
169 
170  cbk = NULL;
171  cbkCtxt = NULL;
172  va = 0;
173  cr3 = 0;
174 
175  pPage = (PUNPACK_PAGE)Context;
176 
177  pPage->State |= UNPACK_STATE_EXEC;
178 
179  if (pPage->State & UNPACK_STATE_DIRTY)
180  {
181  // Decode the designated instruction.
182  status = IntDecDecodeInstructionAtRip(gVcpu->Index, &gVcpu->Regs, NULL, &instrux);
183  if (!INT_SUCCESS(status))
184  {
185  ERROR("[ERROR] IntDecDecodeInstructionAtRip failed: 0x%08x\n", status);
186 
187  memset(&instrux, 0, sizeof(instrux));
188  }
189 
190  if (NULL != pPage->UnpackCallback)
191  {
192  cbk = pPage->UnpackCallback;
193  cr3 = pPage->Cr3;
194  va = pPage->VirtualAddress + (Address & PAGE_OFFSET);
195  cbkCtxt = pPage->CallbackContext;
196  }
197  }
198 
199  // Done, remove the hook from this page.
201 
202  if (NULL != cbk)
203  {
204  status = cbk(cr3, va, &instrux, cbkCtxt);
205  if (!INT_SUCCESS(status))
206  {
207  ERROR("[ERROR] Unpacker callback failed: 0x%08x\n", status);
208  }
209  }
210 
211  *Action = introGuestAllowed;
212 
213  return INT_STATUS_SUCCESS;
214 }
215 
216 
217 static INTSTATUS
219  _In_opt_ void *Context,
220  _In_ void *Hook,
221  _In_ QWORD Address,
222  _Out_ INTRO_ACTION *Action
223  )
239 {
240  INTSTATUS status;
241  PUNPACK_PAGE pPage;
242  BOOLEAN bValid;
243 
244  UNREFERENCED_PARAMETER(Address);
246 
247  if (NULL == Context)
248  {
250  }
251 
252  if (NULL == Action)
253  {
255  }
256 
257 
258  pPage = (PUNPACK_PAGE)Context;
259 
260  // Check if the write is valid (example, it is an IAT).
261  if (NULL != pPage->WriteCheckCallback)
262  {
263  bValid = pPage->WriteCheckCallback(pPage->Cr3,
264  pPage->VirtualAddress + (Address & 0xFFF),
265  pPage->CallbackContext);
266  }
267  else
268  {
269  bValid = FALSE;
270  }
271 
272  if (!bValid)
273  {
274  pPage->State |= UNPACK_STATE_DIRTY;
275  pPage->WriteCount++;
276  }
277 
278  if (pPage->WriteCount >= 32)
279  {
280  if (NULL != pPage->WriteHook)
281  {
282  status = IntHookGvaRemoveHook((HOOK_GVA **)&pPage->WriteHook, 0);
283  if (!INT_SUCCESS(status))
284  {
285  ERROR("[ERROR] IntHookGvaRemoveHook failed: 0x%08x\n", status);
286  }
287  }
288 
289  status = IntHookGvaSetHook(pPage->Cr3,
290  pPage->VirtualAddress,
291  PAGE_SIZE,
294  pPage,
295  NULL,
296  0,
297  (PHOOK_GVA *)&pPage->ExecHook);
298  if (!INT_SUCCESS(status))
299  {
300  ERROR("[ERROR] IntHookGvaSetHook failed: 0x%08x\n", status);
301  pPage->ExecHook = NULL;
302  goto cleanup_and_exit;
303  }
304  }
305 
306 cleanup_and_exit:
307 
308  // We will always allow the actions, but we won't send any notifications.
309  *Action = introGuestAllowed;
310 
311  return INT_STATUS_SUCCESS;
312 }
313 
314 
315 INTSTATUS
317  _In_ QWORD Cr3,
322  )
344 {
345  INTSTATUS status;
346 
347  UNPACK_PAGE *pPage = HpAllocWithTag(sizeof(*pPage), IC_TAG_UNPG);
348  if (NULL == pPage)
349  {
351  }
352 
353  pPage->Cr3 = Cr3;
355  pPage->State = UNPACK_STATE_NONE;
359  pPage->WriteHook = NULL;
360  pPage->ExecHook = NULL;
361 
362  InsertTailList(&gUnpckPages, &pPage->Link);
363 
364  status = IntHookGvaSetHook(Cr3,
365  VirtualAddress,
366  PAGE_SIZE,
369  pPage,
370  NULL,
371  0,
372  (PHOOK_GVA *)&pPage->WriteHook);
373  if (!INT_SUCCESS(status))
374  {
375  ERROR("[ERROR] IntHookGvaSetHook failed: 0x%08x\n", status);
376  pPage->WriteHook = NULL;
377  goto cleanup_and_exit;
378  }
379 
380  status = INT_STATUS_SUCCESS;
381 
382 cleanup_and_exit:
383  if (!INT_SUCCESS(status))
384  {
385  if (NULL != pPage)
386  {
388  }
389  }
390 
391  return status;
392 }
393 
394 
395 INTSTATUS
397  _In_ QWORD Cr3,
399  )
408 {
409  INTSTATUS status;
410  LIST_ENTRY *list;
411 
412  list = gUnpckPages.Flink;
413  while (list != &gUnpckPages)
414  {
415  PUNPACK_PAGE pPage = CONTAINING_RECORD(list, UNPACK_PAGE, Link);
416  list = list->Flink;
417 
418  if ((pPage->VirtualAddress == VirtualAddress) && (pPage->Cr3 == Cr3))
419  {
420  status = IntUnpUnWatchPageInternal(pPage);
421  if (!INT_SUCCESS(status))
422  {
423  ERROR("[ERROR] IntUnpUnWatchPageInternal failed: 0x%08x, page 0x%016llx/0x%016llx\n",
424  status,
425  pPage->VirtualAddress,
426  pPage->Cr3);
427  }
428 
429  break;
430  }
431  }
432 
433  return INT_STATUS_SUCCESS;
434 }
435 
436 
437 INTSTATUS
439  _In_ QWORD Cr3
440  )
448 {
449  INTSTATUS status;
450  LIST_ENTRY *list;
451 
452  list = gUnpckPages.Flink;
453  while (list != &gUnpckPages)
454  {
455  PUNPACK_PAGE pPage = CONTAINING_RECORD(list, UNPACK_PAGE, Link);
456  list = list->Flink;
457 
458  if (pPage->Cr3 == Cr3)
459  {
460  status = IntUnpUnWatchPageInternal(pPage);
461  if (!INT_SUCCESS(status))
462  {
463  ERROR("[ERROR] IntUnpUnWatchPageInternal failed: 0x%08x, page 0x%016llx/0x%016llx\n",
464  status, pPage->VirtualAddress, pPage->Cr3);
465  }
466  }
467  }
468 
469  return INT_STATUS_SUCCESS;
470 }
471 
472 
473 INTSTATUS
475  void
476  )
482 {
483  INTSTATUS status;
484  LIST_ENTRY *list;
485 
486  list = gUnpckPages.Flink;
487  while (list != &gUnpckPages)
488  {
489  PUNPACK_PAGE pPage = CONTAINING_RECORD(list, UNPACK_PAGE, Link);
490  list = list->Flink;
491 
492  status = IntUnpUnWatchPageInternal(pPage);
493  if (!INT_SUCCESS(status))
494  {
495  ERROR("[ERROR] IntUnpUnWatchPageInternal failed: 0x%08x, page 0x%016llx/0x%016llx\n",
496  status, pPage->VirtualAddress, pPage->Cr3);
497  }
498  }
499 
500  return INT_STATUS_SUCCESS;
501 }
502 
503 
504 void
506  void
507  )
511 {
513 }
#define _In_opt_
Definition: intro_sal.h:16
void * CallbackContext
Optional context, passed to the callbacks.
Definition: unpacker.c:43
_Bool BOOLEAN
Definition: intro_types.h:58
#define _Out_
Definition: intro_sal.h:22
#define CONTAINING_RECORD(List, Type, Member)
Definition: introlists.h:36
uint8_t BYTE
Definition: intro_types.h:47
IG_ARCH_REGS Regs
The current state of the guest registers.
Definition: guests.h:95
DWORD Index
The VCPU number.
Definition: guests.h:172
#define _In_
Definition: intro_sal.h:21
#define INT_STATUS_SUCCESS
Definition: introstatus.h:54
uint16_t WORD
Definition: intro_types.h:48
struct _LIST_ENTRY * Flink
Definition: introlists.h:20
void * ExecHook
Exec hook handle.
Definition: unpacker.c:45
INTSTATUS IntUnpUnWatchVaSpacePages(QWORD Cr3)
Stop monitoring all pages belonging to a virtual address space.
Definition: unpacker.c:438
#define INT_SUCCESS(Status)
Definition: introstatus.h:42
#define PAGE_OFFSET
Definition: pgtable.h:32
INTSTATUS(* PFUNC_PageUnpackedCallback)(QWORD Cr3, QWORD VirtualAddress, PINSTRUX Instrux, void *Context)
Called when a page is considered to be "unpacked".
Definition: unpacker.h:24
QWORD VirtualAddress
Page virtual address.
Definition: unpacker.c:38
#define ERROR(fmt,...)
Definition: glue.h:62
INTSTATUS IntDecDecodeInstructionAtRip(DWORD CpuNumber, IG_ARCH_REGS *Registers, IG_SEG_REGS *Segments, INSTRUX *Instrux)
Decode an instruction at current RIP on the provided VCPU.
Definition: decoder.c:384
#define HpAllocWithTag(Len, Tag)
Definition: glue.h:516
int INTSTATUS
The status data type.
Definition: introstatus.h:24
static INTSTATUS IntUnpPageWriteCallback(void *Context, void *Hook, QWORD Address, INTRO_ACTION *Action)
Handle writes inside a monitored page.
Definition: unpacker.c:218
WORD WriteCount
Number of times the page has been written.
Definition: unpacker.c:39
LIST_ENTRY Link
List entry link.
Definition: unpacker.c:36
static INTSTATUS IntUnpUnWatchPageInternal(PUNPACK_PAGE Page)
Remove monitor from the indicated page.
Definition: unpacker.c:86
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
static INTSTATUS IntUnpPageExecuteCallback(void *Context, void *Hook, QWORD Address, INTRO_ACTION *Action)
Handle executions from a monitored page.
Definition: unpacker.c:132
static BOOLEAN RemoveEntryList(LIST_ENTRY *Entry)
Definition: introlists.h:87
unsigned long long QWORD
Definition: intro_types.h:53
void * WriteHook
Write hook handle.
Definition: unpacker.c:44
#define INT_STATUS_INVALID_PARAMETER_4
Definition: introstatus.h:71
#define HpFreeAndNullWithTag(Add, Tag)
Definition: glue.h:517
#define INT_STATUS_INVALID_PARAMETER_5
Definition: introstatus.h:74
PFUNC_PageIsWriteValid WriteCheckCallback
Write callback, called when the page is written.
Definition: unpacker.c:42
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
#define PAGE_SIZE
Definition: common.h:70
#define UNREFERENCED_PARAMETER(P)
Definition: introdefs.h:29
void IntUnpUninit(void)
Uninit the unpacker. This will stop the monitor on all pages.
Definition: unpacker.c:505
enum _INTRO_ACTION INTRO_ACTION
Event actions.
#define UNPACK_STATE_EXEC
The page contains code that has been fetched for execution.
Definition: unpacker.c:28
INTSTATUS IntUnpUnWatchPage(QWORD Cr3, QWORD VirtualAddress)
Stop monitoring the indicated page.
Definition: unpacker.c:396
struct _UNPACK_PAGE UNPACK_PAGE
#define UNPACK_STATE_DIRTY
The page was written.
Definition: unpacker.c:27
BOOLEAN(* PFUNC_PageIsWriteValid)(QWORD Cr3, QWORD VirtualAddress, void *Context)
Called when a page is written.
Definition: unpacker.h:47
static PUNPACK_PAGE IntUnpFindPage(QWORD Cr3, QWORD VirtualAddress)
Finds a monitored page.
Definition: unpacker.c:54
QWORD Cr3
Virtual address space this page belongs to.
Definition: unpacker.c:37
#define LIST_HEAD_INIT(Name)
Definition: introlists.h:39
#define IC_TAG_UNPG
Protected unpacker-page.
Definition: memtags.h:39
#define INT_STATUS_INVALID_PARAMETER_1
Definition: introstatus.h:62
VCPU_STATE * gVcpu
The state of the current VCPU.
Definition: guests.c:59
VE_CACHE_LINE * Page
Mapped page inside Introspection virtual address space.
Definition: vecore.c:120
INTSTATUS IntUnpRemovePages(void)
Stop monitoring all pages.
Definition: unpacker.c:474
static LIST_HEAD gUnpckPages
Definition: unpacker.c:49
PFUNC_PageUnpackedCallback UnpackCallback
Unpack callback, called as soon as the page has been unpacked.
Definition: unpacker.c:41
#define UNPACK_STATE_NONE
Initial state.
Definition: unpacker.c:26
Execute-access hook.
Definition: glueiface.h:300
Write-access hook.
Definition: glueiface.h:299
BYTE State
Page state - check UNPACK_STATE*.
Definition: unpacker.c:40
INTSTATUS IntUnpWatchPage(QWORD Cr3, QWORD VirtualAddress, PFUNC_PageUnpackedCallback UnpackCallback, PFUNC_PageIsWriteValid WriteCheckCallback, void *CallbackContext)
Monitor a page against unpacking.
Definition: unpacker.c:316
struct _UNPACK_PAGE * PUNPACK_PAGE
#define FALSE
Definition: intro_types.h:34
#define INT_STATUS_INSUFFICIENT_RESOURCES
Definition: introstatus.h:281