Bitdefender Hypervisor Memory Introspection
memcloak.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 Bitdefender
3  * SPDX-License-Identifier: Apache-2.0
4  */
7 
8 #include "memcloak.h"
9 #include "crc32.h"
10 #include "decoder.h"
11 #include "hook.h"
12 
16 typedef struct _MEMCLOAK_PAGE
17 {
21  void *SwapHook;
22  void *ReadHook;
23  void *WriteHook;
24  void *Region;
26 
27 #define MEMCLOACK_PAGE_MAX_COUNT 2
28 
29 typedef struct _MEMCLOAK_REGION
32 {
40 
46 
52 
54 static LIST_HEAD gMemClkRegions = LIST_HEAD_INIT(gMemClkRegions);
55 
56 
57 static INTSTATUS
59  _In_ DWORD Options,
60  _Inout_ PMEMCLOAK_REGION Region
61  );
62 
63 static INTSTATUS
65  _Inout_ MEMCLOAK_PAGE *Context,
66  _In_ QWORD VirtualAddress,
67  _In_ QWORD OldEntry,
68  _In_ QWORD NewEntry,
69  _In_ QWORD OldPageSize,
70  _In_ QWORD NewPageSize
71  )
87 
88 {
89  INTSTATUS status;
90  PMEMCLOAK_REGION pCloak;
91  PMEMCLOAK_PAGE pPage;
92  DWORD oldPgOffs, newPgOffs, internalOffset, size;
93  QWORD oldPhysAddr, newPhysAddr;
94  BOOLEAN oldValid, newValid;
95 
96  status = INT_STATUS_SUCCESS;
97 
98  // Fetch the context
99  pPage = Context;
100  pCloak = (PMEMCLOAK_REGION)pPage->Region;
101 
102  // Get the physical page address.
103  oldPhysAddr = CLEAN_PHYS_ADDRESS64(OldEntry) & (~(OldPageSize - 1));
104  newPhysAddr = CLEAN_PHYS_ADDRESS64(NewEntry) & (~(NewPageSize - 1));
105 
106  oldValid = (OldEntry & 1);
107  newValid = (NewEntry & 1);
108 
109  // If nothing interesting changed, bail out.
110  if ((!oldValid && !newValid) || ((oldValid && newValid && (oldPhysAddr == newPhysAddr))))
111  {
112  return INT_STATUS_SUCCESS;
113  }
114 
115  // Compute the offset inside the current page of the cloaked data.
116  if ((pCloak->Gva & PAGE_MASK) ==
117  ((pCloak->Gva + pCloak->Size - 1) & PAGE_MASK))
118  {
119  // The cloaked region is contained within the same 4K page. This is the simplest case. Note that we use
120  // 4K pages for this test because they're the smallest entity that can be translated by the CPU.
121  oldPgOffs = (DWORD)(pCloak->Gva & (OldPageSize - 1));
122  newPgOffs = (DWORD)(pCloak->Gva & (NewPageSize - 1));
123 
124  internalOffset = 0;
125 
126  size = pCloak->Size;
127  }
128  else
129  {
130  // Hook spills multiple pages. Handle each case. Note that we use PAGE_SIZE and PAGE_MASK because we place
131  // hooks on 4K pages: even if the pages will be remapped as a large 2M page, this callback will be invoked
132  // for each 4K page individually.
133  if ((pCloak->Gva & PAGE_MASK) == (VirtualAddress & PAGE_MASK))
134  {
135  // The first page written. This also covers the above case.
136  oldPgOffs = (DWORD)(pCloak->Gva & (OldPageSize - 1));
137  newPgOffs = (DWORD)(pCloak->Gva & (NewPageSize - 1));
138 
139  internalOffset = 0;
140 
141  size = (DWORD)MIN(PAGE_REMAINING(pCloak->Gva), pCloak->Size);
142  }
143  else
144  {
145  // Another page written.
146  oldPgOffs = (DWORD)(((pCloak->Gva & PAGE_MASK) + PAGE_SIZE) & (OldPageSize - 1));
147  newPgOffs = (DWORD)(((pCloak->Gva & PAGE_MASK) + PAGE_SIZE) & (NewPageSize - 1));
148 
149  internalOffset = (DWORD)((VirtualAddress & PAGE_MASK) - pCloak->Gva);
150 
151  size = MIN(PAGE_SIZE, pCloak->Size - internalOffset);
152  }
153  }
154 
155  // Even if the old page and the new page have different sizes, the internalOffset will be the same, since we
156  // align the accesses to 4K (we always deal with 4K pages).
157 
158  if ((internalOffset >= pCloak->Size) || (internalOffset + size > pCloak->Size) || (size > pCloak->Size))
159  {
160  ERROR("[ERROR] Invalid state: internalOffset = %d, size = %d, total size = %d\n",
161  internalOffset, size, pCloak->Size);
163  }
164 
165  TRACE("[MEMCLOAK] Translation modification, VA = 0x%016llx, OldEntry = 0x%016llx, NewEntry = 0x%016llx, "
166  "OldSize = 0x%016llx, NewSize = 0x%016llx, OldOffs = %x, NewOffs = %x, size = %d\n",
167  VirtualAddress, OldEntry, NewEntry, OldPageSize, NewPageSize, oldPgOffs, newPgOffs, size);
168 
169  TRACE("[MEMCLOAK] Will patch at GPA %llx/%llx offset %x/%x, size %d, int offset %d, [GVA1: %llx GVA2: %llx]\n",
170  oldPhysAddr, newPhysAddr, oldPgOffs, newPgOffs, size,
171  internalOffset, pCloak->Gva, VirtualAddress);
172 
173  if (oldValid)
174  {
175  status = IntPhysicalMemWrite(oldPhysAddr + oldPgOffs, size, &pCloak->OriginalData[internalOffset]);
176  if (!INT_SUCCESS(status))
177  {
178  ERROR("[ERROR] IntPhysicalMemWrite failed: 0x%08x\n", status);
179  }
180  }
181 
182  if (newValid)
183  {
184  // Old entry was not present, the new one is.
185  status = IntPhysicalMemWrite(newPhysAddr + newPgOffs, size, &pCloak->PatchedData[internalOffset]);
186  if (!INT_SUCCESS(status))
187  {
188  ERROR("[ERROR] IntPhysicalMemWrite failed: 0x%08x\n", status);
189  }
190  }
191 
192  return status;
193 }
194 
195 
196 static INTSTATUS
198  _Inout_ MEMCLOAK_PAGE *Context,
199  _In_ PHOOK_GPA Hook,
200  _In_ QWORD Gpa,
201  _Out_ INTRO_ACTION *Action
202  )
240 {
241  PIG_ARCH_REGS regs;
242  PMEMCLOAK_REGION pClkReg;
243  PMEMCLOAK_PAGE pClkPage;
244  PPATCH_BUFFER pb;
245  PINSTRUX instrux;
246  INTSTATUS status;
247  DWORD patchSize, origCodeOffset, retBufOffset, readSize;
248  QWORD gla;
249  PHOOK_GVA pGva;
250  DWORD startReadOffset, endReadOffset;
251  DWORD glaOffset, gpaOffset;
252 
253  // Fetch the cloak region pointer.
254  pClkPage = Context;
255  pClkReg = (PMEMCLOAK_REGION)pClkPage->Region;
256 
257  *Action = introGuestAllowed;
258 
259  pGva = (PHOOK_GVA)Hook->Header.ParentHook;
260  if (NULL == pGva)
261  {
263  }
264 
265  if (pGva->Header.HookType != hookTypeGva)
266  {
268  }
269 
270  // Get the register state
271  regs = &gVcpu->Regs;
272  instrux = &gVcpu->Instruction;
273  pb = &gVcpu->PatchBuffer;
274 
275  // In certain situations when the GLA is page-aligned it can't be trusted
276  // Let's say we have a cloak region starting at 0x1006 and the guest starts a read on 0xffc with size 8
277  // This will end at 0x1004, not even touching our region, but the CPU may report 0x1000 as the faulting GLA
278  // So take the GLA from the instruction instead
279  // This is not needed on Napoca due to the way read accessed are handled. On Xen, a read is emulated, so we need
280  // to supply the actual value for the operand of the offending instruction, on Napoca the read is single stepped
281  // so the contents are copied inside the guest starting with the offending GLA
282  status = IntDecDecodeSourceLinearAddressFromInstruction(instrux, regs, &gla);
283  if (!INT_SUCCESS(status))
284  {
285  ERROR("[ERROR] IntDecDecodeSourceLinearAddressFromInstruction failed: 0x%08x\n", status);
286  return status;
287  }
288 
289  // If reads are allowed to be performed from within the cloaked region, bail out.
290  if ((0 != (pClkReg->Options & MEMCLOAK_OPT_ALLOW_INTERNAL)) &&
291  ((regs->Rip >= pClkReg->Gva) && (regs->Rip < pClkReg->Gva + pClkReg->Size)))
292  {
294  }
295 
296  readSize = gVcpu->AccessSize;
297  glaOffset = gla & PAGE_OFFSET;
298  gpaOffset = Gpa & PAGE_OFFSET;
299 
300  if ((0 == readSize) || (readSize > ND_MAX_REGISTER_SIZE))
301  {
302  CHAR instrText[ND_MIN_BUF_SIZE] = {0};
303 
304  NdToText(instrux, regs->Rip, ND_MIN_BUF_SIZE, instrText);
305 
306  ERROR("[MEMCLOAK] [ERROR] Couldn't find the source memory operand for instruction at RIP 0x%016llx '%s'!\n",
307  regs->Rip, instrText);
308 
310 
312  }
313 
314  // Read the actual bytes form the guest. We will patch below with the original bytes. Note that if the patch-buffer
315  // has already been initialized, it simply means that this is an access that touches multiple cloaks (example,
316  // inside the kernel slack space, where the detour handlers are closely packed together).
317  if (!pb->Valid)
318  {
319  status = IntVirtMemRead(gla, readSize, regs->Cr3, pb->Data, NULL);
320  if ((INT_STATUS_PAGE_NOT_PRESENT == status) || (INT_STATUS_NO_MAPPING_STRUCTURES == status))
321  {
322  *Action = introGuestAllowed;
323  return INT_STATUS_SUCCESS;
324  }
325  else if (!INT_SUCCESS(status))
326  {
327  ERROR("[ERROR] IntVirtMemRead failed for GPA 0x%016llx with size %d: 0x%08x\n", gla, readSize, status);
328  return status;
329  }
330 
331  pb->Gla = gla;
332  pb->Size = readSize;
333  pb->Valid = TRUE;
334  }
335  else if (pb->Gla != gla)
336  {
337  // The access was already handled. This can happen if the access spans inside two pages, because we call the
338  // handlers for each accessed page. For example, if we have a read at 0x1FFC with size 8, this callback will
339  // be called two times: the first time with GLA = 0x1FFC and size 8, and the second time with GLA = 0x2000 and
340  // size 4. Since the read was already handled the first time this callback was called, we can safely bail out.
341  // Note that if the patch buffer is valid we may still have to handle this access, as the read may span multiple
342  // cloaks, but on those cases, the patch buffer and the current GLAs will always be the same.
344  }
345 
346  startReadOffset = gpaOffset;
347  endReadOffset = startReadOffset + readSize;
348 
349  //
350  // There are a few cases that must be supported, which can be summarized as:
351  // If the cloak sub-region is: [cStart, cEnd) and the read region is [rStart, rEnd), we can combine them as:
352  // 1. rStart < cStart < rEnd < cEnd (starts before the sub-region, ends inside it)
353  // 2. rStart < cStart < cEnd < rEnd (starts before the sub-region, ends after it)
354  // 3. cStart < rStart < rEnd < cEnd (starts inside the sub-region, ends inside it)
355  // 4. cStart < rStart < cEnd < rEnd (starts inside the sub-region, ends after it)
356  // Note that rEnd < cStart (read ends before the start of the sub-region) and cEnd < rStart (read starts after the
357  // end of the sub-region) are handled by the hooking mechanism and this callback is never invoked in those cases
358  // The above examples can get more complicated when dealing with page boundary issues, mainly: a read that starts
359  // in one page and ends in the next one, touches a sub-region contained only in the next page: this is true when a
360  // region is split across two pages (so it is contained in two sub-regions) or when it starts at the page start
361  //
362 
363  if (glaOffset == gpaOffset)
364  {
365  // The access starts in the same page as the hooked page
366  // The read ends in the next page, but we care only about the current one
367  if (endReadOffset > PAGE_SIZE)
368  {
369  // The read ends at the end of this page
370  endReadOffset = PAGE_SIZE;
371  // Ignore anything that is read from the next page
372  readSize = endReadOffset - startReadOffset;
373  }
374 
375  if (startReadOffset < pClkPage->PageStartOffset)
376  {
377  // The read starts before the cloak
378  origCodeOffset = 0 + pClkPage->DataOffset;
379  retBufOffset = pClkPage->PageStartOffset - startReadOffset;
380  // The read either ends inside the sub-region or after it ends, give back the minimum between the sub-region
381  // size and the read size (ignoring everything that comes before the sub-region)
382  patchSize = MIN(endReadOffset - pClkPage->PageStartOffset,
383  pClkPage->PageEndOffset - pClkPage->PageStartOffset);
384  }
385  else
386  {
387  // The read starts inside the sub-region
388  origCodeOffset = startReadOffset - pClkPage->PageStartOffset + pClkPage->DataOffset;
389  retBufOffset = 0;
390  // The read either ends inside the sub-region or after it ends, give back the minimum between the sub-region
391  // size and the read size (ignoring everything that comes before the sub-region)
392  patchSize = MIN(endReadOffset - startReadOffset, pClkPage->PageEndOffset - startReadOffset);
393  }
394  }
395  else
396  {
397  DWORD readInPrevPage;
398 
399  // The access starts in a page, but the hook is in the next one
400  // Advance the read start to the next page
401  startReadOffset = gpaOffset;
402  // Recalculate the read size and the read end offset to ignore anything from the previous page
403  readSize -= (PAGE_SIZE - glaOffset);
404  endReadOffset = startReadOffset + readSize;
405  // Remember what was read in the previous page
406  readInPrevPage = gVcpu->AccessSize - readSize;
407 
408  if (startReadOffset <= pClkPage->PageStartOffset)
409  {
410  // The read starts before the cloak
411  origCodeOffset = 0 + pClkPage->DataOffset;
412  // Keep in mind that the instruction reads data from two pages, but pClkPage->PageStartOffset and
413  // startReadOffset refer only to the current page
414  retBufOffset = pClkPage->PageStartOffset - startReadOffset + readInPrevPage;
415  // The read either ends inside the sub-region or after it ends, give back the minimum between the sub-region
416  // size and the read size (ignoring everything that comes before the sub-region)
417  patchSize = MIN(endReadOffset - pClkPage->PageStartOffset,
418  pClkPage->PageEndOffset - pClkPage->PageStartOffset);
419  }
420  else
421  {
422  // The read starts inside the cloak sub-range. This shouldn't happen as the read starts in the previous page
423  // and the sub-range starts in the current page
424 
425  CHAR text[ND_MIN_BUF_SIZE] = { 0 };
426 
427  NdToText(instrux, regs->Rip, ND_MIN_BUF_SIZE, text);
428 
429  LOG("[MEMCLOAK] This should not happen\n");
430  LOG("[MEMCLOAK] [CPU %d] From RIP 0x%016llx with instruction '%s', RegSize %d, GPA 0x%016llx, "
431  "MemSize %d, access 0x%016llx/%d [0x%08x, 0x%08x) -> [0x%08x: 0x%08x, 0x%08x)\n",
432  gVcpu->Index, regs->Rip, text, instrux->Operands[0].Size, Gpa,
433  instrux->Operands[1].Size, gla, readSize, startReadOffset, endReadOffset,
434  pClkPage->DataOffset, pClkPage->PageStartOffset, pClkPage->PageEndOffset);
436 
437  origCodeOffset = retBufOffset = patchSize = 0;
438  }
439  }
440 
441  if (startReadOffset < pClkPage->PageEndOffset &&
442  endReadOffset > pClkPage->PageStartOffset)
443  {
444  // Patch the return buffer only if the guest read overlaps with the region guarded by this cloak page
445  // This can happen as in the above case when the region starts at 0x1006 and and 8 bytes read starts at 0xffc,
446  // but it is reported as being done for [0x1000, 0x10008), when in fact it does not overlap the region
447  memcpy(&pb->Data[retBufOffset], &pClkReg->OriginalData[origCodeOffset], patchSize);
448  }
449 
450 #ifdef DEBUG
451 
452  TRACE("[MEMCLOAK] Handled the access on 0x%016llx/0x%016llx with retBufOffset = 0x%08x, "
453  "origCodeOffset = 0x%08x, patchSize = 0x%08x\n",
454  gla, Gpa, retBufOffset, origCodeOffset, patchSize);
455 
456  IntDumpBuffer(pb->Data, pb->Gla, pb->Size, 16, 1, TRUE, TRUE);
457 
458 #endif // DEBUG
459 
460  *Action = introGuestAllowedPatched;
461 
462  return INT_STATUS_SUCCESS;
463 }
464 
465 
466 static INTSTATUS
468  _In_ PMEMCLOAK_PAGE *Context,
469  _In_ void *Hook,
470  _In_ QWORD Address,
471  _Out_ INTRO_ACTION *Action
472  )
495 {
496  INTSTATUS status;
497  MEMCLOAK_REGION *pClkRegion;
498 
499  pClkRegion = (MEMCLOAK_REGION *)((MEMCLOAK_PAGE *)Context)->Region;
500 
501  // By default, any writes inside a memcloak should be denied.
502  // This may be overwritten by the region's write handler.
503  *Action = introGuestNotAllowed;
504 
505  if (NULL != pClkRegion->WriteHandler)
506  {
507  status = pClkRegion->WriteHandler(Hook, Address, pClkRegion->Gva, pClkRegion, Action);
508  if (!INT_SUCCESS(status))
509  {
510  WARNING("[WARNING] Memcloak write handler failed for virtual address 0x%llx. Status: 0x%08x\n",
511  Address, status);
512  }
513  }
514 
515  // Force the returned action, on beta. We must deny writes inside the hooked portions, even on beta!
517 }
518 
519 
520 void
523  )
532 {
533  if (NULL != Region->OriginalData)
534  {
535  HpFreeAndNullWithTag(&Region->OriginalData, IC_TAG_MCBF);
536  }
537 
538  if (NULL != Region->PatchedData)
539  {
540  HpFreeAndNullWithTag(&Region->PatchedData, IC_TAG_MCBF);
541  }
542 
544 }
545 
546 
547 INTSTATUS
549  _In_ QWORD VirtualAddress,
550  _In_ QWORD Cr3,
551  _In_ DWORD Size,
552  _In_ DWORD Options,
553  _In_opt_ PBYTE OriginalData,
554  _In_opt_ PBYTE PatchedData,
556  _Out_ void **CloakHandle
557  )
593 {
594  INTSTATUS status;
595  PMEMCLOAK_REGION pClkReg;
596  DWORD leftToHook;
597  DWORD pageCount;
598 
599  if (NULL == CloakHandle)
600  {
602  }
603 
604  leftToHook = Size;
605  pageCount = ((((VirtualAddress + Size - 1) & PAGE_MASK) - (VirtualAddress & PAGE_MASK)) / PAGE_SIZE) + 1;
606 
607  if (pageCount > MEMCLOACK_PAGE_MAX_COUNT)
608  {
610  }
611 
612  pClkReg = HpAllocWithTag(sizeof(*pClkReg), IC_TAG_MCRG);
613  if (NULL == pClkReg)
614  {
616  }
617 
618  InsertTailList(&gMemClkRegions, &pClkReg->Link);
619 
620  if (0 == Cr3)
621  {
622  Cr3 = gGuest.Mm.SystemCr3;
623  }
624 
625  pClkReg->Gva = VirtualAddress;
626  pClkReg->Cr3 = Cr3;
627  pClkReg->Size = Size;
628  pClkReg->Options = Options;
629  pClkReg->WriteHandler = WriteHandler;
630 
631  pClkReg->OriginalData = HpAllocWithTag(Size, IC_TAG_MCBF);
632  if (NULL == pClkReg->OriginalData)
633  {
635  goto cleanup_and_exit;
636  }
637 
638  pClkReg->PatchedData = HpAllocWithTag(Size, IC_TAG_MCBF);
639  if (NULL == pClkReg->PatchedData)
640  {
642  goto cleanup_and_exit;
643  }
644 
645  // Now copy the original data inside the internal buffer.
646  if (NULL != OriginalData)
647  {
648  memcpy(pClkReg->OriginalData, OriginalData, Size);
649  }
650 
651  // And the patched data.
652  if (NULL != PatchedData)
653  {
654  memcpy(pClkReg->PatchedData, PatchedData, Size);
655  }
656 
657  for (QWORD va = VirtualAddress; va < VirtualAddress + Size;)
658  {
659  DWORD hookLen = MIN(leftToHook, PAGE_REMAINING(va));
660  PMEMCLOAK_PAGE pClkPage = &pClkReg->Pages[pClkReg->PageCount++];
661 
662  leftToHook -= hookLen;
663 
664  pClkPage->Region = pClkReg;
665  pClkPage->DataOffset = (DWORD)(va - VirtualAddress);
666  pClkPage->PageStartOffset = va & PAGE_OFFSET;
667  pClkPage->PageEndOffset = pClkPage->PageStartOffset + hookLen;
668 
669  // Swap hook. The callback will be called on translation changes.
670  status = IntHookGvaSetHook(Cr3, va, hookLen, IG_EPT_HOOK_NONE, IntMemClkHandleSwap,
671  pClkPage, NULL, HOOK_FLG_HIGH_PRIORITY, (PHOOK_GVA *)&pClkPage->SwapHook);
672  if (!INT_SUCCESS(status))
673  {
674  ERROR("[ERROR] IntHookGvaSetHook failed for 0x%016llx: 0x%08x\n", va, status);
675  goto cleanup_and_exit;
676  }
677 
678  // Write hook. Needed on order to ensure correct EPT access rights are present.
679  status = IntHookGvaSetHook(Cr3, va, hookLen, IG_EPT_HOOK_WRITE, IntMemClkHandleWrite,
680  pClkPage, NULL, 0, (PHOOK_GVA *)&pClkPage->WriteHook);
681  if (!INT_SUCCESS(status))
682  {
683  ERROR("[ERROR] IntHookGvaSetHook failed for 0x%016llx: 0x%08x\n", va, status);
684  goto cleanup_and_exit;
685  }
686 
687  // Read hook. On each read hitting our cloaked region, return the original date, thus hiding our modifications.
688  status = IntHookGvaSetHook(Cr3, va, hookLen, IG_EPT_HOOK_READ, IntMemClkHandleRead,
689  pClkPage, NULL, 0, (PHOOK_GVA *)&pClkPage->ReadHook);
690  if (!INT_SUCCESS(status))
691  {
692  ERROR("[ERROR] IntHookGvaSetHook failed for 0x%016llx: 0x%08x\n", va, status);
693  goto cleanup_and_exit;
694  }
695 
696  va += hookLen;
697  }
698 
699  if (0 != (Options & MEMCLOAK_OPT_APPLY_PATCH))
700  {
701  IntPauseVcpus();
702 
703  status = IntVirtMemWrite(VirtualAddress, Size, Cr3, pClkReg->PatchedData);
704 
705  IntResumeVcpus();
706 
707  if (!INT_SUCCESS(status) && (INT_STATUS_PAGE_NOT_PRESENT != status) &&
709  {
710  ERROR("[ERROR] IntVirtMemWrite failed: 0x%08x\n", status);
711  goto cleanup_and_exit;
712  }
713  }
714 
715  *CloakHandle = pClkReg;
716 
717  return INT_STATUS_SUCCESS;
718 
719 cleanup_and_exit:
720  if (NULL != pClkReg)
721  {
722  INTSTATUS status2 = IntMemClkUncloakRegionInternal(Options, pClkReg);
723  if (!INT_SUCCESS(status2))
724  {
725  ERROR("[ERROR] IntMemClkUncloakRegionInternal failed: 0x%08x\n", status2);
726  }
727  }
728 
729  return status;
730 }
731 
732 
733 INTSTATUS
735  _In_ void *CloakHandle,
736  _In_ DWORD Offset,
737  _In_ DWORD Size,
738  _In_ void *Data
739  )
758 {
759  PMEMCLOAK_REGION pClk;
760 
761  if (NULL == CloakHandle)
762  {
764  }
765 
766  pClk = (PMEMCLOAK_REGION)CloakHandle;
767 
768  if (Offset >= pClk->Size)
769  {
771  }
772 
773  if (Offset + Size > pClk->Size)
774  {
776  }
777 
778  if (Size > pClk->Size)
779  {
781  }
782 
783  if (NULL == Data)
784  {
786  }
787 
788  memcpy(pClk->OriginalData + Offset, Data, Size);
789 
790  return INT_STATUS_SUCCESS;
791 }
792 
793 
794 INTSTATUS
796  _In_ void *CloakHandle,
797  _In_ DWORD Offset,
798  _In_ DWORD Size,
799  _In_opt_ const void *Data
800  )
828 {
829  INTSTATUS status;
830  PMEMCLOAK_REGION pClk;
831 
832  if (NULL == CloakHandle)
833  {
835  }
836 
837  pClk = (PMEMCLOAK_REGION)CloakHandle;
838 
839  if (Offset >= pClk->Size)
840  {
842  }
843 
844  if (Offset + Size > pClk->Size)
845  {
847  }
848 
849  if (Size > pClk->Size)
850  {
852  }
853 
854  IntPauseVcpus();
855 
856  if (NULL != Data)
857  {
858  memcpy(pClk->PatchedData + Offset, Data, Size);
859  }
860  else
861  {
862  memset(pClk->PatchedData + Offset, 0, Size);
863  }
864 
865  status = IntVirtMemWrite(pClk->Gva + Offset,
866  Size,
867  pClk->Cr3,
868  pClk->PatchedData + Offset);
869 
870  IntResumeVcpus();
871 
872  if (!INT_SUCCESS(status) && (INT_STATUS_PAGE_NOT_PRESENT != status) && (INT_STATUS_NO_MAPPING_STRUCTURES != status))
873  {
874  ERROR("[ERROR] IntVirtMemWrite failed: 0x%08x\n", status);
875  return status;
876  }
877 
878  // Even if we couldn't modify the memory, because it's swapped out, we're still ok - on swap-in, the
879  // original content will be brought back in memory.
880  return INT_STATUS_SUCCESS;
881 }
882 
883 
884 static INTSTATUS
886  _In_ DWORD Options,
887  _Inout_ PMEMCLOAK_REGION Region
888  )
908 {
909  INTSTATUS status = INT_STATUS_SUCCESS;
910 
911  RemoveEntryList(&Region->Link);
912 
913  if (0 != (Options & MEMCLOAK_OPT_APPLY_PATCH))
914  {
915  IntPauseVcpus();
916 
917  memcpy(Region->PatchedData, Region->OriginalData, Region->Size);
918 
919  status = IntKernVirtMemWrite(Region->Gva, Region->Size, Region->OriginalData);
920 
921  IntResumeVcpus();
922 
923  if (!INT_SUCCESS(status) &&
924  (INT_STATUS_PAGE_NOT_PRESENT != status) &&
926  {
927  ERROR("[ERROR] IntKernVirtMemWrite failed: 0x%08x\n", status);
928  }
929  }
930 
931  for (DWORD i = 0; i < Region->PageCount; i++)
932  {
933  MEMCLOAK_PAGE *pPage = &Region->Pages[i];
934 
935  if (NULL != pPage->ReadHook)
936  {
937  status = IntHookGvaRemoveHook((HOOK_GVA **)&pPage->ReadHook, 0);
938  if (!INT_SUCCESS(status))
939  {
940  ERROR("[ERROR] IntHookGvaRemoveHook failed: 0x%08x\n", status);
941  }
942  }
943 
944  if (NULL != pPage->WriteHook)
945  {
946  status = IntHookGvaRemoveHook((HOOK_GVA **)&pPage->WriteHook, 0);
947  if (!INT_SUCCESS(status))
948  {
949  ERROR("[ERROR] IntHookGvaRemoveHook failed: 0x%08x\n", status);
950  }
951  }
952 
953  if (NULL != pPage->SwapHook)
954  {
955  status = IntHookGvaRemoveHook((HOOK_GVA **)&pPage->SwapHook, 0);
956  if (!INT_SUCCESS(status))
957  {
958  ERROR("[ERROR] IntHookGvaRemoveHook failed: 0x%08x\n", status);
959  }
960  }
961  }
962 
963  IntMemClkCleanup(Region);
964 
965  return status;
966 }
967 
968 
969 INTSTATUS
971  _In_ void *CloakHandle,
972  _In_ DWORD Options
973  )
986 {
987  INTSTATUS status;
988 
989  if (NULL == CloakHandle)
990  {
992  }
993 
994  status = IntMemClkUncloakRegionInternal(Options, CloakHandle);
995  if (!INT_SUCCESS(status))
996  {
997  ERROR("[ERROR] IntMemClkUncloakRegionInternal failed: 0x%08x\n", status);
998  }
999 
1000  return status;
1001 }
1002 
1003 
1004 INTSTATUS
1006  _In_ QWORD VirtualAddress,
1007  _In_ QWORD PhysicalAddress,
1008  _In_ DWORD Size,
1009  _Out_ DWORD *Crc32
1010  )
1025 {
1026  INTSTATUS status;
1027  QWORD virtPage;
1028  LIST_ENTRY *list;
1029  DWORD pageOffset, intOffset, size;
1030  static BYTE pPageContent[PAGE_SIZE];
1031 
1032  if ((VirtualAddress & PAGE_OFFSET) + Size > PAGE_SIZE)
1033  {
1035  }
1036 
1037  if (NULL == Crc32)
1038  {
1040  }
1041 
1042  virtPage = VirtualAddress & PAGE_MASK;
1043 
1044  status = IntPhysicalMemRead(PhysicalAddress & PHYS_PAGE_MASK, PAGE_SIZE, pPageContent, NULL);
1045  if (!INT_SUCCESS(status))
1046  {
1047  ERROR("[ERROR] IntPhysMemRead failed: 0x%08x\n", status);
1048  return status;
1049  }
1050 
1051  list = gMemClkRegions.Flink;
1052  while (list != &gMemClkRegions)
1053  {
1054  PMEMCLOAK_REGION pReg = CONTAINING_RECORD(list, MEMCLOAK_REGION, Link);
1055 
1056  list = list->Flink;
1057 
1058  if ((pReg->Gva >= virtPage) && (pReg->Gva < virtPage + PAGE_SIZE))
1059  {
1060  // The cloaked region is contained within the page or starts within this page.
1061  pageOffset = pReg->Gva & PAGE_OFFSET;
1062  intOffset = 0;
1063  size = MIN(PAGE_SIZE - pageOffset, pReg->Size);
1064  }
1065  else if ((virtPage >= pReg->Gva) && (virtPage < pReg->Gva + pReg->Size))
1066  {
1067  // The cloaked region contains the page or parts of the page.
1068  pageOffset = 0;
1069  intOffset = (DWORD)(virtPage - pReg->Gva);
1070  size = MIN(PAGE_SIZE, pReg->Size - intOffset);
1071  }
1072  else
1073  {
1074  // No overlap, continue.
1075  continue;
1076  }
1077 
1078  memcpy(&pPageContent[pageOffset], &pReg->OriginalData[intOffset], size);
1079  }
1080 
1081  *Crc32 = Crc32ComputeFast(&pPageContent[VirtualAddress & PAGE_OFFSET], Size, 0);
1082 
1083  return status;
1084 
1085 }
1086 
1087 
1088 BOOLEAN
1090  _In_ const void *Cloak,
1091  _In_ QWORD Ptr
1092  )
1102 {
1103  const MEMCLOAK_REGION *pReg;
1104 
1105  if (NULL == Cloak)
1106  {
1107  return FALSE;
1108  }
1109 
1110  pReg = Cloak;
1111  if (Ptr >= pReg->Gva && Ptr < pReg->Gva + pReg->Size)
1112  {
1113  return TRUE;
1114  }
1115 
1116  return FALSE;
1117 }
1118 
1119 
1120 INTSTATUS
1122  _In_ void *CloakHandle,
1123  _Out_ BYTE **OriginalData,
1124  _Out_ DWORD *Length
1125  )
1140 {
1141  MEMCLOAK_REGION *pClk;
1142 
1143  if (NULL == CloakHandle)
1144  {
1146  }
1147 
1148  if (NULL == OriginalData)
1149  {
1151  }
1152 
1153  if (NULL == Length)
1154  {
1156  }
1157 
1158  pClk = (MEMCLOAK_REGION *)CloakHandle;
1159 
1160  *OriginalData = pClk->OriginalData;
1161 
1162  *Length = pClk->Size;
1163 
1164  return INT_STATUS_SUCCESS;
1165 }
1166 
1167 
1168 INTSTATUS
1170  void
1171  )
1183 {
1184  INTSTATUS status;
1185  LIST_ENTRY *list;
1186 
1187  list = gMemClkRegions.Flink;
1188  while (list != &gMemClkRegions)
1189  {
1190  PMEMCLOAK_REGION pClkReg = CONTAINING_RECORD(list, MEMCLOAK_REGION, Link);
1191  list = list->Flink;
1192 
1193  ERROR("[ERROR] There should be no memcloaks remaining... Got one on %llx, %llx, %d!\n",
1194  pClkReg->Gva, pClkReg->Cr3, pClkReg->Size);
1195 
1196  if (pClkReg->OriginalData)
1197  {
1198  IntDumpBuffer(pClkReg->OriginalData, 0, pClkReg->Size, 16, 1, TRUE, FALSE);
1199  }
1200 
1201  if (pClkReg->PatchedData)
1202  {
1203  IntDumpBuffer(pClkReg->PatchedData, 0, pClkReg->Size, 16, 1, TRUE, FALSE);
1204  }
1205 
1206  status = IntMemClkUncloakRegionInternal(0, pClkReg);
1207  if (!INT_SUCCESS(status))
1208  {
1209  ERROR("[ERROR] IntMemClkUncloakRegionInternal failed: 0x%08x\n", status);
1210  }
1211  }
1212 
1213  return INT_STATUS_SUCCESS;
1214 }
1215 
1216 
1217 void
1219  void
1220  )
1224 {
1225  if (IsListEmpty(&gMemClkRegions))
1226  {
1227  LOG("No cloack regions!\n");
1228  }
1229 
1230  for (LIST_ENTRY *entry = gMemClkRegions.Flink; entry != &gMemClkRegions; entry = entry->Flink)
1231  {
1232  PMEMCLOAK_REGION pClkReg = CONTAINING_RECORD(entry, MEMCLOAK_REGION, Link);
1233 
1234  LOG("Region @ [0x%016llx, 0x%016llx) with Cr3 = 0x%016llx. Page count: %d. Options: 0x%08x\n",
1235  pClkReg->Gva, pClkReg->Gva + pClkReg->Size,
1236  pClkReg->Cr3, pClkReg->PageCount, pClkReg->Options);
1237  LOG("Original data:\n");
1238  IntDumpBuffer(pClkReg->OriginalData, pClkReg->Gva, pClkReg->Size, 16, 1, TRUE, TRUE);
1239  LOG("Patched data:\n");
1240  IntDumpBuffer(pClkReg->PatchedData, pClkReg->Gva, pClkReg->Size, 16, 1, TRUE, TRUE);
1241 
1242  for (DWORD i = 0; i < pClkReg->PageCount; i++)
1243  {
1244  PMEMCLOAK_PAGE pClkPage = &pClkReg->Pages[i];
1245 
1246  NLOG("\t\tData offset: 0x%08x Start: 0x%08x End: 0x%08x\n",
1247  pClkPage->DataOffset, pClkPage->PageStartOffset, pClkPage->PageEndOffset);
1248  }
1249  }
1250 }
1251 
DWORD Options
A combination of MEMCLOAK_OPTIONS values.
Definition: memcloak.c:37
#define _In_opt_
Definition: intro_sal.h:16
#define INT_STATUS_PAGE_NOT_PRESENT
Indicates that a virtual address is not present.
Definition: introstatus.h:438
#define _Out_
Definition: intro_sal.h:22
_Bool BOOLEAN
Definition: intro_types.h:58
#define CONTAINING_RECORD(List, Type, Member)
Definition: introlists.h:36
MEMCLOAK_PAGE Pages[MEMCLOACK_PAGE_MAX_COUNT]
Array of pages contained in this region.
Definition: memcloak.c:44
INTSTATUS IntPhysicalMemRead(QWORD PhysicalAddress, DWORD Length, void *Buffer, DWORD *RetLength)
Reads data from a guest physical memory range, but only for a single page.
Definition: introcore.c:721
DWORD PageStartOffset
Offset at which the data starts in this page.
Definition: memcloak.c:19
uint8_t BYTE
Definition: intro_types.h:47
Read-access hook.
Definition: glueiface.h:298
INTSTATUS IntKernVirtMemWrite(QWORD KernelGva, DWORD Length, void *Buffer)
Writes data to a guest kernel virtual memory range.
Definition: introcore.c:699
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 CLEAN_PHYS_ADDRESS64(x)
Definition: pgtable.h:119
QWORD SystemCr3
The Cr3 used to map the kernel.
Definition: guests.h:211
#define INT_STATUS_SUCCESS
Definition: introstatus.h:54
void * ReadHook
The read hook handle.
Definition: memcloak.c:22
#define PAGE_REMAINING(addr)
Definition: pgtable.h:163
INTSTATUS(* PFUNC_IntMemCloakWriteHandle)(void *Hook, QWORD Address, QWORD RegionVirtualAddress, void *CloakHandle, INTRO_ACTION *Action)
The type of custom write handlers that can be used by cloak regions.
Definition: memcloak.h:41
INTSTATUS IntMemClkModifyPatchedData(void *CloakHandle, DWORD Offset, DWORD Size, const void *Data)
Modifies the patched data inside the guest memory.
Definition: memcloak.c:795
#define IntEnterDebugger()
Definition: introcore.h:373
struct _LIST_ENTRY * Flink
Definition: introlists.h:20
#define INT_SUCCESS(Status)
Definition: introstatus.h:42
static BOOLEAN IsListEmpty(const LIST_ENTRY *ListHead)
Definition: introlists.h:78
INTSTATUS IntResumeVcpus(void)
Resumes the VCPUs previously paused with IntPauseVcpus.
Definition: introcore.c:2355
#define PAGE_OFFSET
Definition: pgtable.h:32
Used by GVA hooks.
Definition: hook.h:18
INTSTATUS IntVirtMemWrite(QWORD Gva, DWORD Length, QWORD Cr3, void *Buffer)
Writes data to a guest virtual memory range.
Definition: introcore.c:652
DWORD PageEndOffset
Offset at which the data ends in this page.
Definition: memcloak.c:20
struct _MEMCLOAK_REGION * PMEMCLOAK_REGION
BOOLEAN IntMemClkIsPtrInCloak(const void *Cloak, QWORD Ptr)
Checks if a guest virtual address is located inside a cloak region.
Definition: memcloak.c:1089
#define INT_STATUS_NOT_NEEDED_HINT
Definition: introstatus.h:317
#define ERROR(fmt,...)
Definition: glue.h:62
static INTSTATUS IntMemClkHandleRead(MEMCLOAK_PAGE *Context, PHOOK_GPA Hook, QWORD Gpa, INTRO_ACTION *Action)
Handles reads from a hidden memory region.
Definition: memcloak.c:197
LIST_ENTRY Link
Entry inside the gMemClkRegions linked list.
Definition: memcloak.c:33
QWORD Gla
The guest linear address for which the buffer is filled.
Definition: guests.h:40
#define HpAllocWithTag(Len, Tag)
Definition: glue.h:516
#define PHYS_PAGE_MASK
Definition: pgtable.h:38
int INTSTATUS
The status data type.
Definition: introstatus.h:24
INTSTATUS IntMemClkUnInit(void)
Uninits the memory cloak subsystem.
Definition: memcloak.c:1169
void * SwapHook
The swap handle.
Definition: memcloak.c:21
INTSTATUS IntPhysicalMemWrite(QWORD PhysicalAddress, DWORD Length, void *Buffer)
Writes data to a guest physical memory range, but only for a single page.
Definition: introcore.c:744
static INTSTATUS IntMemClkHandleWrite(PMEMCLOAK_PAGE *Context, void *Hook, QWORD Address, INTRO_ACTION *Action)
Handles writes done inside a hidden memory region.
Definition: memcloak.c:467
BOOLEAN Valid
True if Data is valid, False if it is not.
Definition: guests.h:42
INTSTATUS IntPauseVcpus(void)
Pauses all the guest VCPUs.
Definition: introcore.c:2320
INSTRUX Instruction
The current instruction, pointed by the guest RIP.
Definition: guests.h:88
PBYTE OriginalData
A buffer containing the original data.
Definition: memcloak.c:38
#define MIN(a, b)
Definition: introdefs.h:146
Will write the contents of the patched data inside the guest.
Definition: memcloak.h:54
#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
DWORD AccessSize
The size of the memory access. Valid only for EPT exits.
Definition: guests.h:103
#define HOOK_FLG_HIGH_PRIORITY
If flag is set, the callback associated to this hook will have a higher priority than the others...
Definition: hook.h:54
Allows the code inside the region to modify the region.
Definition: memcloak.h:53
BYTE HookType
The type of the hook structure (see _HOOK_TYPE)
Definition: hook.h:68
struct _MEMCLOAK_PAGE MEMCLOAK_PAGE
A structure that describes a hidden guest memory page.
#define _Inout_
Definition: intro_sal.h:20
DWORD Size
The size of the hidden region. OriginalData and PatchedData have this size.
Definition: memcloak.c:36
DWORD DataOffset
Offset inside the data buffer.
Definition: memcloak.c:18
QWORD Cr3
The Cr3 in which the hidden region is mapped.
Definition: memcloak.c:35
uint8_t * PBYTE
Definition: intro_types.h:47
static INTSTATUS IntMemClkUncloakRegionInternal(DWORD Options, PMEMCLOAK_REGION Region)
Removes a cloak region, making the original memory contents available again to the guest...
Definition: memcloak.c:885
static BOOLEAN RemoveEntryList(LIST_ENTRY *Entry)
Definition: introlists.h:87
INTSTATUS IntMemClkGetOriginalData(void *CloakHandle, BYTE **OriginalData, DWORD *Length)
Returns the original data of a cloaked region.
Definition: memcloak.c:1121
unsigned long long QWORD
Definition: intro_types.h:53
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
HOOK_HEADER Header
The hook header.
Definition: hook_gva.h:20
#define TRUE
Definition: intro_types.h:30
#define INT_STATUS_INVALID_PARAMETER_4
Definition: introstatus.h:71
void IntMemClkCleanup(MEMCLOAK_REGION *Region)
Frees any resources held by a MEMCLOAK_REGION.
Definition: memcloak.c:521
INTSTATUS IntMemClkModifyOriginalData(void *CloakHandle, DWORD Offset, DWORD Size, void *Data)
Modifies the internal copy of the original data buffer held by a cloak region.
Definition: memcloak.c:734
#define IC_TAG_MCBF
MemCloak original buffer.
Definition: memtags.h:58
DWORD PageCount
The number of valid entries in the Pages array.
Definition: memcloak.c:45
TIMER_FRIENDLY void IntDumpBuffer(const void *Buffer, QWORD Gva, DWORD Length, DWORD RowLength, DWORD ElementLength, BOOLEAN LogHeader, BOOLEAN DumpAscii)
This function dumps a given buffer in a user friendly format.
Definition: dumper.c:48
#define HpFreeAndNullWithTag(Add, Tag)
Definition: glue.h:517
#define TRACE(fmt,...)
Definition: glue.h:58
struct _MEMCLOAK_PAGE * PMEMCLOAK_PAGE
struct _MEMCLOAK_REGION MEMCLOAK_REGION
A structure that describes a hidden guest memory region.
INTSTATUS IntMemClkCloakRegion(QWORD VirtualAddress, QWORD Cr3, DWORD Size, DWORD Options, PBYTE OriginalData, PBYTE PatchedData, PFUNC_IntMemCloakWriteHandle WriteHandler, void **CloakHandle)
Hides a memory zone from the guest.
Definition: memcloak.c:548
QWORD Gva
The guest virtual address at which the hidden region starts.
Definition: memcloak.c:34
Contains information about the patch buffer.
Definition: guests.h:38
A structure that describes a hidden guest memory region.
Definition: memcloak.c:31
PFUNC_IntMemCloakWriteHandle WriteHandler
The write handler used for this region.
Definition: memcloak.c:50
static void InsertTailList(LIST_ENTRY *ListHead, LIST_ENTRY *Entry)
Definition: introlists.h:135
#define WARNING(fmt,...)
Definition: glue.h:60
#define MEMCLOACK_PAGE_MAX_COUNT
The maximum number of pages that can be contained in a MEMCLOAK_REGION.
Definition: memcloak.c:27
INTSTATUS IntHookGvaRemoveHook(HOOK_GVA **Hook, DWORD Flags)
Remove a GVA hook.
Definition: hook_gva.c:507
static INTSTATUS IntMemClkHandleSwap(MEMCLOAK_PAGE *Context, QWORD VirtualAddress, QWORD OldEntry, QWORD NewEntry, QWORD OldPageSize, QWORD NewPageSize)
Handles swap-in and swap-outs performed on hidden memory regions.
Definition: memcloak.c:64
#define PAGE_SIZE
Definition: common.h:70
void IntMemClkDump(void)
Dumps all the active cloak regions.
Definition: memcloak.c:1218
uint32_t DWORD
Definition: intro_types.h:49
enum _INTRO_ACTION INTRO_ACTION
Event actions.
No access type. This can be used for swap hooks.
Definition: glueiface.h:297
#define IntDbgEnterDebugger()
Definition: introcore.h:381
MM Mm
Guest memory information, such as paging mode, system Cr3 value, etc.
Definition: guests.h:374
INTSTATUS IntDecDecodeSourceLinearAddressFromInstruction(PINSTRUX Instrux, PIG_ARCH_REGS Registers, QWORD *LinearAddress)
Decode the source memory linear address.
Definition: decoder.c:1149
PATCH_BUFFER PatchBuffer
The patch buffer used to emulate reads.
Definition: guests.h:168
GUEST_STATE gGuest
The current guest state.
Definition: guests.c:50
INTSTATUS IntVirtMemRead(QWORD Gva, DWORD Length, QWORD Cr3, void *Buffer, DWORD *RetLength)
Reads data from a guest virtual memory range.
Definition: introcore.c:627
void * WriteHook
The write hook handle.
Definition: memcloak.c:23
DWORD Crc32ComputeFast(const void *Buffer, size_t Size, DWORD InitialCrc)
Definition: crc32.c:136
#define INT_STATUS_NO_MAPPING_STRUCTURES
Indicates that not all mapping structures of a virtual address are present.
Definition: introstatus.h:434
#define NLOG(fmt,...)
Definition: glue.h:43
#define LIST_HEAD_INIT(Name)
Definition: introlists.h:39
PBYTE PatchedData
A buffer containing the data patched by introcore.
Definition: memcloak.c:39
DWORD Size
The valid size of the Data buffer.
Definition: guests.h:41
#define INT_STATUS_INVALID_PARAMETER_8
Definition: introstatus.h:83
#define INT_STATUS_INVALID_PARAMETER_1
Definition: introstatus.h:62
#define INT_STATUS_NOT_SUPPORTED
Definition: introstatus.h:287
#define IC_TAG_MCRG
MemCloak region.
Definition: memtags.h:57
INTSTATUS IntMemClkUncloakRegion(void *CloakHandle, DWORD Options)
Removes a cloak region, making the original memory contents available again to the guest...
Definition: memcloak.c:970
VCPU_STATE * gVcpu
The state of the current VCPU.
Definition: guests.c:59
#define INT_STATUS_FORCE_ACTION_ON_BETA
Definition: introstatus.h:370
void * Region
The parent MEMCLOAK_REGION region.
Definition: memcloak.c:24
Holds register state.
Definition: glueiface.h:30
char CHAR
Definition: intro_types.h:56
Write-access hook.
Definition: glueiface.h:299
#define PAGE_MASK
Definition: pgtable.h:35
BYTE Data[ND_MAX_REGISTER_SIZE]
The actual contents of the buffer.
Definition: guests.h:43
#define INT_STATUS_INVALID_PARAMETER_2
Definition: introstatus.h:65
A structure that describes a hidden guest memory page.
Definition: memcloak.c:16
INTSTATUS IntMemClkHashRegion(QWORD VirtualAddress, QWORD PhysicalAddress, DWORD Size, DWORD *Crc32)
Hashes the contents of a cloaked memory page.
Definition: memcloak.c:1005
#define FALSE
Definition: intro_types.h:34
static LIST_HEAD gMemClkRegions
A list containing all the memory regions that are currently hidden from the guest.
Definition: memcloak.c:54
#define INT_STATUS_INSUFFICIENT_RESOURCES
Definition: introstatus.h:281
#define INT_STATUS_INVALID_PARAMETER_3
Definition: introstatus.h:68