Bitdefender Hypervisor Memory Introspection
vecore.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 Bitdefender
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 #include "vecore.h"
6 #include "winagent.h"
7 #include "alerts.h"
8 #include "crc32.h"
9 #include "decoder.h"
10 #include "hook.h"
11 #include "kernvm.h"
12 #include "loader.h"
13 #include "memcloak.h"
14 #include "winagent_ve_x64.h"
15 #include "winapi.h"
16 #include "winpe.h"
17 #include "winpower.h"
18 #include "ptfilter.h"
19 
91 
92 
93 #define VE_DRV_NAME u"#VE Agent"
94 #define VE_DRV_PATH VE_DRV_NAME
95 
98 
100 
106 
112 
113 
114 
118 struct
119 {
123 
124 
125 static INTSTATUS
127  _Inout_ QWORD *KiKernelExit
128  );
129 
130 static INTSTATUS
132  _In_ DWORD CpuNumber,
133  _In_ QWORD VeInfoPageGva
134  );
135 
136 static void
138  void
139  );
140 
141 #define VE_TRAMPO_SIZE 24
142 
143 
144 
145 static void
147  _In_ DWORD CpuNumber
148  )
154 {
155  INSTRUX ix;
156  PVCPU_STATE vcpu = &gGuest.VcpuArray[CpuNumber];
157  QWORD veInfoGpa;
158  BOOLEAN c;
159  VA_TRANSLATION tr;
160  CHAR text[ND_MIN_BUF_SIZE];
161 
162  if (vcpu->VeInfoPage == NULL)
163  {
164  LOG("NO #VE info page on CPU %d!\n", CpuNumber);
165  return;
166  }
167 
168  LOG("**** #VE info page on CPU %d\n", vcpu->Index);
169  LOG(" Reason = 0x%08x, Reserved = 0x%08x, Qualification = 0x%016llx\n",
171  LOG(" GLA = 0x%016llx, GPA = 0x%016llx, EPT = 0x%016llx, Reserved = 0x%016llx\n",
173  vcpu->VeInfoPage->EptpIndex, vcpu->VeInfoPage->Reserved2);
174  LOG(" RAX = 0x%016llx RCX = 0x%016llx RDX = 0x%016llx RBX = 0x%016llx\n",
177  LOG(" RSP = 0x%016llx RBP = 0x%016llx RSI = 0x%016llx RDI = 0x%016llx\n",
180  LOG(" R8 = 0x%016llx R9 = 0x%016llx R10 = 0x%016llx R11 = 0x%016llx\n",
181  vcpu->VeInfoPage->Registers.R8, vcpu->VeInfoPage->Registers.R9,
183  LOG(" R12 = 0x%016llx R13 = 0x%016llx R14 = 0x%016llx R15 = 0x%016llx\n",
186  LOG(" RIP = 0x%016llx FLG = 0x%016llx CS = 0x%016llx SS = 0x%016llx\n",
188  vcpu->VeInfoPage->Registers.CS, vcpu->VeInfoPage->Registers.SS);
189  LOG(" CR0 = 0x%016llx CR3 = 0x%016llx CR4 = 0x%016llx DR7 = 0x%016llx\n",
192  LOG(" OLD = 0x%016llx NEW = 0x%016llx RSPN = %p RSPO = %p\n",
194  vcpu->VeInfoPage->OriginalStack);
195 
197 
198  LOG("**** Instruction stream:\n");
199  IntDisasmGva(vcpu->VeInfoPage->Registers.RIP, 0x100);
200 
201  LOG("**** Instruction in cache:\n");
202  NdToText(&ix, vcpu->VeInfoPage->Registers.RIP, ND_MIN_BUF_SIZE, text);
203  LOG("0x%016llx -> %s\n", vcpu->VeInfoPage->Registers.RIP, text);
204 
205  LOG("**** VE core state:\n");
206 
209 
210  LOG("*** Translation for GLA 0x%016llx\n", vcpu->VeInfoPage->GuestLinearAddress);
211  LOG("CR3 = 0x%016llx", vcpu->VeInfoPage->Registers.CR3);
212  LOG("PML4e = 0x%016llx", tr.MappingsEntries[0]);
213  LOG("PDPe = 0x%016llx", tr.MappingsEntries[1]);
214  LOG("PDe = 0x%016llx", tr.MappingsEntries[2]);
215  LOG("PTe = 0x%016llx", tr.MappingsEntries[3]);
216 
217  LOG("Self at 0x%016llx, phys at 0x%016llx, CPU %lld, current %d\n", vcpu->VeInfoPage->Self, veInfoGpa,
218  vcpu->VeInfoPage->Index, CpuNumber);
219 
221  LOG("The faulted GPA 0x%016llx is %s!\n", vcpu->VeInfoPage->GuestPhysicalAddress,
222  c ? "convertible" : "NOT CONVERTIBLE");
223 
224  IntGetGprs(CpuNumber, &gGuest.VcpuArray[CpuNumber].Regs);
225  IntDumpArchRegs(&gGuest.VcpuArray[CpuNumber].Regs);
226  LOG("RFLAGS = 0x%016llx\n", gGuest.VcpuArray[CpuNumber].Regs.Flags);
227  IntDisasmGva(gGuest.VcpuArray[CpuNumber].Regs.Rip, 0x100);
228 
229  LOG("==============================================================================================\n");
230 }
231 
232 
233 INTSTATUS
235  _In_ IG_EPT_ACCESS AccessType,
236  _Out_ INTRO_ACTION *Action
237  )
253 {
254  INTSTATUS status = INT_STATUS_SUCCESS;
255  PIG_ARCH_REGS regs = NULL;
256  INTRO_ACTION_REASON reason;
257  EXCEPTION_VICTIM_ZONE victim;
258  EXCEPTION_KM_ORIGINATOR originator;
259  PEVENT_EPT_VIOLATION pEptViol = &gAlert.Ept;
260  DWORD zoneFlags;
261  VA_TRANSLATION tr = { 0 };
262  BYTE r1, w1, x1, r2, w2, x2;
263  BOOLEAN c1, c2;
264  DWORD eptindex;
265 
266  memzero(&victim, sizeof(victim));
267  memzero(&originator, sizeof(originator));
268  memzero(pEptViol, sizeof(*pEptViol));
269 
270  LOG("[VECORE] %s took place inside the #VE agent in protected view at GLA 0x%llx, from RIP 0x%llx!\n",
271  IG_EPT_HOOK_READ == AccessType ? "Read" :
272  IG_EPT_HOOK_WRITE == AccessType ? "Write" :
273  (IG_EPT_HOOK_READ | IG_EPT_HOOK_WRITE) == AccessType ? "Read-Write" :
274  IG_EPT_HOOK_EXECUTE == AccessType ? "Execute" : "-",
275  gVcpu->Gla,
276  gVcpu->Regs.Rip);
277 
278  // The following code is used for debugging, so it should be left here for now. It helped us pinpoint a nasty race
279  // condition.
281  LOG("[VECORE] Current VCPU is %d\n", gVcpu->Index);
282  LOG("[VECORE] System CR3 = 0x%016llx, Current CR3 = 0x%016llx\n", gGuest.Mm.SystemCr3, gVcpu->Regs.Cr3);
283  LOG("[VECORE] Current EPT = %d, untrusted EPT = %d, protected EPT = %d\n", eptindex, gGuest.UntrustedEptIndex,
285 
291  LOG("[VECORE] RIP GLA 0X%016llx, GPA 0x%016llx, EPT untrusted %c%c%c/%c, EPT protected %c%c%c/%c\n",
292  gVcpu->Regs.Rip, tr.PhysicalAddress, r1 ? 'R' : '-', w1 ? 'W' : '-', x1 ? 'X' : '-', c1 ? 'C' : '-',
293  r2 ? 'R' : '-', w2 ? 'W' : '-', x2 ? 'X' : '-', c2 ? 'C' : '-');
294 
300  LOG("[VECORE] GVA GLA 0X%016llx, GPA 0x%016llx, EPT untrusted %c%c%c/%c, EPT protected %c%c%c/%c\n",
301  gVcpu->Gla, tr.PhysicalAddress, r1 ? 'R' : '-', w1 ? 'W' : '-', x1 ? 'X' : '-', c1 ? 'C' : '-',
302  r2 ? 'R' : '-', w2 ? 'W' : '-', x2 ? 'X' : '-', c2 ? 'C' : '-');
303 
306 
307  // By default we do not allow this
308  *Action = introGuestNotAllowed;
309  reason = introReasonUnknown;
310 
311  if (IG_EPT_HOOK_EXECUTE == AccessType)
312  {
313  zoneFlags = ZONE_EXECUTE;
314  }
315  else if (!!(IG_EPT_HOOK_WRITE & AccessType))
316  {
317  zoneFlags = ZONE_WRITE;
318  }
319  else if (!!(IG_EPT_HOOK_READ & AccessType))
320  {
321  zoneFlags = ZONE_READ;
322  }
323  else
324  {
325  ERROR("[ERROR] Invalid access type!");
327  }
328 
330  status = IntExceptGetVictimEpt(&gVeModule,
331  gVcpu->Gpa,
332  gVcpu->Gla,
334  zoneFlags,
335  &victim);
336  if (!INT_SUCCESS(status))
337  {
338  ERROR("[ERROR] IntExceptGetVictimEpt failed: 0x%08x\n", status);
339  reason = introReasonInternalError;
340  }
341  else
342  {
343  status = IntExceptKernelGetOriginator(&originator, 0);
344  if (INT_STATUS_EXCEPTION_BLOCK == status)
345  {
346  reason = introReasonNoException;
347  }
348  else if (!INT_SUCCESS(status))
349  {
350  ERROR("[ERROR] IntExceptKernelGetOriginator failed: 0x%08x\n", status);
351  reason = introReasonInternalError;
352  }
353  }
354 
355  IntExceptKernelLogInformation(&victim, &originator, *Action, reason);
356 
358 
359  regs = &gVcpu->Regs;
360 
361  if (IG_EPT_HOOK_EXECUTE == AccessType)
362  {
363  IntDisasmGva(victim.Ept.Gva, PAGE_SIZE - (victim.Ept.Gva & PAGE_OFFSET));
364  IntDumpArchRegs(regs);
365  }
366 
367  pEptViol->Header.Action = *Action;
368  pEptViol->Header.Reason = reason;
369  pEptViol->Header.MitreID = idRootkit;
370 
372  IntAlertEptFillFromKmOriginator(&originator, pEptViol);
373  IntAlertEptFillFromVictimZone(&victim, pEptViol);
374 
375  pEptViol->Header.Flags = IntAlertCoreGetFlags(0, reason);
378  {
379  pEptViol->Header.Flags |= ALERT_FLAG_BETA;
380  }
381 
383  IntAlertFillCodeBlocks(originator.Original.Rip, regs->Cr3, FALSE, &pEptViol->CodeBlocks);
384  IntAlertFillExecContext(0, &pEptViol->ExecContext);
385  IntAlertFillVersionInfo(&pEptViol->Header);
386 
387  status = IntNotifyIntroEvent(introEventEptViolation, pEptViol, sizeof(*pEptViol));
388  if (!INT_SUCCESS(status))
389  {
390  WARNING("[WARNING] IntNotifyIntroEvent failed: 0x%08x\n", status);
391  }
392 
393  return INT_STATUS_SUCCESS;
394 }
395 
396 
397 static INTSTATUS
399  _In_opt_ void *Context,
400  _In_ void *Hook,
401  _In_ QWORD Address,
402  _Out_ INTRO_ACTION *Action
403  )
416 {
417  INTSTATUS status = INT_STATUS_SUCCESS;
418  PEVENT_EPT_VIOLATION pEptViol = &gAlert.Ept;
419  PIG_ARCH_REGS regs = NULL;
420  EXCEPTION_VICTIM_ZONE victim;
421  EXCEPTION_KM_ORIGINATOR originator;
422  INTRO_ACTION_REASON reason;
423  DWORD zoneFlags;
424  BYTE accessType;
425 
426  UNREFERENCED_PARAMETER(Context);
428 
429  accessType = ((PHOOK_GPA)Hook)->Header.EptHookType;
430 
431  if (IG_EPT_HOOK_EXECUTE == accessType)
432  {
433  zoneFlags = ZONE_EXECUTE;
434  }
435  else if (IG_EPT_HOOK_WRITE == accessType)
436  {
437  zoneFlags = ZONE_WRITE;
438  }
439  else if (IG_EPT_HOOK_READ == accessType)
440  {
441  // IMPORTANT: We occasionally get some random reads from the \#VE agent, triggered by the nt
442  // (memcpy & friends), which are called from hal DMA related APIs. In order to avoid alert spams,
443  // we will simply block these reads, and send an alert only for debug builds. There is no point in sending
444  // alerts for reads from the \#VE agent, since this wouldn't be considered a legit attack (more like some
445  // targeted attack, but there are no secrets inside the \#VE agent, and an attacker could get its hands on the
446  // \#VE binary anyway).
447  // NOTE: since we support \#VE agent pages remapping, there's no point in sending any alerts for reads. We will
448  // simply silently block reads and do the remapping.
449  *Action = introGuestNotAllowed;
450  return INT_STATUS_SUCCESS;
451  }
452  else
453  {
454  ERROR("[ERROR] Invalid access type!");
456  }
457 
458  LOG("[VECORE] %s took place inside the #VE agent at GLA 0x%llx, from RIP 0x%llx!\n",
459  ZONE_READ == zoneFlags ? "Read" :
460  (ZONE_WRITE == zoneFlags ? "Write" :
461  (ZONE_EXECUTE == zoneFlags ? "Execute" : "-")),
462  gVcpu->Gla,
463  gVcpu->Regs.Rip);
464 
465 
466  memzero(&victim, sizeof(victim));
467  memzero(&originator, sizeof(originator));
468  memzero(pEptViol, sizeof(*pEptViol));
469 
470  // By default we do not allow this
471  *Action = introGuestNotAllowed;
472  reason = introReasonUnknown;
473 
475 
476  status = IntExceptGetVictimEpt(&gVeModule,
477  Address,
478  IntHookGetGlaFromGpaHook(Hook, Address),
480  zoneFlags,
481  &victim);
482  if (!INT_SUCCESS(status))
483  {
484  ERROR("[ERROR] IntExceptGetVictimEpt failed: 0x%08x\n", status);
485  reason = introReasonInternalError;
486  }
487  else
488  {
489  status = IntExceptKernelGetOriginator(&originator, 0);
490  if (INT_STATUS_EXCEPTION_BLOCK == status)
491  {
492  reason = introReasonNoException;
493  }
494  else if (!INT_SUCCESS(status))
495  {
496  ERROR("[ERROR] IntExceptKernelGetOriginator failed: 0x%08x\n", status);
497  reason = introReasonInternalError;
498  }
499  }
500 
501  IntExceptKernelLogInformation(&victim, &originator, *Action, reason);
502 
504 
505  regs = &gVcpu->Regs;
506 
507  if (IG_EPT_HOOK_EXECUTE == accessType)
508  {
509  IntDisasmGva(victim.Ept.Gva, PAGE_SIZE - (victim.Ept.Gva & PAGE_OFFSET));
510  IntDumpArchRegs(regs);
511  }
512 
513  pEptViol->Header.Action = *Action;
514  pEptViol->Header.Reason = reason;
515  pEptViol->Header.MitreID = idRootkit;
516 
518 
519  IntAlertEptFillFromKmOriginator(&originator, pEptViol);
520  IntAlertEptFillFromVictimZone(&victim, pEptViol);
521 
522  pEptViol->Header.Flags |= IntAlertCoreGetFlags(0, reason);
524  {
525  pEptViol->Header.Flags |= ALERT_FLAG_BETA;
526  }
527 
529 
530  IntAlertFillCodeBlocks(originator.Original.Rip, regs->Cr3, FALSE, &pEptViol->CodeBlocks);
531  IntAlertFillExecContext(0, &pEptViol->ExecContext);
532 
533  IntAlertFillVersionInfo(&pEptViol->Header);
534 
535  status = IntNotifyIntroEvent(introEventEptViolation, pEptViol, sizeof(*pEptViol));
536  if (!INT_SUCCESS(status))
537  {
538  WARNING("[WARNING] IntNotifyIntroEvent failed: 0x%08x\n", status);
539  }
540 
541  return INT_STATUS_SUCCESS;
542 }
543 
544 
545 static INTSTATUS
547  _In_ void *Context,
548  _In_ QWORD VirtualAddress,
549  _In_ QWORD OldEntry,
550  _In_ QWORD NewEntry,
551  _In_ QWORD OldPageSize,
552  _In_ QWORD NewPageSize
553  )
574 {
575  INTSTATUS status;
576  PBYTE oldPage, newPage;
577  QWORD oldAddr, newAddr;
578  BYTE r, w, x;
579 
580  oldPage = newPage = NULL;
581 
582  UNREFERENCED_PARAMETER(Context);
583  UNREFERENCED_PARAMETER(OldPageSize);
584  UNREFERENCED_PARAMETER(NewPageSize);
585 
586  LOG("[VECORE] Modified #VE agent page 0x%016llx from 0x%016llx to 0x%016llx!\n",
587  VirtualAddress,
588  OldEntry,
589  NewEntry);
590 
591  // The pages are the same, so we can safely bail out here.
592  if ((OldPageSize == NewPageSize) && !!(OldEntry & PT_P) && !!(NewEntry & PT_P) &&
593  CLEAN_PHYS_ADDRESS64(OldEntry) == CLEAN_PHYS_ADDRESS64(NewEntry))
594  {
595  return INT_STATUS_SUCCESS;
596  }
597 
598  if (!(OldEntry & PT_P) || !(NewEntry & PT_P))
599  {
600  ERROR("[ERROR] Old or new PT entry is not valid!\n");
603  }
604 
605 
606  oldAddr = (CLEAN_PHYS_ADDRESS64(OldEntry) & ~(OldPageSize - 1)) + (VirtualAddress & (OldPageSize - 1));
607  newAddr = (CLEAN_PHYS_ADDRESS64(NewEntry) & ~(NewPageSize - 1)) + (VirtualAddress & (NewPageSize - 1));
608 
609 
610  // 1. Copy the contents of the old page into the new page.
611 
612  // Map the old page.
613  status = IntPhysMemMap(oldAddr, PAGE_SIZE, 0, &oldPage);
614  if (!INT_SUCCESS(status))
615  {
616  ERROR("[ERROR] Failed mapping the old address: 0x%08x\n", status);
619  goto cleanup_and_exit;
620  }
621 
622  // Map the new page.
623  status = IntPhysMemMap(newAddr, PAGE_SIZE, 0, &newPage);
624  if (!INT_SUCCESS(status))
625  {
626  ERROR("[ERROR] Failed mapping the old address: 0x%08x\n", status);
629  goto cleanup_and_exit;
630  }
631 
632  // Copy the contents.
633  memcpy(newPage, oldPage, PAGE_SIZE);
634 
635 
636  // 2. Update the EPT access rights for the old & new pages inside the protected view.
637 
638  // Update the access rights for these new GPAs inside the protected view.
639  status = IntGetEPTPageProtection(gGuest.ProtectedEptIndex, oldAddr, &r, &w, &x);
640  if (!INT_SUCCESS(status))
641  {
642  ERROR("[ERROR] IntGetEPTPageProtection failed: 0x%08x\n", status);
645  }
646 
647  // Mark the old page as RW- inside the protected view.
648  status = IntSetEPTPageProtection(gGuest.ProtectedEptIndex, oldAddr, 1, 1, 0);
649  if (!INT_SUCCESS(status))
650  {
651  ERROR("[ERROR] IntSetEPTPageProtection failed: 0x%08x\n", status);
654  }
655 
656  // Put the required access rights into the new page now.
657  status = IntSetEPTPageProtection(gGuest.ProtectedEptIndex, newAddr, r, w, x);
658  if (!INT_SUCCESS(status))
659  {
660  ERROR("[ERROR] IntSetEPTPageProtection failed: 0x%08x\n", status);
663  }
664 
665  // 3. Move the protection to the new page - this is handled by the hook_gva swap callback. Note that the VCPUs
666  // will remain paused for ALL the swap callbacks as long as there is at least one high priority callback such as
667  // this one, so atomicity is ensured.
668 
669  // 4. Handle #VE cache remapping - we must unmap the old page and remap it to the new location.
670  for (QWORD i = 0; i < VE_CACHE_LINES; i++)
671  {
672  if (gVeCache + PAGE_SIZE * i == VirtualAddress)
673  {
674  LOG("[VECORE] Remapping the #VE cache page at index %llu\n", i);
675 
676  if (NULL != gVeCachePages[i].Page)
677  {
678  IntVirtMemUnmap(&gVeCachePages[i].Page);
679  }
680 
681  break;
682  }
683  }
684 
685  // 5. Handle #VE info page remapping by updating it inside the VMCS.
686 
687  // If a #VE info page is being remapped, make sure we update it inside the VMCS.
688  for (DWORD i = 0; i < gGuest.CpuCount; i++)
689  {
690  if (gVeInfoPages + PAGE_SIZE * (QWORD)i == VirtualAddress)
691  {
692  LOG("[VECORE] Remapping the #VE info page for VCPU %d\n", i);
693 
694  // A #VE info page is being remapped, update the VMCS.
695  status = IntVeSetVeInfoPage(i, VirtualAddress);
696  if (!INT_SUCCESS(status))
697  {
698  ERROR("[ERROR] IntVeSetVeInfoPage failed: 0x%08x\n", status);
701  }
702 
703  break;
704  }
705  }
706 
707  status = INT_STATUS_SUCCESS;
708 
709 cleanup_and_exit:
710  if (NULL != newPage)
711  {
712  IntPhysMemUnmap(&newPage);
713  }
714 
715  if (NULL != oldPage)
716  {
717  IntPhysMemUnmap(&oldPage);
718  }
719 
720  return status;
721 }
722 
723 
724 static INTSTATUS
726  void
727  )
738 {
739  INTSTATUS status = INT_STATUS_SUCCESS;
740  DWORD i = 0;
741  PIMAGE_SECTION_HEADER pSec = NULL;
742  DWORD sectionRva = 0;
743  DWORD sectionCount = 0;
744 
745  if (NULL == gVeLoadedImageBuffer)
746  {
748  }
749 
750  status = IntPeListSectionsHeaders(0, gVeLoadedImageBuffer, gVeDriverSize, &sectionRva, &sectionCount);
751  if (!INT_SUCCESS(status))
752  {
753  ERROR("[ERROR] IntPeIterateSections failed with status: 0x%08x\n", status);
754  return status;
755  }
756 
757  LOG("[VECORE] Hooking headers against any access...\n");
758 
762  PAGE_SIZE,
765  NULL, 0, NULL);
766  if (!INT_SUCCESS(status))
767  {
768  ERROR("[ERROR] IntHookObjectHookRegion failed: 0x%08x\n", status);
769  }
770 
774  PAGE_SIZE,
777  NULL, 0, NULL);
778  if (!INT_SUCCESS(status))
779  {
780  ERROR("[ERROR] IntHookObjectHookRegion failed: 0x%08x\n", status);
781  }
782 
786  PAGE_SIZE,
789  NULL, 0, NULL);
790  if (!INT_SUCCESS(status))
791  {
792  ERROR("[ERROR] IntHookObjectHookRegion failed: 0x%08x\n", status);
793  }
794 
795  pSec = (IMAGE_SECTION_HEADER *)(gVeLoadedImageBuffer + sectionRva);
796  for (i = 0; i < sectionCount; i++, pSec++)
797  {
798  LOG("[VECORE] Hooking section %d (%s) with characteristics 0x%08x against writes\n",
799  i, pSec->Name, pSec->Characteristics);
800 
807  NULL, 0, NULL);
808  if (!INT_SUCCESS(status))
809  {
810  ERROR("[ERROR] IntHookObjectHookRegion failed: 0x%08x\n", status);
811  }
812 
813  LOG("[VECORE] Hooking section %d (%s) with characteristics 0x%08x against reads\n",
814  i, pSec->Name, pSec->Characteristics);
815 
822  NULL, 0, NULL);
823  if (!INT_SUCCESS(status))
824  {
825  ERROR("[ERROR] IntHookObjectHookRegion failed: 0x%08x\n", status);
826  }
827 
828  if (0 != memcmp(pSec->Name, "VESTUB", sizeof("VESTUB")))
829  {
830  LOG("[VECORE] Hooking section %d (%s) with characteristics 0x%08x against executes\n",
831  i, pSec->Name, pSec->Characteristics);
832 
839  NULL, 0, NULL);
840  if (!INT_SUCCESS(status))
841  {
842  ERROR("[ERROR] IntHookObjectHookRegion failed: 0x%08x\n", status);
843  }
844  }
845  }
846 
847  return INT_STATUS_SUCCESS;
848 }
849 
850 
851 static INTSTATUS
853  _In_ BOOLEAN Enable
854  )
868 {
869  INTSTATUS status;
870  PIMAGE_SECTION_HEADER pSec = NULL;
871  DWORD sectionRva = 0;
872  DWORD sectionCount = 0;
873 
874  if (NULL == gVeLoadedImageBuffer)
875  {
877  }
878 
879  status = IntPeListSectionsHeaders(0, gVeLoadedImageBuffer, gVeDriverSize, &sectionRva, &sectionCount);
880  if (!INT_SUCCESS(status))
881  {
882  ERROR("[ERROR] IntPeIterateSections failed with status: 0x%08x\n", status);
883  return status;
884  }
885 
886  pSec = (IMAGE_SECTION_HEADER *)(gVeLoadedImageBuffer + sectionRva);
887  for (DWORD i = 0; i < sectionCount; i++, pSec++)
888  {
889  BYTE r, w, x;
890 
891  if (Enable)
892  {
893  r = 1;
894  w = !!(pSec->Characteristics & IMAGE_SCN_MEM_WRITE) || (0 == memcmp(pSec->Name, "VEINS", 5));
895  x = !!(pSec->Characteristics & IMAGE_SCN_MEM_EXECUTE) || (0 == memcmp(pSec->Name, "VEINS", 5));
896  }
897  else
898  {
899  r = w = 1;
900  x = 0;
901  }
902 
903  for (QWORD gva = 0; gva < pSec->Misc.VirtualSize; gva += 0x1000)
904  {
905  QWORD gpa;
906 
908  gGuest.Mm.SystemCr3, &gpa);
909  if (!INT_SUCCESS(status))
910  {
911  ERROR("[ERROR] IntTranslateVirtualAddress failed for GVA %llx: 0x%08x\n", gva, status);
912  break;
913  }
914 
915  status = IntSetEPTPageProtection(gGuest.ProtectedEptIndex, gpa, r, w, x);
916  if (!INT_SUCCESS(status))
917  {
918  ERROR("[ERROR] IntSetEPTPageProtectionEx failed for GPA %llx: 0x%08x\n", gpa, status);
919  break;
920  }
921  }
922  }
923 
924  return INT_STATUS_SUCCESS;
925 }
926 
927 
928 static INTSTATUS
930  void
931  )
942 {
943  INTSTATUS status;
944  DWORD pgCount = gVeDriverSize / PAGE_SIZE;
945 
946  gVeDriverPages = HpAllocWithTag(pgCount * sizeof(void *), IC_TAG_VEPG);
947  if (NULL == gVeDriverPages)
948  {
950  }
951 
952  LOG("Hooking PTs...\n");
953 
954  for (DWORD page = 0; page < gVeDriverSize; page += PAGE_SIZE)
955  {
956  // The SWAP hook on the #VE agent pages must be high priority, because we need to copy the real agent contents
957  // inside the new page BEFORE doing the integrity checks.
959  NULL, NULL, HOOK_FLG_HIGH_PRIORITY, (PHOOK_GVA *)&gVeDriverPages[page / PAGE_SIZE]);
960  if (!INT_SUCCESS(status))
961  {
962  ERROR("[ERROR] IntHookGvaSetHook failed: 0x%08x\n", status);
963  return status;
964  }
965  }
966 
967  return INT_STATUS_SUCCESS;
968 }
969 
970 
971 static INTSTATUS
973  void
974  )
981 {
982  INTSTATUS status;
983 
984  if (NULL == gVeDriverPages)
985  {
987  }
988 
989  for (DWORD page = 0; page < gVeDriverSize; page += PAGE_SIZE)
990  {
991  status = IntHookGvaRemoveHook((HOOK_GVA **)&gVeDriverPages[page / PAGE_SIZE], 0);
992  if (!INT_SUCCESS(status))
993  {
994  WARNING("[WARNING] IntHookGvaRemoveHook failed: 0x%08x\n", status);
995  }
996  }
997 
999 
1000  return INT_STATUS_SUCCESS;
1001 }
1002 
1003 
1004 static INTSTATUS
1006  _In_ DWORD CpuNumber,
1007  _In_ QWORD VeInfoPageGva
1008  )
1021 {
1022  INTSTATUS status;
1023  QWORD veinfoGpa;
1024 
1025  if (NULL != gGuest.VcpuArray[CpuNumber].VeInfoPage)
1026  {
1028 
1029  // This is used to disable #VE on this VCPU.
1030 #ifdef USER_MODE
1031  veinfoGpa = ~0ULL;
1032 #else
1033  veinfoGpa = 0;
1034 #endif // USER_MODE
1035 
1036  LOG("[VECORE] Setting the #VE info page on CPU %d at %llx/%llx\n", CpuNumber, VeInfoPageGva, veinfoGpa);
1037 
1038  status = IntSetVEInfoPage(CpuNumber, veinfoGpa);
1039  if (!INT_SUCCESS(status))
1040  {
1041  ERROR("[ERROR] IntSetVEInfoPage failed: 0x%08x\n", status);
1042  }
1043  }
1044 
1045  if (0 != VeInfoPageGva)
1046  {
1047  status = IntTranslateVirtualAddress(VeInfoPageGva, gGuest.Mm.SystemCr3, &veinfoGpa);
1048  if (!INT_SUCCESS(status))
1049  {
1050  ERROR("[ERROR] IntTranslateVirtualAddress failed: 0x%08x\n", status);
1051  return status;
1052  }
1053 
1054  status = IntPhysMemMap(veinfoGpa, PAGE_SIZE, 0, &gGuest.VcpuArray[CpuNumber].VeInfoPage);
1055  if (!INT_SUCCESS(status))
1056  {
1057  ERROR("[ERROR] IntPhysMemMap failed: 0x%08x\n", status);
1058  return status;
1059  }
1060 
1061  LOG("[VECORE] Setting the #VE info page on CPU %d at %llx/%llx\n", CpuNumber, VeInfoPageGva, veinfoGpa);
1062 
1063  status = IntSetVEInfoPage(CpuNumber, veinfoGpa);
1064  if (!INT_SUCCESS(status))
1065  {
1066  ERROR("[ERROR] IntSetVEInfoPage failed: 0x%08x\n", status);
1067  return status;
1068  }
1069  }
1070 
1071  return INT_STATUS_SUCCESS;
1072 }
1073 
1074 
1075 static INTSTATUS
1077  _In_ QWORD GuestVirtualAddress,
1078  _In_ DWORD AgentTag,
1079  _In_opt_ void *Context
1080  )
1090 {
1091  UNREFERENCED_PARAMETER(GuestVirtualAddress);
1092  UNREFERENCED_PARAMETER(AgentTag);
1093  UNREFERENCED_PARAMETER(Context);
1094 
1095  LOG("[VECORE] #VE loader deployed!\n");
1096 
1097  return INT_STATUS_SUCCESS;
1098 }
1099 
1100 
1101 static INTSTATUS
1103  _In_ QWORD GuestVirtualAddress,
1104  _In_ DWORD ErrorCode,
1105  _In_ DWORD AgentTag,
1106  _In_opt_ void *Context
1107  )
1123 {
1124  INTSTATUS status;
1125 
1126  UNREFERENCED_PARAMETER(Context);
1127  UNREFERENCED_PARAMETER(AgentTag);
1128  UNREFERENCED_PARAMETER(GuestVirtualAddress);
1129 
1130  if (ErrorCode != 0 || gVeLoadFailed)
1131  {
1132  ERROR("[ERROR] #VE driver injection failed with error 0x%08x, load failed: %s, will bail out.\n", ErrorCode,
1133  gVeLoadFailed ? "yes" : "no");
1134 
1136 
1138  {
1139  LOG("[INFO] Will activate INTRO_OPT_IN_GUEST_PT_FILTER because the flag has been removed "
1140  "due to INTRO_OPT_VE\n");
1141 
1143 
1145  }
1146 
1147  IntVeResetState();
1148 
1149  return INT_STATUS_SUCCESS;
1150  }
1151 
1153  gVeDeployed = TRUE;
1154 
1155  IntPauseVcpus();
1156 
1157  // Enable #VE in the hooks system. We can now handle #VEs.
1158  status = IntHookGpaEnableVe();
1159  if (!INT_SUCCESS(status))
1160  {
1161  ERROR("[ERROR] IntHookGpaEnableVe failed: 0x%08x\n", status);
1162  IntEnterDebugger();
1163  }
1164 
1165  IntResumeVcpus();
1166 
1167  LOG("[VECORE] #VE driver loaded successfully!\n");
1168 
1169  return INT_STATUS_SUCCESS;
1170 }
1171 
1172 
1173 static INTSTATUS
1175  _In_ QWORD GuestVirtualAddress,
1176  _In_ DWORD AgentTag,
1177  _In_opt_ void *Context
1178  )
1188 {
1189  UNREFERENCED_PARAMETER(GuestVirtualAddress);
1190  UNREFERENCED_PARAMETER(AgentTag);
1191  UNREFERENCED_PARAMETER(Context);
1192 
1193  // Disable #VE in the hooks system. From now on, there will be no #VEs generated.
1195 
1196  LOG("[VECORE] #VE unloader deployed!\n");
1197 
1198  return INT_STATUS_SUCCESS;
1199 }
1200 
1201 
1202 static INTSTATUS
1204  _In_ QWORD Address,
1205  _In_ QWORD Target
1206  )
1223 {
1224  INTSTATUS status;
1225  DWORD iidx = 0;
1226  BYTE buff[VE_TRAMPO_SIZE];
1227 
1228  // CALL $+4
1229  buff[iidx++] = 0xE8;
1230  buff[iidx++] = 0x03;
1231  buff[iidx++] = 0x00;
1232  buff[iidx++] = 0x00;
1233  buff[iidx++] = 0x00;
1234 
1235  // LFENCE
1236  buff[iidx++] = 0x0F;
1237  buff[iidx++] = 0xAE;
1238  buff[iidx++] = 0xE8;
1239 
1240  // MOV dword [rsp], NewHandle low
1241  buff[iidx++] = 0xC7;
1242  buff[iidx++] = 0x04;
1243  buff[iidx++] = 0x24;
1244  buff[iidx++] = (Target >> 0) & 0xFF;
1245  buff[iidx++] = (Target >> 8) & 0xFF;
1246  buff[iidx++] = (Target >> 16) & 0xFF;
1247  buff[iidx++] = (Target >> 24) & 0xFF;
1248 
1249  // MOV dword [rsp + 4], NewHandle high
1250  buff[iidx++] = 0xC7;
1251  buff[iidx++] = 0x44;
1252  buff[iidx++] = 0x24;
1253  buff[iidx++] = 0x04;
1254  buff[iidx++] = (Target >> 32) & 0xFF;
1255  buff[iidx++] = (Target >> 40) & 0xFF;
1256  buff[iidx++] = (Target >> 48) & 0xFF;
1257  buff[iidx++] = (Target >> 56) & 0xFF;
1258 
1259  buff[iidx++] = 0xC3;
1260 
1261  status = IntVirtMemSafeWrite(gGuest.Mm.SystemCr3, Address, iidx, buff, IG_CS_RING_0);
1262  if (!INT_SUCCESS(status))
1263  {
1264  ERROR("[ERROR] IntVirtMemSafeWrite failed for GVA %llx: 0x%08x\n", Address, status);
1265  }
1266 
1267  return status;
1268 }
1269 
1270 
1271 static INTSTATUS
1273  _In_ QWORD VeCoreJmpKiKernelExitAddress
1274  )
1291 {
1292  INTSTATUS status;
1293  QWORD kiKernelExit = 0;
1294 
1295  if (!gGuest.KptiActive)
1296  {
1297  // The size of this array must match the size of the trampoline code, which is 24 bytes!
1298  BYTE nopbuff[VE_TRAMPO_SIZE] =
1299  {
1300  0x90, 0x90, 0x90, 0x90,
1301  0x90, 0x90, 0x90, 0x90,
1302  0x90, 0x90, 0x90, 0x90,
1303  0x90, 0x90, 0x90, 0x90,
1304  0x90, 0x90, 0x90, 0x90,
1305  0x90, 0x90, 0x90, 0x90,
1306  };
1307 
1308  status = IntVirtMemSafeWrite(gGuest.Mm.SystemCr3, VeCoreJmpKiKernelExitAddress,
1309  sizeof(nopbuff), nopbuff, IG_CS_RING_0);
1310  if (!INT_SUCCESS(status))
1311  {
1312  ERROR("[ERROR] IntVirtMemSafeWrite failed for GVA %llx: 0x%08x\n", VeCoreJmpKiKernelExitAddress, status);
1313  }
1314 
1315  return status;
1316  }
1317 
1318  status = IntVeFindKernelKvaShadowAndKernelExit(&kiKernelExit);
1319  if (!INT_SUCCESS(status))
1320  {
1321  ERROR("[ERROR] IntVeFindKernelKvaShadowAndKernelExit failed: 0x%08x\n", status);
1322  return status;
1323  }
1324  else if (INT_STATUS_NOT_NEEDED_HINT == status)
1325  {
1326  return status;
1327  }
1328 
1329  LOG("[INFO] Found KiKernelExit @ %llx\n", kiKernelExit);
1330 
1331  return IntVePatchVeCoreJmpTrampoline(VeCoreJmpKiKernelExitAddress, kiKernelExit);
1332 }
1333 
1334 
1335 static QWORD
1337  _In_ QWORD GuestVirtualAddress,
1338  _In_ DWORD MaxSize,
1339  _In_opt_ void *Context
1340  )
1375 {
1376  INTSTATUS status = INT_STATUS_SUCCESS;
1377  DWORD rva = 0, codelen = 0;
1378  PBYTE pImage;
1379  BYTE oldcode[64];
1380  QWORD newvehnd = 0, oldvehnd = 0, relevantbits = HOOK_PTS_MONITORED_BITS, vepages, vestacks;
1381  QWORD ret = 1;
1382 
1383  UNREFERENCED_PARAMETER(Context);
1384  UNREFERENCED_PARAMETER(MaxSize);
1385 
1387  if (NULL == pImage)
1388  {
1389  return ret;
1390  }
1391 
1392  LOG("[VECORE] Delivering the #VE agent at GVA %llx...\n", GuestVirtualAddress);
1393 
1394  gVeDriverAddress = GuestVirtualAddress;
1395 
1396  IntPauseVcpus();
1397 
1398  gVeLoadedImageBuffer = pImage;
1399 
1400 
1401  // Load the image & prepare it for execution.
1402  status = IntLdrLoadPEImage(gVeDriverx64, sizeof(gVeDriverx64), GuestVirtualAddress,
1404  if (!INT_SUCCESS(status))
1405  {
1406  ERROR("[ERROR] IntLdrLoadPEImage failed: 0x%08x\n", status);
1407  goto cleanup_and_exit;
1408  }
1409 
1410 
1411  // Deploy the #VE driver inside the guest.
1412  status = IntVirtMemSafeWrite(gGuest.Mm.SystemCr3, GuestVirtualAddress, gVeDriverSize, pImage, IG_CS_RING_0);
1413  if (!INT_SUCCESS(status))
1414  {
1415  ERROR("[ERROR] IntVirtMemSafeWrite failed: 0x%08x\n", status);
1416  goto cleanup_and_exit;
1417  }
1418 
1419  LOG("[VECORE] The #VE agent was written at GVA %llx!\n", GuestVirtualAddress);
1420 
1421 
1422  // Get the VeCoreProtectedEptIndex export - this points to the "mov ecx, ept_index" instruction which must be
1423  // patched in order to reflect the actual protected EPT index (which could be any value).
1424  status = IntPeFindExportByName(GuestVirtualAddress, pImage, "VeCoreProtectedEptIndex", &rva);
1425  if (!INT_SUCCESS(status))
1426  {
1427  ERROR("[ERROR] IntPeFindExportByName failed: 0x%08x\n", status);
1428  goto cleanup_and_exit;
1429  }
1430 
1431  // Patch the "mov ecx, ept_index" instruction with the actual ept index - we will modify the immediate of the
1432  // "mov ecx" instruction.
1433  status = IntVirtMemSafeWrite(gGuest.Mm.SystemCr3, GuestVirtualAddress + rva, 4,
1435  if (!INT_SUCCESS(status))
1436  {
1437  ERROR("[ERROR] IntVirtMemSafeWrite failed: 0x%08x\n", status);
1438  goto cleanup_and_exit;
1439  }
1440 
1441  LOG("[VECORE] Patched protected EPT index %d at %llx!\n", gGuest.ProtectedEptIndex, GuestVirtualAddress + rva);
1442 
1443 
1444  // Get the VeCoreUntrustedEptIndex export - this points to the "mov ecx, ept_index" instruction which must be
1445  // patched in order to reflect the actual untrusted EPT index (which could be any value). Normally, we could use
1446  // the value saved by the CPU inside the #VE info page, but due to an errata, on Xeon Gold 5118, that value will
1447  // always be 0.
1448  status = IntPeFindExportByName(GuestVirtualAddress, pImage, "VeCoreUntrustedEptIndex", &rva);
1449  if (!INT_SUCCESS(status))
1450  {
1451  ERROR("[ERROR] IntPeFindExportByName failed: 0x%08x\n", status);
1452  goto cleanup_and_exit;
1453  }
1454 
1455  // Patch the "mov ecx, ept_index" instruction with the actual ept index - we will modify the immediate of the
1456  // "mov ecx" instruction.
1457  status = IntVirtMemSafeWrite(gGuest.Mm.SystemCr3, GuestVirtualAddress + rva, 4,
1459  if (!INT_SUCCESS(status))
1460  {
1461  ERROR("[ERROR] IntVirtMemSafeWrite failed: 0x%08x\n", status);
1462  goto cleanup_and_exit;
1463  }
1464 
1465  LOG("[VECORE] Patched untrusted EPT index %d at %llx!\n", gGuest.UntrustedEptIndex, GuestVirtualAddress + rva);
1466 
1467 
1468  // Get the VeCoreSelfMapIndex export - this points to the self-map index inside the page-tables, and we will patch
1469  // it to reflect the actual self-map index inside the PTs.
1470  status = IntPeFindExportByName(GuestVirtualAddress, pImage, "VeCoreSelfMapIndex", &rva);
1471  if (!INT_SUCCESS(status))
1472  {
1473  ERROR("[ERROR] IntPeFindExportByName failed: 0x%08x\n", status);
1474  goto cleanup_and_exit;
1475  }
1476 
1477  // Patch the VeCoreSelfMapIndex value inside the VE driver.
1478  status = IntVirtMemSafeWrite(gGuest.Mm.SystemCr3, GuestVirtualAddress + rva, 4,
1480  if (!INT_SUCCESS(status))
1481  {
1482  ERROR("[ERROR] IntVirtMemSafeWrite failed: 0x%08x\n", status);
1483  goto cleanup_and_exit;
1484  }
1485 
1486  LOG("[VECORE] Patched self-map index %d at %llx!\n", gGuest.Mm.SelfMapIndex, GuestVirtualAddress + rva);
1487 
1488 
1489  // Get the VeCoreMonitoredPtBits export -this contains the bits that we wish to monitor inside a page-table.
1490  // This value can be changed while the HVI is active in order to enable/disable hypercalls on different bits.
1491  status = IntPeFindExportByName(GuestVirtualAddress, pImage, "VeCoreMonitoredPtBits", &rva);
1492  if (!INT_SUCCESS(status))
1493  {
1494  ERROR("[ERROR] IntPeFindExportByName failed: 0x%08x\n", status);
1495  goto cleanup_and_exit;
1496  }
1497 
1498  // Patch the VeCoreMonitoredPtBits value inside the VE driver.
1499  status = IntVirtMemSafeWrite(gGuest.Mm.SystemCr3, GuestVirtualAddress + rva, 8, &relevantbits, IG_CS_RING_0);
1500  if (!INT_SUCCESS(status))
1501  {
1502  ERROR("[ERROR] IntVirtMemSafeWrite failed: 0x%08x\n", status);
1503  goto cleanup_and_exit;
1504  }
1505 
1506  LOG("[VECORE] Patched relevant-bits value 0x%016llx at %llx!\n", relevantbits, GuestVirtualAddress + rva);
1507 
1508 
1509  // Get the VeCoreJumpToKiKernelExit export - this is where the "jump to KiKernelExit" trampoline is situated,
1510  // so that we exit gracefully from the #VE handler
1511  status = IntPeFindExportByName(GuestVirtualAddress, pImage, "VeCoreJumpToKiKernelExit", &rva);
1512  if (!INT_SUCCESS(status))
1513  {
1514  ERROR("[ERROR] IntPeFindExportByName failed: 0x%08x\n", status);
1515  goto cleanup_and_exit;
1516  }
1517 
1518  // Now patch the VeCoreJumpBack code
1519  status = IntVePatchVeCoreJmpKiKernelExit(GuestVirtualAddress + rva);
1520  if (!INT_SUCCESS(status))
1521  {
1522  ERROR("[ERROR] IntVePatchVeCoreJumpBack failed: 0x%08x\n", status);
1523  goto cleanup_and_exit;
1524  }
1525 
1526  LOG("[VECORE] Patched VeCoreJumpToKiKernelExit code at %llx\n", GuestVirtualAddress + rva);
1527 
1528 
1529  // Initialize the #VE info pages & #VE stacks. Once we do this, the #VE agent is ready to accept #VEs.
1530  status = IntPeFindExportByName(GuestVirtualAddress, pImage, "VeCoreVePages", &rva);
1531  if (!INT_SUCCESS(status))
1532  {
1533  ERROR("[ERROR] IntPeFindExportByName failed for VeCoreVePages: 0x%08x\n", status);
1534  goto cleanup_and_exit;
1535  }
1536 
1537  vepages = gVeInfoPages = GuestVirtualAddress + rva;
1538 
1539  status = IntPeFindExportByName(GuestVirtualAddress, pImage, "VeCoreVeStacks", &rva);
1540  if (!INT_SUCCESS(status))
1541  {
1542  ERROR("[ERROR] IntPeFindExportByName failed VeCoreVeStacks: 0x%08x\n", status);
1543  goto cleanup_and_exit;
1544  }
1545 
1546  vestacks = GuestVirtualAddress + rva;
1547 
1548  status = IntPeFindExportByName(GuestVirtualAddress, pImage, "VeCoreCache", &rva);
1549  if (!INT_SUCCESS(status))
1550  {
1551  ERROR("[ERROR] IntPeFindExportByName failed VeCoreCache: 0x%08x\n", status);
1552  goto cleanup_and_exit;
1553  }
1554 
1555  gVeCache = GuestVirtualAddress + rva;
1556 
1557  LOG("[VECORE] #VE info pages at 0x%016llx\n", vepages);
1558  LOG("[VECORE] #VE stacks at 0x%016llx\n", vestacks);
1559  LOG("[VECORE] #VE page cache 0x%016llx\n", gVeCache);
1560 
1561 
1562  // Get the Introspection #VE handler address and hook the int 20 exception handler inside the guest.
1563  if (gGuest.OSVersion < 16299 && gGuest.KptiActive)
1564  {
1565  // Windows 7, 8, 8.1, TH1, TH2, RS1 or RS2, with KPTI - the stub is "PUSH 0x14/JMP KiIsrThunkShadow".
1567  "VeCoreVirtualizationExceptionHandlerKPTI", &rva);
1568  }
1569  else if (gGuest.OSVersion < 16299 && !gGuest.KptiActive)
1570  {
1571  // Windows 7, 8, 8.1, TH1, TH2, RS1 or RS2, without KPTI - the stub is
1572  // "PUSH 0x14/PUSH rbp/JMP KiIsrThunkShadow".
1574  "VeCoreVirtualizationExceptionHandlerNoKPTI", &rva);
1575  }
1576  else
1577  {
1578  // Windows RS3 and newer - same treatment with/without KPTI, because it is aware of the VirtualizationException.
1580  "VeCoreVirtualizationExceptionHandler", &rva);
1581  }
1582 
1583  if (!INT_SUCCESS(status))
1584  {
1585  ERROR("[ERROR] IntPeFindExportByName failed: 0x%08x\n", status);
1586  goto cleanup_and_exit;
1587  }
1588 
1589  newvehnd = gVeDriverAddress + rva;
1590 
1591  // Hook the #VE handler and make it point inside our driver.
1592  status = IntWinApiHookVeHandler(newvehnd, &gVeHandlerCloak, &oldvehnd, &codelen, oldcode);
1593  if (!INT_SUCCESS(status))
1594  {
1595  ERROR("[ERROR] IntWinApiHookVeHandler failed: 0x%08x\n", status);
1596  goto cleanup_and_exit;
1597  }
1598 
1599  LOG("[VECORE] Hooked the #VE handler, old handler at %llx, new handler at %llx!\n", oldvehnd, newvehnd);
1600 
1601 
1602  // Store the original code. We need it when we have to execute the original handler. This will usually happen on
1603  // older Windows versions, which are not aware of the Virtualization Exception, and will generically handle
1604  // int 20 as a spurious event.
1605  status = IntPeFindExportByName(GuestVirtualAddress, pImage, "VeCoreReplacedCode", &rva);
1606  if (!INT_SUCCESS(status))
1607  {
1608  ERROR("[ERROR] IntPeFindExportByName failed: 0x%08x\n", status);
1609  goto cleanup_and_exit;
1610  }
1611 
1612  // Write the original code inside the dedicated area of our #VE agent.
1613  status = IntVirtMemSafeWrite(gGuest.Mm.SystemCr3, GuestVirtualAddress + rva, codelen, oldcode, IG_CS_RING_0);
1614  if (!INT_SUCCESS(status))
1615  {
1616  ERROR("[ERROR] IntVirtMemSafeWrite failed: 0x%08x\n", status);
1617  goto cleanup_and_exit;
1618  }
1619 
1620  // Write the jump-back handler code.
1621  // Store the original code. We need it when we have to execute the original handler.
1622  status = IntPeFindExportByName(GuestVirtualAddress, pImage, "VeCoreJumpToReplacedCode", &rva);
1623  if (!INT_SUCCESS(status))
1624  {
1625  ERROR("[ERROR] IntPeFindExportByName failed: 0x%08x\n", status);
1626  goto cleanup_and_exit;
1627  }
1628 
1629  status = IntVePatchVeCoreJmpTrampoline(GuestVirtualAddress + rva, oldvehnd + codelen);
1630  if (!INT_SUCCESS(status))
1631  {
1632  ERROR("[ERROR] IntVePatchVeCoreJmpTrampoline failed: 0x%08x\n", status);
1633  goto cleanup_and_exit;
1634  }
1635 
1636  // Initialize the CPU map. We use the PCR as a key to determine the current CPU index.
1637  status = IntPeFindExportByName(GuestVirtualAddress, pImage, "VeCpuMap", &rva);
1638  if (!INT_SUCCESS(status))
1639  {
1640  ERROR("[ERROR] IntPeFindExportByName failed: 0x%08x\n", status);
1641  goto cleanup_and_exit;
1642  }
1643 
1644  for (DWORD i = 0; i < gGuest.CpuCount; i++)
1645  {
1646  // Check out which logical CPU VCPU i is inside the guest. In order to do so, we use the guest algorithm,
1647  // which is based on the limit of GDT descriptor 0x50.
1648  QWORD gdtbase, desc;
1649  WORD gdtlimit;
1650  DWORD cpuid;
1651  DWORD limit;
1652 
1653  // We could also check the IDT here, which will be at 0 (the CPU is in real mode)
1655  {
1656  TRACE("[VECORE] VCPU %u is not used by the guest, will skip it\n", i);
1657  continue;
1658  }
1659 
1660  // Get the GDT base & limit on the designated VCPU.
1661  status = IntGdtFindBase(i, &gdtbase, &gdtlimit);
1662  if (!INT_SUCCESS(status))
1663  {
1664  ERROR("[ERROR] IntGdtFindBase failed: 0x%08x\n", status);
1665  goto cleanup_and_exit;
1666  }
1667 
1668  // Make sure descriptor 0x50 is valid.
1669  if (gdtlimit < 0x57)
1670  {
1671  ERROR("[ERROR] GDT 0x%016llx on CPU %u has limit %u, which is too small!\n", gdtbase, i, gdtlimit);
1672  status = INT_STATUS_NOT_SUPPORTED;
1673  goto cleanup_and_exit;
1674  }
1675 
1676  // Read the descriptor.
1677  status = IntKernVirtMemRead(gdtbase + 0x50, 8, &desc, NULL);
1678  if (!INT_SUCCESS(status))
1679  {
1680  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x\n", status);
1681  goto cleanup_and_exit;
1682  }
1683 
1684  // Compute the segment limit.
1685  limit = ((desc & 0xFFFF) | ((desc & 0x000F000000000000) >> 32)) << ((desc & 0x80000000000000) ? 12 : 0);
1686 
1687  cpuid = ((limit & 0x3FF) << 6) | (limit >> 14);
1688 
1689  LOG("[VECORE] VCPU %d maps on guest CPU %d, GDT entry is 0x%016llx\n", i, cpuid, desc);
1690 
1691  // Write the mapping.
1692  status = IntVirtMemSafeWrite(gGuest.Mm.SystemCr3, GuestVirtualAddress + rva + cpuid * 4ull,
1693  4, &i, IG_CS_RING_0);
1694  if (!INT_SUCCESS(status))
1695  {
1696  ERROR("[ERROR] IntVirtMemSafeWrite failed: 0x%08x\n", status);
1697  goto cleanup_and_exit;
1698  }
1699  }
1700 
1701 
1702  // Now hook the #VE agent, and protect it against modifications/PT alterations.
1703 
1704  // Create a hook object for the #VE driver.
1706  if (!INT_SUCCESS(status))
1707  {
1708  ERROR("[ERROR] IntHookObjectCreate failed: 0x%08x\n", status);
1709  goto cleanup_and_exit;
1710  }
1711 
1712  // Hook the #VE driver inside the guest - we won't allow any rights to take place on it.
1713  status = IntVeHookVeDriver();
1714  if (!INT_SUCCESS(status))
1715  {
1716  ERROR("[ERROR] IntVeHookVeDriver failed: 0x%08x\n", status);
1717  goto cleanup_and_exit;
1718  }
1719 
1720  LOG("[VECORE] #VE driver hooked in guest view!\n");
1721 
1722 
1723  // Enable execute rights for the #VE inside the protected view.
1725  if (!INT_SUCCESS(status))
1726  {
1727  ERROR("[ERROR] IntVeEnableDisableDriverAccessInProtectedView failed: 0x%08x\n", status);
1728  goto cleanup_and_exit;
1729  }
1730 
1731  LOG("[VECORE] #VE driver hooked in protected view!\n");
1732 
1733 
1734  // Lock the #VE driver.
1735  status = IntVeLockDriver();
1736  if (!INT_SUCCESS(status))
1737  {
1738  ERROR("[ERROR] IntVeLockDriver failed: 0x%08x\n", status);
1739  goto cleanup_and_exit;
1740  }
1741 
1742  LOG("[VECORE] #VE driver locked in memory!\n");
1743 
1744 
1745  for (QWORD i = 0; i < gGuest.CpuCount; i++)
1746  {
1747  // Set the #VE info page for this VCPU.
1748  status = IntVeSetVeInfoPage((DWORD)i, vepages + PAGE_SIZE * i);
1749  if (!INT_SUCCESS(status))
1750  {
1751  ERROR("[ERROR] IntVeSetVeInfoPage failed: 0x%08x\n", status);
1752  goto cleanup_and_exit;
1753  }
1754 
1755  // Initialize the (few) required fields inside the #VE info page.
1757  gGuest.VcpuArray[i].VeInfoPage->Self = vepages + PAGE_SIZE * i;
1758  gGuest.VcpuArray[i].VeInfoPage->Index = i;
1759  }
1760 
1761  LOG("[VECORE] Initialized the #VE info paged at 0x%016llx, stacks at 0x%016llx!\n", vepages, vestacks);
1762 
1763 
1764  // All done!
1765 
1766  // Add BaseVa and size of #VE Agent.
1767  gVeModule.BaseVa = GuestVirtualAddress;
1768  gVeModule.Size = gVeDriverSize;
1769 
1770  status = INT_STATUS_SUCCESS;
1771 
1772  ret = 0;
1773 
1774 cleanup_and_exit:
1775  if (!INT_SUCCESS(status))
1776  {
1777  ERROR("[ERROR] Something failed during #VE load, will unload it!\n");
1778  gVeLoadFailed = TRUE;
1779  IntVeRemoveAgent(0);
1780  }
1781 
1782  IntResumeVcpus();
1783 
1784  return ret;
1785 }
1786 
1787 
1788 static INTSTATUS
1790  void
1791  )
1804 {
1805  INTSTATUS status = INT_STATUS_SUCCESS;
1806 
1807  IntPauseVcpus();
1808 
1809  for (DWORD i = 0; i < gGuest.CpuCount; i++)
1810  {
1811  status = IntVeSetVeInfoPage(i, 0);
1812  if (!INT_SUCCESS(status))
1813  {
1814  ERROR("[ERROR] IntVeSetVeInfoPage failed: 0x%08x\n", status);
1815  }
1816  }
1817 
1818  // Remove the #VE handler cloak.
1819  if (NULL != gVeHandlerCloak)
1820  {
1822  if (!INT_SUCCESS(status))
1823  {
1824  ERROR("[ERROR] IntMemClkUncloakRegion failed: 0x%08x\n", status);
1825  }
1826 
1827  gVeHandlerCloak = NULL;
1828  }
1829 
1830  // Remove the #VE agent hooks.
1831  if (NULL != gVeHookObject)
1832  {
1834  if (!INT_SUCCESS(status))
1835  {
1836  ERROR("[ERROR] IntHookObjectDestroyFastSafe failed: 0x%08x\n", status);
1837  }
1838  }
1839 
1840  LOG("[VECORE] Successfully removed agent protection in default view!\n");
1841 
1842  // Remove the #VE driver lock.
1843  status = IntVeUnlockDriver();
1844  if (!INT_SUCCESS(status))
1845  {
1846  ERROR("[ERROR] IntVeUnlockDriver failed: 0x%08x\n", status);
1847  }
1848 
1849  // Remove executable rights on the agent inside protected view.
1851  if (!INT_SUCCESS(status))
1852  {
1853  ERROR("[ERROR] IntVeEnableDisableDriverAccessInProtectedView failed: 0x%08x\n", status);
1854  }
1855 
1856  IntResumeVcpus();
1857 
1858  LOG("[VECORE] Successfully removed agent protection in protected view!\n");
1859 
1860  if (NULL != gVeLoadedImageBuffer)
1861  {
1863  }
1864 
1865  return status;
1866 }
1867 
1868 
1869 static QWORD
1871  _In_ QWORD GuestVirtualAddress,
1872  _In_ DWORD MaxSize,
1873  _In_opt_ void *Context
1874  )
1889 {
1890  INTSTATUS status;
1891 
1892  UNREFERENCED_PARAMETER(Context);
1893  UNREFERENCED_PARAMETER(MaxSize);
1894  UNREFERENCED_PARAMETER(GuestVirtualAddress);
1895 
1896  // Make sure there are no RIPs inside the agent.
1897  IntPauseVcpus();
1898 
1900  if (INT_STATUS_CANNOT_UNLOAD == status)
1901  {
1902  LOG("[WARNING] Cannot unload yet, RIPs still point inside the filter!\n");
1903  IntResumeVcpus();
1904  return 1;
1905  }
1906 
1907  status = IntVeUnhookVeAgent();
1908  if (!INT_SUCCESS(status))
1909  {
1910  ERROR("[ERROR] IntVeUnhookVeAgent failed: 0x%08x\n", status);
1911  }
1912 
1913  // Unamp the #VE cache pages.
1914  for (DWORD i = 0; i < VE_CACHE_LINES; i++)
1915  {
1916  if (NULL != gVeCachePages[i].Page)
1917  {
1918  IntVirtMemUnmap(&gVeCachePages[i].Page);
1919  }
1920  }
1921 
1922  IntResumeVcpus();
1923 
1924  LOG("[VECORE] #VE driver unloaded successfully!\n");
1925 
1926  return 0;
1927 }
1928 
1929 
1930 static void
1932  void
1933  )
1937 {
1939  gVeDriverAddress = 0;
1940  gVeInfoPages = 0;
1941  gVeCache = 0;
1942 }
1943 
1944 
1945 static INTSTATUS
1947  _In_ QWORD GuestVirtualAddress,
1948  _In_ DWORD ErrorCode,
1949  _In_ DWORD AgentTag,
1950  _In_opt_ void *Context
1951  )
1962 {
1963  INTSTATUS status;
1964 
1965  UNREFERENCED_PARAMETER(Context);
1966  UNREFERENCED_PARAMETER(AgentTag);
1967  UNREFERENCED_PARAMETER(ErrorCode);
1968  UNREFERENCED_PARAMETER(GuestVirtualAddress);
1969 
1970  IntVeResetState();
1971 
1972  LOG("[VECORE] #VE driver unloaded successfully!\n");
1973 
1974  status = IntWinPowDisableSpinWait();
1975  if (!INT_SUCCESS(status))
1976  {
1977  ERROR("[ERROR] IntWinPowDisableSpinWait failed: 0x%08x\n", status);
1978  }
1979 
1980  return INT_STATUS_SUCCESS;
1981 }
1982 
1983 
1984 INTSTATUS
1986  _In_ DWORD CpuNumber
1987  )
2006 {
2007  INTSTATUS status;
2008  PIG_ARCH_REGS regs;
2009 
2010  status = INT_STATUS_SUCCESS;
2011  regs = &gVcpu->Regs;
2012 
2013  if (regs->Rdx == VE_HCALL_NOP)
2014  {
2015  // Do nothing.
2016  }
2017  else if (regs->Rdx == VE_HCALL_BREAK)
2018  {
2019  // Break in debugger.
2020  LOG("Breaking in debugger due to #VE request, reason 0x%016llx, argument 0x%016llx.\n",
2021  regs->Rbx, regs->Rcx);
2023  LOG("==============================================================================================\n");
2024  LOG("==============================================================================================\n");
2025  LOG("==============================================================================================\n");
2026  LOG("==============================================================================================\n");
2028  LOG("==============================================================================================\n");
2029  IntEnterDebugger();
2030  }
2031  else if (regs->Rdx == VE_HCALL_TRACE)
2032  {
2033  LOG("$$$$ #VE on CPU %d/%lld/%lld, #VE info page at 0x%016llx, self at 0x%016llx, "
2034  "GLA 0x%016llx, GPA 0x%016llx, QUAL 0x%016llx\n",
2035  gVcpu->Index, regs->Rbx, regs->Rcx, gVcpu->VeInfoPage->Self, gVcpu->VeInfoPage->Index,
2038 
2039  IntVeDumpVeInfoPage(CpuNumber);
2040  }
2041  else if (regs->Rdx == VE_HCALL_RAISE_EPT)
2042  {
2043  // Invoke the #VE hypercall handler.
2044  if (NULL == gVcpu->VeInfoPage)
2045  {
2046  ERROR("[ERROR] Trying to raise EPT violation with NULL #VE info page!\n");
2047  IntEnterDebugger();
2048  }
2049 
2050  status = INT_STATUS_RAISE_EPT;
2051  }
2052  else
2053  {
2054  ERROR("[ERROR] Unknown #VE hypercall number %lld!\n", regs->Rdx);
2055  status = INT_STATUS_NOT_SUPPORTED;
2056  }
2057 
2058  return status;
2059 }
2060 
2061 
2062 INTSTATUS
2064  void
2065  )
2077 {
2078  if (!gVeVeInitialized)
2079  {
2080  WARNING("[WARNING] Cannot inject the #VE agent, because #VE is not supported!\n");
2082  }
2083 
2084  if (gGuest.UninitPrepared)
2085  {
2086  WARNING("[WARNING] Cannot inject the #VE agent, because uninit is being prepared!\n");
2088  }
2089 
2091  {
2092  WARNING("[WARNING] Cannot inject the #VE agent, because an agent is already %s!\n",
2093  gVeDeployed ? "deployed" : gVePendingDeploy ? "pending deploy" : "pending unload");
2095  }
2096 
2097  // We only support #VE on Windows x64
2099  {
2100  WARNING("[WARNING] Cannot inject the #VE agent, because it is only supported on x64 Windows!\n");
2102  }
2103 
2104  gVeDriverAddress = 0;
2105  gVeInfoPages = 0;
2106 
2108 
2111  NULL, 0, NULL, 0, NULL);
2112 }
2113 
2114 
2115 INTSTATUS
2117  _In_ DWORD AgOpts
2118  )
2130 {
2131  INTSTATUS status;
2132 
2133  if (!gVeVeInitialized)
2134  {
2136  }
2137 
2139  {
2141  }
2142 
2143  //IntVeDumpStats();
2144 
2146  {
2147  status = IntHookGpaDisableVe();
2148  if (!INT_SUCCESS(status))
2149  {
2150  ERROR("[ERROR] IntHookGpaDisableVe failed: 0x%08x\n", status);
2151  }
2152 
2153  status = IntVeUnhookVeAgent();
2154  if (!INT_SUCCESS(status))
2155  {
2156  ERROR("[ERROR] IntVeUnhookVeAgent failed: 0x%08x\n", status);
2157  }
2158 
2159  // Overwrite the whole driver with zeros.
2160  // This SHOULD be safe. We will only get here if `BugCheckInProgress` is set.
2161  // `BugCheckInProgress` will only be set if the guest reaches our hook inside
2162  // `KiDisplayBlueScreen` and by that time we *assume* that there's only one
2163  // VCPU running (with the rip inside our hook). Thus, there *shouldn't* be any
2164  // other VCPUs inside the VE agent.
2165  if (0 != gVeDriverAddress)
2166  {
2167  for (DWORD page = 0; page < gVeDriverSize; page += PAGE_SIZE)
2168  {
2169  PBYTE pMap;
2170 
2171  status = IntVirtMemMap(gVeDriverAddress + page, PAGE_SIZE, gGuest.Mm.SystemCr3, 0, &pMap);
2172  if (!INT_SUCCESS(status))
2173  {
2174  continue;
2175  }
2176 
2177  memzero(pMap, PAGE_SIZE);
2178 
2179  IntVirtMemUnmap(&pMap);
2180  }
2181  }
2182 
2183  IntVeResetState();
2184 
2185  LOG("[VECORE] #VE driver unloaded successfully!\n");
2186 
2187  return status;
2188  }
2189 
2191 
2193  NULL, gVeDriverx64, sizeof(gVeDriverx64), TRUE,
2195  NULL, AgOpts, NULL, 0, NULL);
2196 }
2197 
2198 
2199 QWORD
2201  void
2202  )
2208 {
2209  return gVeDriverAddress;
2210 }
2211 
2212 
2213 BOOLEAN
2215  _In_ QWORD Ptr,
2216  _In_ THS_PTR_TYPE Type
2217  )
2226 {
2227  if (0 == gVeDriverAddress)
2228  {
2229  return FALSE;
2230  }
2231  else
2232  {
2233  if (ptrLiveRip == Type)
2234  {
2235  QWORD vehnd;
2236 
2237  IntIdtGetEntry(IG_CURRENT_VCPU, 20, &vehnd);
2238 
2239  return (Ptr >= gVeDriverAddress && Ptr < gVeDriverAddress + gVeDriverSize) ||
2240  (Ptr >= vehnd && Ptr < vehnd + 0x80) ||
2242  }
2243  else
2244  {
2245  // Since our agent runs with interrupts disabled and on separate stack, it is safe to always return false.
2246  return FALSE;
2247  }
2248  }
2249 }
2250 
2251 
2252 BOOLEAN
2254  void
2255  )
2264 {
2265  if (0 == gVeDriverAddress)
2266  {
2267  return FALSE;
2268  }
2269 
2271 }
2272 
2273 
2274 static INTSTATUS
2276  _Inout_ QWORD *KiKernelExit
2277  )
2287 {
2288 #define MAX_INSTRUX_VE_KERNEL_OBJECTS_COUNT 1024
2289 
2290  DWORD instruxCount = 0;
2291  QWORD currentRip;
2292  BOOLEAN bIretqFound = FALSE;
2293  BOOLEAN bJmpFound = FALSE;
2294  INTSTATUS status = INT_STATUS_SUCCESS;
2295  PBYTE instruxBuffer = NULL;
2296  DWORD currentPosInBuff = 0;
2297  // keeps the current instruction size, used for going back in the buffer
2299  QWORD kernelExit = 0, kvaShadow = 0;
2300  DWORD maxInstruxCount;
2301  BOOLEAN bMovCr3 = FALSE;
2302 
2303  if (!gGuest.KptiActive)
2304  {
2306  }
2307 
2308  // Main idea: disassemble an interrupt (e.g. #PF) and before the IRETQ and swapgs find KiKvaShadow and KiKernelExit
2309  instruxBuffer = HpAllocWithTag(PAGE_SIZE, IC_TAG_ALLOC);
2310  if (NULL == instruxBuffer)
2311  {
2313  goto cleanup_and_exit;
2314  }
2315 
2316  // Read the nt!KiPageFaultShadow address
2317  status = IntIdtGetEntry(IG_CURRENT_VCPU, VECTOR_PF, &currentRip);
2318  if (!INT_SUCCESS(status))
2319  {
2320  ERROR("[ERROR] IntIdtGetEntry failed for IDT base 0x%016llx: 0x%08x\n",
2321  gVcpu->IdtBase + IDT_DESC_SIZE64 * 14, status);
2322  goto cleanup_and_exit;
2323  }
2324 
2325  status = IntKernVirtMemRead(currentRip, PAGE_SIZE, instruxBuffer, NULL);
2326  if (!INT_SUCCESS(status))
2327  {
2328  ERROR("[ERROR] Failed to read the PF Shadow buffer: 0x%08x\n", status);
2329  goto cleanup_and_exit;
2330  }
2331 
2332  while (instruxCount < MAX_INSTRUX_VE_KERNEL_OBJECTS_COUNT && currentPosInBuff + 16 < PAGE_SIZE)
2333  {
2334  INSTRUX instrux;
2335 
2336  status = IntDecDecodeInstructionFromBuffer(instruxBuffer + currentPosInBuff,
2337  PAGE_SIZE - currentPosInBuff,
2339  &instrux);
2340  if (!INT_SUCCESS(status))
2341  {
2342  ERROR("[ERROR] IntDecDecodeInstructionFromBuffer failed: 0x%08x\n", status);
2343  goto cleanup_and_exit;
2344  }
2345 
2346  if ((instrux.Instruction == ND_INS_MOV_CR) && ND_IS_OP_REG(&instrux.Operands[0], ND_REG_CR, 8, NDR_CR3))
2347  {
2348  bMovCr3 = TRUE;
2349  }
2350 
2351  // We get the first JMP after the MOV CR3 instruction from nt!KiPageFaultShadow, which will point to the
2352  // real nt!KiPageFault.
2353  if (instrux.Instruction == ND_INS_JMPNR && bMovCr3)
2354  {
2355  currentRip = currentRip + SIGN_EX(instrux.RelOffsLength, instrux.RelativeOffset) + instrux.Length;
2356  bJmpFound = TRUE;
2357  break;
2358  }
2359 
2360  instruxCount++;
2361  currentPosInBuff += instrux.Length;
2362  currentRip += instrux.Length;
2363  }
2364 
2365  if (!bJmpFound)
2366  {
2367  ERROR("[ERROR] Failed to find JMP after MOV CR3 instruction, bailing out!\n");
2368 
2369  status = INT_STATUS_NOT_FOUND;
2370  goto cleanup_and_exit;
2371  }
2372 
2373  // read the real nt!KiPageFault
2374  status = IntKernVirtMemRead(currentRip, PAGE_SIZE, instruxBuffer, NULL);
2375  if (!INT_SUCCESS(status))
2376  {
2377  ERROR("[ERROR] Failed to read the PF buffer: 0x%08x\n", status);
2378  goto cleanup_and_exit;
2379  }
2380 
2381  instruxCount = 0;
2382  currentPosInBuff = 0;
2383 
2384  while (instruxCount < MAX_INSTRUX_VE_KERNEL_OBJECTS_COUNT && currentPosInBuff + 16 < PAGE_SIZE)
2385  {
2386  INSTRUX instrux;
2387 
2388  status = IntDecDecodeInstructionFromBuffer(instruxBuffer + currentPosInBuff, PAGE_SIZE - currentPosInBuff,
2389  IG_CS_TYPE_64B, &instrux);
2390  if (!INT_SUCCESS(status))
2391  {
2392  ERROR("[ERROR] IntDecDecodeInstructionFromBuffer failed: 0x%08x\n", status);
2393  goto cleanup_and_exit;
2394  }
2395 
2396  if (instrux.Instruction == ND_INS_IRET)
2397  {
2398  bIretqFound = TRUE;
2399  break;
2400  }
2401 
2402  // We keep for each instruction the size of the instruction, so we can go back in the buffer
2403  instruxSizes[instruxCount] = instrux.Length;
2404  instruxCount++;
2405  currentPosInBuff += instrux.Length;
2406  currentRip += instrux.Length;
2407  }
2408 
2409  if (!bIretqFound)
2410  {
2411  status = INT_STATUS_NOT_FOUND;
2412  goto cleanup_and_exit;
2413  }
2414 
2415  if (instruxCount == 0)
2416  {
2417  ERROR("[ERROR] We found an IRET as the first instruction in nt!KiPageFault, will bail out!\n");
2418 
2419  status = INT_STATUS_NOT_FOUND;
2420  goto cleanup_and_exit;
2421  }
2422 
2423  maxInstruxCount = instruxCount >= 5 ? instruxCount - 5 : 0;
2424 
2425  // now we go back (by decreasing the current position in buffer and the current RIP, using the already kept
2426  // instruction size in the instruxSizes array) until we find an JMP rel instruction
2427  for (DWORD i = instruxCount - 1; i >= maxInstruxCount; i--)
2428  {
2429  INSTRUX instrux;
2430 
2431  // Go back in the buffer with current instruction length bytes
2432  currentPosInBuff -= instruxSizes[i];
2433  currentRip -= instruxSizes[i];
2434 
2435  status = IntDecDecodeInstructionFromBuffer(instruxBuffer + currentPosInBuff, PAGE_SIZE - currentPosInBuff,
2436  IG_CS_TYPE_64B, &instrux);
2437  if (!INT_SUCCESS(status))
2438  {
2439  ERROR("[ERROR] IntDecDecodeInstructionFromBuffer failed: 0x%08x\n", status);
2440  goto cleanup_and_exit;
2441  }
2442 
2443  // Before IRETQ instruction, there will always be the following pattern:
2444  // test [nt!KiKvaShadow], 1
2445  // jne _to_swapgs_and_iretq
2446  // jmp nt!KiKernelExit
2447  //_to_swapgs_and_iretq:
2448  // swapgs
2449  // iretq
2450  if (instrux.Instruction == ND_INS_JMPNR &&
2451  instrux.Operands[0].Type == ND_OP_OFFS)
2452  {
2453  kernelExit = currentRip + SIGN_EX(instrux.RelOffsLength, instrux.RelativeOffset) + instrux.Length;
2454  }
2455  else if (instrux.Instruction == ND_INS_TEST &&
2456  instrux.Operands[0].Type == ND_OP_MEM &&
2457  instrux.Operands[0].Info.Memory.IsRipRel &&
2458  instrux.Operands[1].Type == ND_OP_IMM &&
2459  instrux.Operands[1].Info.Immediate.Imm == 1)
2460  {
2461  // Note: we only get the address of KiKvaShadow for validation, we already have the KptiEnabled value
2462  // in gGuest.
2463  kvaShadow = currentRip + instrux.Operands[0].Info.Memory.Disp + instrux.Length;
2464  }
2465 
2466  if (kernelExit != 0 && kvaShadow != 0)
2467  {
2468  break;
2469  }
2470  }
2471 
2472  if (kernelExit == 0 || kvaShadow == 0)
2473  {
2474  status = INT_STATUS_NOT_FOUND;
2475  }
2476 
2477 cleanup_and_exit:
2478  if (INT_SUCCESS(status))
2479  {
2480  *KiKernelExit = kernelExit;
2481  }
2482 
2483  if (NULL != instruxBuffer)
2484  {
2485  HpFreeAndNullWithTag(&instruxBuffer, IC_TAG_ALLOC);
2486  }
2487 
2488  return status;
2489 }
2490 
2491 
2492 INTSTATUS
2494  void
2495  )
2514 {
2515  INTSTATUS status;
2516 
2517  if (gVeVeInitialized)
2518  {
2520  }
2521 
2522  if (!gGuest.SupportVE)
2523  {
2524  LOG("[INFO] No #VE support is present for guest, will not enable the #VE system!\n");
2526  }
2527 
2528  if (!gGuest.SupportVMFUNC)
2529  {
2530  LOG("[INFO] No VMFUNC support is present for guest, will not enable the #VE system!\n");
2532  }
2533 
2534  if (gGuest.CpuCount > VE_MAX_CPUS)
2535  {
2536  LOG("[INFO] The #VE agent supports max %d VCPUs, the guest currently has %d!\n", VE_MAX_CPUS, gGuest.CpuCount);
2538  }
2539 
2540  // Make sure that there are no EPT hooks set
2541  for (DWORD i = 0; i < GPA_HOOK_TABLE_SIZE; i++)
2542  {
2546  {
2548  }
2549  }
2550 
2551  // Make sure the #VE related API has been initialized by the integrator.
2552  if (!GlueIsVeApiAvailable())
2553  {
2554  TRACE("[VECORE] Required APIs not found, will not use #VE.\n");
2556  }
2557 
2558  LOG("[INFO] #VE and VMFUNC support detected, will use the #VE filtering optimization!\n");
2559 
2560  // Create the protected EPT.
2562  if (!INT_SUCCESS(status))
2563  {
2564  ERROR("[ERROR] IntCreateEPT failed: 0x%08x\n", status);
2565  goto cleanup_and_exit;
2566  }
2567 
2568  LOG("[INFO] Untrusted EPT index is: %d, Protected EPT index is: %d\n",
2570 
2571  status = IntGuestGetLastGpa(&gVeMaxGpa);
2572  if (!INT_SUCCESS(status))
2573  {
2574  ERROR("[ERROR] IntGuestGetLastGpa failed: 0x%08x\n", status);
2575  goto cleanup_and_exit;
2576  }
2577 
2578  TRACE("[VECORE] Max physical address for guest: 0x%016llx\n", gVeMaxGpa);
2579 
2580  // Protected EPT successfully created, now mark it as RW-. We want execute rights only for the agent.
2581  for (QWORD gpa = 0; gpa < gVeMaxGpa; gpa += 0x1000)
2582  {
2583  // The pages are not convertible, by default. We want to keep them that way.
2585 
2586  // Xen/KVM populate newly created EPT lazily. This means that by default, all entries inside the newly created
2587  // EPT will be 0 (not present). However, a NULL entry also has the not convertible bit cleared, which means that
2588  // #VE can be generated for that particular entry. As a result, whenever a page which hasn't been accessed yet
2589  // is read/written/executed for the first time, we will get a #VE. However, the agent cannot handle such #VEs,
2590  // which must be converted to EPT violations for the HV to handle by filling in the entry inside the EPT.
2591  // As a workaround, we can iterate the entire guest physical memory space and forcefully modify the access
2592  // rights for each physical page. This will ensure us that those pages will be committed inside the newly
2593  // created EPT, and as a result, no spurious #VEs can be generated on pages which were not yet migrated to the
2594  // new EPT.
2595  // Also note that since this code is executed during init, there are no HVI hooks set whatsoever. As a result,
2596  // we can simply mark each physical page with full rights (RWX). Devices will be filtered out by the HV.
2597  // We only do this if the untrusted EPT is not the default one (for example, we don't have to do this
2598  // on Napoca).
2599  if (gGuest.UntrustedEptIndex != 0)
2600  {
2602  }
2603  }
2604 
2605  // Get the #VE driver info.
2606  status = IntLdrGetImageSizeAndEntryPoint(gVeDriverx64, sizeof(gVeDriverx64), &gVeDriverSize, &gVeDriverEntryPoint);
2607  if (!INT_SUCCESS(status))
2608  {
2609  ERROR("[ERROR] IntLdrGetImageSizeAndEntryPoint failed: 0x%08x\n", status);
2610  goto cleanup_and_exit;
2611  }
2612 
2613  // Add relevant information to the static gVeModule
2614  memset(&gVeModule, 0, sizeof(gVeModule));
2615 
2616  gVeModule.Protected = TRUE;
2617  gVeModule.ProtectionFlag = INTRO_OPT_VE;
2618 
2619  gVeModule.NameLength = sizeof(VE_DRV_NAME) / 2 - 1;
2620  gVeModule.Name = VE_DRV_NAME;
2621  gVeModule.NameHash = kmExcNameVeAgent;
2622 
2623  gVeModule.Win.PathLength = sizeof(VE_DRV_PATH) / 2 - 1;
2624  gVeModule.Win.Path = VE_DRV_PATH;
2625  gVeModule.Win.PathHash = Crc32Compute(gVeModule.Win.Path, sizeof(VE_DRV_PATH) - 2, INITIAL_CRC_VALUE);
2626 
2627  gVeModule.Win.MzPeHeaders = gVeDriverx64;
2628 
2629  // Note that BaseVa and Size are set in IntVeDeliverDriverForLoad because at this time we don't know them
2630 
2631  LOG("[VECORE] Successfully added all the relevant info in gVeModule!\n");
2632 
2634 
2635  // All good!
2636  status = INT_STATUS_SUCCESS;
2637 
2638 cleanup_and_exit:
2639  if (!INT_SUCCESS(status))
2640  {
2642  {
2644  }
2645 
2646  // Don't destroy the untrusted EPT index, we'll continue to use it.
2647  }
2648 
2649  return status;
2650 }
2651 
2652 
2653 INTSTATUS
2655  void
2656  )
2664 {
2665  INTSTATUS status = INT_STATUS_SUCCESS;
2666 
2667  if (!gVeVeInitialized)
2668  {
2670  }
2671 
2672  // Destroy the protected EPT.
2674  {
2676  if (!INT_SUCCESS(status))
2677  {
2678  ERROR("[ERROR] IntDestroyEPT failed: 0x%08x\n", status);
2679  }
2680  }
2681 
2682  if (NULL != gVeLoadedImageBuffer)
2683  {
2685  }
2686 
2688  gVeModule.Protected = FALSE;
2689  gVeModule.ProtectionFlag = 0;
2690 
2692 
2693  return status;
2694 }
2695 
2696 
2697 void
2699  void
2700  )
2704 {
2705  if (!gVeVeInitialized || !gVeDeployed)
2706  {
2707  return;
2708  }
2709 
2710  for (DWORD cpu = 0; cpu < gGuest.CpuCount; cpu++)
2711  {
2712  IntVeDumpVeInfoPage(cpu);
2713  LOG("\n");
2714  }
2715 }
2716 
2717 
2718 void
2720  void
2721  )
2725 {
2726  extern QWORD gEptEvents;
2727  DWORD i;
2728  QWORD totalVE = 0, mmFaults = 0, ignoredFaults = 0, igpw = 0, igpt = 0, igir = 0;
2729 
2731  {
2732  return;
2733  }
2734 
2736  {
2737  LOG("[VECORE] No #VE stats to dump!\n");
2738  return;
2739  }
2740 
2741  for (i = 0; i < gGuest.CpuCount; i++)
2742  {
2743  if (NULL != gGuest.VcpuArray[i].VeInfoPage)
2744  {
2745  LOG("[VECORE] [CPU %d] We've got %llu #VE events, %llu ignored "
2746  "(%llu page-walk, %llu irrelevant, %llu cache), %f ticks/#VE\n",
2747  i,
2752 
2753  totalVE += gGuest.VcpuArray[i].VeInfoPage->VeTotal;
2754  mmFaults += gGuest.VcpuArray[i].VeInfoPage->VeMm;
2755  ignoredFaults += gGuest.VcpuArray[i].VeInfoPage->VeIgnoredTotal;
2756  igpw += gGuest.VcpuArray[i].VeInfoPage->VePageWalk;
2759  }
2760  }
2761 
2762  if (totalVE != 0)
2763  {
2764  LOG("[VECORE] Total EPT = %llu, non-#VE = %llu, #VE/PT = %llu, by MM = %llu, "
2765  "ignored = %llu - %f%% (page-walk = %llu, cache = %llu, irrel = %llu)\n",
2766  gEptEvents + totalVE, gEptEvents, totalVE, mmFaults, ignoredFaults,
2767  ignoredFaults * 100.0 / (double)totalVE, igpw, igpt, igir);
2768  }
2769 
2770  // INFO:
2771  // gEptEvents = number of clean EPT violations generated (does not include any kind of #VE)
2772  // totalFaults = total number of #VEs (by OS + by CPU page walk)
2773  // mmFaults = faults made explicitly by the OS memory manager
2774  // ignoredFaults = total number of ignored faults
2775  // igpw = ignored due to the page-walker
2776  // igpt = ignored due to the PT cache
2777  // igir = ignored due to irrelevant modification
2778  // igpw + igpt + igir = ignoredFaults
2779  // igpw + mmFaults = totalFaults
2780  // totalFaults - ignoredFaults = number of reported #VEs
2781 
2782  gEptEvents = 0;
2783 }
2784 
2785 
2786 void
2788  void
2789  )
2793 {
2795 }
2796 
2797 
2798 INTSTATUS
2800  _In_ QWORD Address,
2801  _In_ BOOLEAN Monitored
2802  )
2819 {
2820  INTSTATUS status;
2821  DWORD line, bucket, entry;
2822 
2823  // If #VE is not initialized, or it is not deployed, or it is pending unload, bail out.
2825  {
2827  }
2828 
2829  // No cache initialized - bail out.
2830  if (gVeCache == 0)
2831  {
2833  }
2834 
2835  line = VE_CACHE_GET_LINE(Address);
2836  bucket = VE_CACHE_GET_BUCKET(Address);
2837 
2838  if (NULL == gVeCachePages[line].Page)
2839  {
2840  status = IntVirtMemMap(gVeCache + (QWORD)line * PAGE_SIZE, PAGE_SIZE,
2841  gGuest.Mm.SystemCr3, 0, &gVeCachePages[line].Page);
2842  if (!INT_SUCCESS(status))
2843  {
2844  ERROR("[ERROR] IntVirtMemMap failed: 0x%08x\n", status);
2845  goto cleanup_and_exit;
2846  }
2847  }
2848 
2849  if (Monitored)
2850  {
2851  // Remove the entry from the cache.
2852  for (entry = 0; entry < VE_CACHE_ENTRIES; entry++)
2853  {
2854  if (gVeCachePages[line].Page->Entries[bucket][entry] == Address)
2855  {
2856  gVeCachePages[line].Page->Entries[bucket][entry] = 0;
2857  break;
2858  }
2859  }
2860  }
2861  else
2862  {
2863  BOOLEAN found = FALSE;
2864 
2865  // Add the entry to the cache. First make sure it isn't there already.
2866  for (entry = 0; entry < VE_CACHE_ENTRIES; entry++)
2867  {
2868  if (gVeCachePages[line].Page->Entries[bucket][entry] == Address)
2869  {
2870  found = TRUE;
2871  break;
2872  }
2873  }
2874 
2875  if (!found)
2876  {
2877  if (gVeCachePages[line].Indexes[bucket] == VE_CACHE_ENTRIES)
2878  {
2879  entry = __rdtsc() % VE_CACHE_ENTRIES;
2880  }
2881  else
2882  {
2883  entry = gVeCachePages[line].Indexes[bucket]++;
2884  }
2885 
2886  gVeCachePages[line].Page->Entries[bucket][entry] = Address;
2887  }
2888  }
2889 
2890  status = INT_STATUS_SUCCESS;
2891 
2892 cleanup_and_exit:
2893 
2894  return status;
2895 }
2896 
2897 
2898 BOOLEAN
2900  _In_ QWORD Gla
2901  )
2913 {
2914  QWORD mapBase, mapSize;
2915  QWORD pml4i, pdpi, pdi, pti, pfi;
2916 
2917  // Check if the modified page-table entry remaps any portion of the #VE agent.
2918  pml4i = PML4_INDEX(Gla);
2919  pdpi = PDP_INDEX(Gla);
2920  pdi = PD_INDEX(Gla);
2921  pti = PT_INDEX(Gla);
2922  pfi = (Gla >> 3) & 0x1ff;
2923  mapBase = 0;
2924  mapSize = 8;
2925 
2926  // We assume the SelfMapIndex will not be 0.
2927  while (pml4i == gGuest.Mm.SelfMapIndex)
2928  {
2929  pml4i = pdpi;
2930  pdpi = pdi;
2931  pdi = pti;
2932  pti = pfi;
2933  pfi = 0;
2934  mapSize <<= 9;
2935  }
2936 
2937  mapBase = (pml4i << 39) | (pdpi << 30) | (pdi << 21) | (pti << 12);
2938  mapBase |= pml4i >= 0x100 ? 0xFFFF000000000000 : 0;
2939 
2940  if ((gVeDriverAddress >= mapBase && gVeDriverAddress < mapBase + mapSize) ||
2941  (mapBase >= gVeDriverAddress && mapBase < gVeDriverAddress + gVeDriverSize))
2942  {
2943  return TRUE;
2944  }
2945 
2946  return FALSE;
2947 }
Measures kernel mode exceptions checks.
Definition: stats.h:51
#define IMAGE_SCN_MEM_EXECUTE
Definition: winpe.h:472
static INTSTATUS IntVeUnlockDriver(void)
Removes the translation hook from the VE agent.
Definition: vecore.c:972
TIMER_FRIENDLY void IntDumpArchRegs(IG_ARCH_REGS const *Registers)
This function dumps the register values in a user friendly format.
Definition: dumper.c:20
#define _In_opt_
Definition: intro_sal.h:16
INTSTATUS IntPtiInjectPtFilter(void)
Inject the PT filter inside the guest.
Definition: ptfilter.c:1676
QWORD PhysicalAddress
The physical address to which VirtualAddress translates to.
Definition: introcore.h:107
BOOLEAN gVeVeInitialized
Definition: vecore.c:99
enum _INTRO_ACTION_REASON INTRO_ACTION_REASON
The reason for which an INTRO_ACTION was taken.
QWORD Reserved2
Reserved by Intel.
Definition: vecommon.h:118
INTRO_CODEBLOCKS CodeBlocks
Code blocks extracted for the alert.
Definition: intro_types.h:1263
QWORD RSP
Definition: vecommon.h:73
QWORD VeMm
Number of VEs generated by the OS.
Definition: vecommon.h:129
#define __unlikely(x)
Definition: common.h:64
#define _Out_
Definition: intro_sal.h:22
_Bool BOOLEAN
Definition: intro_types.h:58
#define VE_CACHE_BUCKETS
64 buckets/line, indexed by bits [3, 8] inside the page-table entry address.
Definition: vecommon.h:191
#define ROUND_UP(what, to)
Definition: introdefs.h:158
QWORD OldValue
Old page-table entry.
Definition: vecommon.h:125
INTSTATUS IntVeHandleHypercall(DWORD CpuNumber)
Handles hyper calls initiated by the VE agent.
Definition: vecore.c:1985
DWORD gVeDriverSize
The driver virtual size.
Definition: vecore.c:102
#define GPA_HOOK_TABLE_SIZE
Size of the GPA hook hash.
Definition: hook_gpa.h:87
#define INTRO_OPT_VE
Enable the Virtualization exception page table access pre-filtering agent (64-bit Windows only)...
Definition: intro_types.h:475
QWORD RDX
Definition: vecommon.h:71
INTSTATUS IntVirtMemUnmap(void **HostPtr)
Unmaps a memory range previously mapped with IntVirtMemMap.
Definition: introcore.c:2234
DWORD Indexes[VE_CACHE_BUCKETS]
Array of used indexes inside the cache page.
Definition: vecore.c:121
QWORD IntVeGetDriverAddress(void)
Gets the guest virtual address of the VE agent.
Definition: vecore.c:2200
QWORD CR0
Definition: vecommon.h:88
QWORD IntAlertCoreGetFlags(QWORD ProtectionFlag, INTRO_ACTION_REASON Reason)
Returns the flags for an alert.
Definition: alerts.c:366
INTSTATUS IntGetEPTPageConvertible(DWORD EptIndex, QWORD Address, BOOLEAN *Convertible)
Definition: glue.c:1210
LIST_HEAD GpaHooksWrite[GPA_HOOK_TABLE_SIZE]
Hash table of write hooks.
Definition: hook_gpa.h:106
void ** gVeDriverPages
Swap hook handle for each VE driver page.
Definition: vecore.c:109
An internal error occurred (no memory, pages not present, etc.).
Definition: intro_types.h:195
BYTE Instruction[16]
Current instruction bytes.
Definition: vecommon.h:142
uint8_t BYTE
Definition: intro_types.h:47
Read-access hook.
Definition: glueiface.h:298
static INTSTATUS IntVeHandleAccess(void *Context, void *Hook, QWORD Address, INTRO_ACTION *Action)
Handle accesses inside the VE agent (outside the protected view).
Definition: vecore.c:398
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
IG_ARCH_REGS Regs
The current state of the guest registers.
Definition: guests.h:95
INTSTATUS IntIdtGetEntry(DWORD CpuNumber, DWORD Entry, QWORD *Handler)
Get the handler of an interrupt from the IDT.
Definition: introcpu.c:145
DWORD Index
The VCPU number.
Definition: guests.h:172
INTSTATUS IntHookGpaDisableVe(void)
Disable VE filtering.
Definition: hook_gpa.c:1430
DWORD Crc32Compute(const void *Buffer, size_t Size, DWORD InitialCrc)
Computes the CRC for a byte array.
Definition: crc32.c:103
BOOLEAN Initialized
True if the VCPU is initialized and used by the guest, False if it is not.
Definition: guests.h:199
#define _In_
Definition: intro_sal.h:21
QWORD GuestPhysicalAddress
Same as the GPA field provided on EPT Violations.
Definition: vecommon.h:116
QWORD TscTotal
Total number of CPU ticks spent inside the agent.
Definition: vecommon.h:138
MITRE_ID MitreID
The Mitre ID that corresponds to this attack.
Definition: intro_types.h:1199
#define LDR_FLAG_FIX_RELOCATIONS
If flag is set, the relocations will be applied.
Definition: loader.h:11
#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
EXCEPTION_VICTIM_EPT Ept
Valid if the modified zone is EPT.
Definition: exceptions.h:904
QWORD VeIgnoredIrrelevant
Definition: vecommon.h:135
WIN_KERNEL_DRIVER Win
Valid only for Windows guests.
Definition: drivers.h:70
uint16_t WORD
Definition: intro_types.h:48
BOOLEAN IntVeIsCurrentRipInAgent(void)
Check if the current RIP points inside the VE agent.
Definition: vecore.c:2253
The VE agent unloader.
Definition: aghcall.h:53
#define STATS_EXIT(id)
Definition: stats.h:160
INTSTATUS IntGetGprs(DWORD CpuNumber, PIG_ARCH_REGS Regs)
Get the current guest GPR state.
Definition: introcpu.c:827
#define VE_CACHE_LINES
64 cache lines (pages), indexed by bits [12, 17] inside the page-table address.
Definition: vecommon.h:189
#define IntEnterDebugger()
Definition: introcore.h:373
QWORD RAX
Definition: vecommon.h:69
INTSTATUS IntPeFindExportByName(QWORD ImageBase, BYTE *ImageBaseBuffer, CHAR *Name, DWORD *ExportRva)
Find the export name a Rva lies in.
Definition: winpe.c:1783
#define VE_STACK_SIZE
Total size of the stack used by the VE agent.
Definition: vecommon.h:154
QWORD Qualification
Same as the exit qualification provided on VM Exits.
Definition: vecommon.h:114
QWORD gVeInfoPages
Guest virtual address where the VE info pages are located.
Definition: vecore.c:105
QWORD BaseVa
The guest virtual address of the kernel module that owns this driver object.
Definition: drivers.h:41
#define VE_CACHE_ENTRIES
8 entries/bucket.
Definition: vecommon.h:193
#define IMAGE_SCN_MEM_WRITE
Definition: winpe.h:474
#define INT_SUCCESS(Status)
Definition: introstatus.h:42
QWORD NewValue
New page-table entry.
Definition: vecommon.h:126
static BOOLEAN IsListEmpty(const LIST_ENTRY *ListHead)
Definition: introlists.h:78
QWORD Flags
A combination of ALERT_FLAG_* values describing the alert.
Definition: intro_types.h:1198
INTSTATUS IntResumeVcpus(void)
Resumes the VCPUs previously paused with IntPauseVcpus.
Definition: introcore.c:2355
#define PD_INDEX(a)
Definition: pgtable.h:97
#define PAGE_OFFSET
Definition: pgtable.h:32
BOOLEAN KernelBetaDetections
True if the kernel protection is in beta (log-only) mode.
Definition: guests.h:303
DWORD PathLength
The driver`s path length (number of WCHARS).
Definition: windriver.h:26
QWORD IntHookGetGlaFromGpaHook(HOOK_GPA const *Hook, QWORD Address)
Gets the GLA from a GPA hook.
Definition: hook.c:279
BOOLEAN IntMemClkIsPtrInCloak(const void *Cloak, QWORD Ptr)
Checks if a guest virtual address is located inside a cloak region.
Definition: memcloak.c:1089
PBYTE MzPeHeaders
The driver`s MZ/PE headers (cached internally).
Definition: windriver.h:34
#define INT_STATUS_NOT_NEEDED_HINT
Definition: introstatus.h:317
QWORD R11
Definition: vecommon.h:80
#define ERROR(fmt,...)
Definition: glue.h:62
static INTSTATUS IntVeDeployUnloader(QWORD GuestVirtualAddress, DWORD AgentTag, void *Context)
Called after the boot driver (VE unloader) has been successfully injected.
Definition: vecore.c:1174
REGISTERS Registers
Offset 0x30 - 0x200, general purpose registers.
Definition: vecommon.h:120
INTSTATUS IntVeUpdateCacheEntry(QWORD Address, BOOLEAN Monitored)
Update an address inside the VE cache.
Definition: vecore.c:2799
#define SIGN_EX(sz, x)
Definition: introdefs.h:206
#define HpAllocWithTag(Len, Tag)
Definition: glue.h:516
QWORD DR7
Definition: vecommon.h:91
int INTSTATUS
The status data type.
Definition: introstatus.h:24
QWORD Size
The size of the kernel module that owns this driver object.
Definition: drivers.h:43
QWORD RIP
Definition: vecommon.h:85
static INTSTATUS IntVeHookVeDriver(void)
Protect the VE driver inside the untrusted EPT view.
Definition: vecore.c:725
BOOLEAN Protected
True if the driver is protected, False if it is not.
Definition: drivers.h:65
DWORD OSVersion
Os version.
Definition: guests.h:281
static void IntVeDumpVeInfoPage(DWORD CpuNumber)
Dump the VE info page on the provided VCPU.
Definition: vecore.c:146
#define INT_STATUS_NOT_FOUND
Definition: introstatus.h:284
BOOLEAN SupportVMFUNC
Set to True if support for VMFUNC was detected.
Definition: guests.h:356
HOOK_STATE * gHooks
Global hooks state.
Definition: hook.c:8
Rootkit.
Definition: intro_types.h:1144
Describes a kernel-mode originator.
Definition: exceptions.h:943
QWORD Flags
Definition: glueiface.h:49
PVCPU_STATE VcpuArray
Array of the VCPUs assigned to this guest. The index in this array matches the VCPU number...
Definition: guests.h:372
INTSTATUS IntPauseVcpus(void)
Pauses all the guest VCPUs.
Definition: introcore.c:2320
INTRO_GUEST_TYPE OSType
The type of the guest.
Definition: guests.h:278
void IntAlertFillCpuContext(BOOLEAN CopyInstruction, INTRO_CPUCTX *CpuContext)
Fills the current CPU context for an alert.
Definition: alerts.c:492
BOOLEAN VeAgentWaiting
True if the #VE agent was not yet injected, but it should be.
Definition: guests.h:352
INTSTATUS IntThrSafeCheckThreads(QWORD Options)
Checks if any of the guest threads have their RIP or have any stack pointers pointing to regions of c...
QWORD VeIgnoredCache
Definition: vecommon.h:133
Will write the contents of the patched data inside the guest.
Definition: memcloak.h:54
#define VE_MAX_CPUS
Currently, VE supports only 64 VCPUs max.
Definition: vecommon.h:157
#define PML4_INDEX(a)
Definition: pgtable.h:95
EVENT_EPT_VIOLATION Ept
Definition: alerts.h:16
PVECPU VeInfoPage
Pointer to the VEINFO page used for this VCPU.
Definition: guests.h:190
QWORD CR4
Definition: vecommon.h:90
static INTSTATUS IntVeUnhookVeAgent(void)
Removes the hooks placed on the VE agent.
Definition: vecore.c:1789
#define LOG(fmt,...)
Definition: glue.h:61
static INTSTATUS IntVeDeployLoader(QWORD GuestVirtualAddress, DWORD AgentTag, void *Context)
Called once the VE loaded has been injected.
Definition: vecore.c:1076
#define VE_HCALL_RAISE_EPT
Definition: vecommon.h:56
Describes a kernel driver.
Definition: drivers.h:30
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 IntVeHandleSwap(void *Context, QWORD VirtualAddress, QWORD OldEntry, QWORD NewEntry, QWORD OldPageSize, QWORD NewPageSize)
Handle VE agent page remapping.
Definition: vecore.c:546
void IntAlertFillVersionInfo(INTRO_VIOLATION_HEADER *Header)
Fills version information for an alert.
Definition: alerts.c:327
QWORD VeTotal
Total number of VEs.
Definition: vecommon.h:128
INTSTATUS IntVeInit(void)
Initialize the VE system.
Definition: vecore.c:2493
INTRO_VIOLATION_HEADER Header
The alert header.
Definition: intro_types.h:1217
BOOLEAN KptiActive
True if KPTI is enabled on this guest, False if it is not.
Definition: guests.h:291
#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
INTSTATUS IntSetEPTPageProtection(DWORD EptIndex, QWORD Gpa, BYTE Read, BYTE Write, BYTE Execute)
Definition: glue.c:672
DWORD NameHash
The hash of the name.
Definition: drivers.h:59
INTRO_ACTION_REASON Reason
The reason for which Action was taken.
Definition: intro_types.h:1195
INTSTATUS IntAlertFillCodeBlocks(QWORD Rip, QWORD Cr3, BOOLEAN Execute, INTRO_CODEBLOCKS *CodeBlocks)
Fills the code blocks pattern for an alert.
Definition: alerts.c:71
struct _HOOK_GPA * PHOOK_GPA
#define ALERT_FLAG_BETA
If set, the alert is a BETA alert. No action was taken.
Definition: intro_types.h:671
void IntAlertEptFillFromKmOriginator(const EXCEPTION_KM_ORIGINATOR *Originator, EVENT_EPT_VIOLATION *EptViolation)
Fills kernel mode originator information inside an EPT alert.
Definition: alerts.c:832
LIST_HEAD GpaHooksRead[GPA_HOOK_TABLE_SIZE]
Hash table of read hooks.
Definition: hook_gpa.h:107
QWORD R12
Definition: vecommon.h:81
GENERIC_ALERT gAlert
Global alert buffer.
Definition: alerts.c:27
#define _Inout_
Definition: intro_sal.h:20
#define INT_STATUS_CANNOT_UNLOAD
Indicates that Introcore can not unload in a safely manner.
Definition: introstatus.h:450
INTSTATUS IntGuestGetLastGpa(QWORD *MaxGpa)
Get the upper limit of the guest physical memory range.
Definition: guests.c:1735
#define VE_DRV_NAME
Definition: vecore.c:93
#define VE_CACHE_GET_LINE(x)
Definition: vecommon.h:195
#define PDP_INDEX(a)
Definition: pgtable.h:96
#define INITIAL_CRC_VALUE
Definition: introdefs.h:221
void IntVeDumpStats(void)
Dump VE statistics.
Definition: vecore.c:2719
#define INT_STATUS_EXCEPTION_BLOCK
Definition: introstatus.h:421
static void IntVeResetState(void)
Reset the VE state.
Definition: vecore.c:1931
QWORD R14
Definition: vecommon.h:83
#define INT_STATUS_ALREADY_INITIALIZED
Definition: introstatus.h:263
void IntAlertEptFillFromVictimZone(const EXCEPTION_VICTIM_ZONE *Victim, EVENT_EPT_VIOLATION *EptViolation)
Fills the victim information inside an EPT alert.
Definition: alerts.c:868
PBYTE ProtectedStack
Offset 0x200, the protected stack.
Definition: vecommon.h:122
#define INT_STATUS_NOT_INITIALIZED
Definition: introstatus.h:266
QWORD RBX
Definition: vecommon.h:72
#define IG_CURRENT_VCPU
For APIs that take a VCPU number as a parameter, this can be used to specify that the current VCPU sh...
Definition: glueiface.h:324
SIZE_T NameLength
The length of the Name. This is the number of characters in the Name buffer.
Definition: drivers.h:56
#define STATS_ENTER(id)
Definition: stats.h:153
static QWORD IntVeDeliverDriverForUnload(QWORD GuestVirtualAddress, DWORD MaxSize, void *Context)
Handles the unloading of the VE agent.
Definition: vecore.c:1870
INTRO_CPUCTX CpuContext
The context of the CPU that triggered the alert.
Definition: intro_types.h:1196
uint8_t * PBYTE
Definition: intro_types.h:47
QWORD MappingsEntries[MAX_TRANSLATION_DEPTH]
Contains the entry in which paging table.
Definition: introcore.h:115
QWORD TscCount
Total number of times the agent has been invoked.
Definition: vecommon.h:139
INTSTATUS IntNotifyIntroEvent(INTRO_EVENT_TYPE EventClass, void *Param, size_t EventSize)
Notifies the integrator about an introspection alert.
Definition: glue.c:1042
#define ZONE_EXECUTE
Used for execute violation.
Definition: exceptions.h:736
QWORD EptpIndex
The index of the EPT in which the fault took place.
Definition: vecommon.h:117
#define memzero(a, s)
Definition: introcrt.h:35
#define PT_P
Definition: pgtable.h:83
#define VE_CACHE_GET_BUCKET(x)
Definition: vecommon.h:196
BOOLEAN Guest64
True if this is a 64-bit guest, False if it is a 32-bit guest.
Definition: guests.h:290
INTSTATUS IntExceptGetVictimEpt(void *Context, QWORD Gpa, QWORD Gva, INTRO_OBJECT_TYPE Type, DWORD ZoneFlags, EXCEPTION_VICTIM_ZONE *Victim)
Fills an EXCEPTION_VICTIM_ZONE with relevant information from an EPT violation.
Definition: exceptions.c:742
unsigned long long QWORD
Definition: intro_types.h:53
QWORD Current
The currently used options.
Definition: guests.h:236
QWORD R10
Definition: vecommon.h:79
INTSTATUS IntPeListSectionsHeaders(QWORD ImageBase, BYTE *ImageBuffer, DWORD ImageBufferSize, DWORD *FirstSectionOffset, DWORD *SectionCount)
Will get the offset to the first section header and the number of sections from the given module...
Definition: winpe.c:473
QWORD IdtBase
Original IDT base.
Definition: guests.h:110
Structure encapsulating VCPU-specific information.
Definition: guests.h:83
INTSTATUS IntTranslateVirtualAddress(QWORD Gva, QWORD Cr3, QWORD *PhysicalAddress)
Translates a guest virtual address to a guest physical address.
Definition: introcore.c:1999
DWORD gVeDriverEntryPoint
The driver entry point (RVA).
Definition: vecore.c:103
#define TRUE
Definition: intro_types.h:30
UINT32 VirtualAddress
Definition: winpe.h:85
INTSTATUS IntDecDecodeInstructionFromBuffer(PBYTE Buffer, size_t BufferSize, IG_CS_TYPE CsType, void *Instrux)
Decode an instruction from the provided buffer.
Definition: decoder.c:308
QWORD RDI
Definition: vecommon.h:76
#define INVALID_EPTP_INDEX
Definition: glue.h:66
#define VE_HCALL_BREAK
Definition: vecommon.h:40
QWORD Gpa
The accessed guest physical address. Valid only for EPT exits.
Definition: guests.h:101
The VE agent loader.
Definition: aghcall.h:52
INTSTATUS IntHookGpaEnableVe(void)
Enable VE filtering.
Definition: hook_gpa.c:1406
#define TRACE(fmt,...)
Definition: glue.h:58
#define HpFreeAndNullWithTag(Add, Tag)
Definition: glue.h:517
#define INT_STATUS_INVALID_INTERNAL_STATE
Definition: introstatus.h:272
void * Name
The name of the driver.
Definition: drivers.h:54
BOOLEAN gVeLoadFailed
Definition: vecore.c:99
PBYTE gVeLoadedImageBuffer
Contains the loaded #VE module, relocated and such.
Definition: vecore.c:104
void IntExceptKernelLogInformation(EXCEPTION_VICTIM_ZONE *Victim, EXCEPTION_KM_ORIGINATOR *Originator, INTRO_ACTION Action, INTRO_ACTION_REASON Reason)
Print the information about a kernel-mode violation and dumps the code-blocks.
static INTSTATUS IntVeLockDriver(void)
Monitors all the VE agent pages against translation modifications.
Definition: vecore.c:929
union _IMAGE_SECTION_HEADER::@214 Misc
static INTSTATUS IntVeEnableDisableDriverAccessInProtectedView(BOOLEAN Enable)
Protect the VE driver inside the protected EPT view.
Definition: vecore.c:852
#define VE_DRV_PATH
Definition: vecore.c:94
QWORD GuestLinearAddress
Same as the GLA field provided on EPT Violations.
Definition: vecommon.h:115
The RIP of a thread.
INTSTATUS IntTranslateVirtualAddressEx(QWORD Gva, QWORD Cr3, DWORD Flags, VA_TRANSLATION *Translation)
Translates a guest virtual address to a guest physical address.
Definition: introcore.c:1863
INTRO_EXEC_CONTEXT ExecContext
Information about the instruction that triggered the alert.
Definition: intro_types.h:1309
QWORD Self
Pointer to self.
Definition: vecommon.h:144
QWORD R8
Definition: vecommon.h:77
#define INT_STATUS_ALREADY_INITIALIZED_HINT
Definition: introstatus.h:323
#define IC_TAG_VEPG
#VE agent pages
Definition: memtags.h:117
QWORD gEptEvents
Definition: callbacks.c:29
BOOLEAN UninitPrepared
Definition: guests.h:320
#define WARNING(fmt,...)
Definition: glue.h:60
DWORD SelfMapIndex
The self map index.
Definition: guests.h:220
QWORD RBP
Definition: vecommon.h:74
INTSTATUS IntHookGvaRemoveHook(HOOK_GVA **Hook, DWORD Flags)
Remove a GVA hook.
Definition: hook_gva.c:507
Sent when an EPT violation triggers an alert. See EVENT_EPT_VIOLATION.
Definition: intro_types.h:84
HOOK_GPA_STATE GpaHooks
GPA hooks state.
Definition: hook.h:92
PWCHAR Path
The driver`s path.
Definition: windriver.h:28
#define IDT_DESC_SIZE64
The size of a 64-bit interrupt descriptor.
Definition: wddefs.h:32
QWORD CR3
Definition: vecommon.h:89
INTSTATUS IntWinAgentInject(PFUNC_AgentInjection InjectionCallback, PFUNC_AgentCompletion CompletionCallback, PFUNC_AgentDeliver DeliverCallback, void *Context, PBYTE AgentContent, DWORD AgentSize, BOOLEAN AgentInternal, DWORD AgentTag, AGENT_TYPE AgentType, const CHAR *Name, DWORD Options, const CHAR *Args, DWORD Pid, PWIN_AGENT *Agent)
Schedule an agent injection inside the guest.
Definition: winagent.c:2608
static INTSTATUS IntVePatchVeCoreJmpTrampoline(QWORD Address, QWORD Target)
Patches the VE trampoline inside the guest VE handler.
Definition: vecore.c:1203
DWORD CpuCount
The number of logical CPUs.
Definition: guests.h:279
INTSTATUS IntVirtMemSafeWrite(QWORD Cr3, QWORD VirtualAddress, DWORD Size, void *Buffer, DWORD Ring)
Safely modify guest memory.
Definition: kernvm.c:498
#define PAGE_SIZE
Definition: common.h:70
INTSTATUS IntVeUnInit(void)
Uninit the VE system.
Definition: vecore.c:2654
Describes the modified zone.
Definition: exceptions.h:893
#define UNREFERENCED_PARAMETER(P)
Definition: introdefs.h:29
#define PT_INDEX(a)
Definition: pgtable.h:98
The Virtualization exception agent injected inside the guest.
Definition: intro_types.h:259
uint32_t DWORD
Definition: intro_types.h:49
BYTE IG_EPT_ACCESS
Definition: glueiface.h:303
UINT32 VirtualSize
Definition: winpe.h:83
INTSTATUS IntSetVEInfoPage(DWORD CpuNumber, QWORD VeInfoGpa)
Definition: glue.c:1153
BOOLEAN gVeDeployed
Definition: vecore.c:99
QWORD ProtectionFlag
The introcore option that decided that this driver must be protected.
Definition: drivers.h:49
QWORD SS
Definition: vecommon.h:92
UINT32 Characteristics
Definition: winpe.h:92
DWORD ProtectedEptIndex
The EPTP index of the trusted EPT.
Definition: guests.h:401
void * gVeHandlerCloak
Cloak handle used to hide the guest VE handler.
Definition: vecore.c:108
QWORD R13
Definition: vecommon.h:82
enum _INTRO_ACTION INTRO_ACTION
Event actions.
static INTSTATUS IntVePatchVeCoreJmpKiKernelExit(QWORD VeCoreJmpKiKernelExitAddress)
This function patches the VE code responsible of jumping to the KiKernelExit routine.
Definition: vecore.c:1272
static uint64_t __rdtsc(void)
Definition: intrinsics.h:306
BOOLEAN VeInitialized
Set to True if #VE initialization was done.
Definition: guests.h:353
#define THS_CHECK_VEFILTER
Will check if any RIP is inside the VE filter agent.
QWORD Rip
The RIP from where the call to the exported function came.
Definition: exceptions.h:950
QWORD VeIgnoredTotal
Definition: vecommon.h:131
QWORD gVeMaxGpa
Maximum GPA accessible to the guest.
Definition: vecore.c:110
QWORD Gva
The modified guest virtual address.
Definition: exceptions.h:761
QWORD gVeDriverAddress
The guest virtual address where the driver was deployed.
Definition: vecore.c:101
No access type. This can be used for swap hooks.
Definition: glueiface.h:297
BOOLEAN gVePendingUnload
Definition: vecore.c:99
INTSTATUS IntGetEPTPageProtection(DWORD EptIndex, QWORD Gpa, BYTE *Read, BYTE *Write, BYTE *Execute)
Definition: glue.c:659
INTSTATUS IntDestroyEPT(DWORD EptIndex)
Definition: glue.c:1182
struct @259 gVeCachePages[VE_CACHE_LINES]
static INTSTATUS IntVeFindKernelKvaShadowAndKernelExit(QWORD *KiKernelExit)
Searches for the KvaShadow and KiKernelExit.
Definition: vecore.c:2275
__must_check INTSTATUS IntVirtMemMap(QWORD Gva, DWORD Length, QWORD Cr3, DWORD Flags, void **HostPtr)
Maps a guest virtual memory range inside Introcore virtual address space.
Definition: introcore.c:2134
MM Mm
Guest memory information, such as paging mode, system Cr3 value, etc.
Definition: guests.h:374
INTSTATUS IntLdrLoadPEImage(PBYTE RawPe, DWORD RawPeSize, QWORD GuestVirtualAddress, PBYTE LoadedPe, DWORD VirtualPeSize, DWORD Flags)
Load the provided PE image at the provided guest virtual address, and return it in LoadedPe...
Definition: loader.c:670
GUEST_STATE gGuest
The current guest state.
Definition: guests.c:50
INTSTATUS IntLdrGetImageSizeAndEntryPoint(PBYTE RawPe, DWORD RawSize, DWORD *VirtualSize, DWORD *EntryPoint)
Returns the entry point and the virtual size for the provided module.
Definition: loader.c:11
void IntAlertFillWinProcessByCr3(QWORD ProcessCr3, INTRO_PROCESS *EventProcess)
Saves information about a Windows process inside an alert. The process is searched by its kernel CR3...
Definition: alerts.c:756
THS_PTR_TYPE
The type of pointer to be checked.
BOOLEAN IntVeIsPtrInAgent(QWORD Ptr, THS_PTR_TYPE Type)
Check if an address points inside the VE agent.
Definition: vecore.c:2214
INTSTATUS IntWinPowDisableSpinWait(void)
This function is called in order to disable spin waiting after everything we needed to be unloaded wa...
Definition: winpower.c:170
LIST_HEAD GpaHooksExecute[GPA_HOOK_TABLE_SIZE]
Hash table of execute hooks.
Definition: hook_gpa.h:108
#define IC_TAG_ALLOC
Memory allocation.
Definition: memtags.h:28
BOOLEAN IntVeIsAgentRemapped(QWORD Gla)
Checks if a given guest linear address belongs to the VE agent.
Definition: vecore.c:2899
QWORD CS
Definition: vecommon.h:86
DWORD Reserved
Definition: vecommon.h:111
#define THS_CHECK_ONLY
Will check for safeness, without moving any RIP or stack value.
struct _EXCEPTION_KM_ORIGINATOR::@64 Original
INTRO_ACTION Action
The action that was taken as the result of this alert.
Definition: intro_types.h:1194
INTSTATUS IntKernVirtMemRead(QWORD KernelGva, DWORD Length, void *Buffer, DWORD *RetLength)
Reads data from a guest kernel virtual memory range.
Definition: introcore.c:674
QWORD RFLAGS
Definition: vecommon.h:87
#define INT_STATUS_RAISE_EPT
Raises an EPT event. Can be used to treat another event as an EPT violation.
Definition: introstatus.h:382
#define ZONE_READ
Used for read violation.
Definition: exceptions.h:735
#define VECTOR_PF
Definition: processor.h:116
PBYTE OriginalStack
Offset 0x208, the original stack.
Definition: vecommon.h:123
BOOLEAN SupportVE
Set to True if support for #VE was detected.
Definition: guests.h:355
#define HOOK_PTS_MONITORED_BITS
Definition: hook_pts.h:19
static INTSTATUS IntVeSetVeInfoPage(DWORD CpuNumber, QWORD VeInfoPageGva)
Sets the VE info page on the provided VCPU.
Definition: vecore.c:1005
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
QWORD VePageWalk
Number of VEs generated by the CPU page-walker.
Definition: vecommon.h:130
__must_check INTSTATUS IntPhysMemMap(QWORD PhysAddress, DWORD Length, DWORD Flags, void **HostPtr)
Maps a guest physical address inside Introcore VA space.
Definition: glue.c:338
static KERNEL_DRIVER gVeModule
Indicate the #VE agent state.
Definition: vecore.c:97
#define INT_STATUS_NOT_INITIALIZED_HINT
Definition: introstatus.h:320
Encapsulates information about a virtual to physical memory translation.
Definition: introcore.h:102
INTSTATUS IntCreateEPT(DWORD *EptIndex)
Definition: glue.c:1168
#define INT_STATUS_INVALID_PARAMETER_1
Definition: introstatus.h:62
#define INT_STATUS_NOT_SUPPORTED
Definition: introstatus.h:287
BOOLEAN GlueIsVeApiAvailable(void)
Checks if the virtualization exception API is implemented.
Definition: glue.c:1261
INTRO_PROCESS CurrentProcess
The current process.
Definition: intro_types.h:1197
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
VE_CACHE_LINE * Page
Mapped page inside Introspection virtual address space.
Definition: vecore.c:120
The action was blocked because there was no exception for it.
Definition: intro_types.h:189
The Virtualization exception driver.
Definition: glueiface.h:353
UINT8 Name[IMAGE_SIZEOF_SHORT_NAME]
Definition: winpe.h:79
static QWORD IntVeDeliverDriverForLoad(QWORD GuestVirtualAddress, DWORD MaxSize, void *Context)
Initializes the VE driver agent inside the guest.
Definition: vecore.c:1336
QWORD RSI
Definition: vecommon.h:75
64-bit selector.
Definition: glueiface.h:188
void IntVeHandleGuestResumeFromSleep(void)
Simply set the VeAgentWaiting variable to true if VE is enabled.
Definition: vecore.c:2787
INTSTATUS IntVeRemoveAgent(DWORD AgOpts)
Removes the VE agent from guest memory.
Definition: vecore.c:2116
#define VE_HCALL_TRACE
Definition: vecommon.h:49
INTSTATUS IntGdtFindBase(DWORD CpuNumber, QWORD *GdtBase, WORD *GdtLimit)
Returns the GDT base and limit for a guest CPU.
Definition: introcpu.c:206
BOOLEAN gVePendingDeploy
Definition: vecore.c:99
INTSTATUS IntVeDeployAgent(void)
Inject the VE agent inside the guest.
Definition: vecore.c:2063
Holds register state.
Definition: glueiface.h:30
Event structure for EPT violations.
Definition: intro_types.h:1215
INTSTATUS IntGetCurrentEptIndex(DWORD CpuNumber, DWORD *EptpIndex)
Get the EPTP index of the currently loaded EPT.
Definition: introcpu.c:1238
BOOLEAN BugCheckInProgress
Definition: guests.h:333
static INTSTATUS IntVeCompleteUnloader(QWORD GuestVirtualAddress, DWORD ErrorCode, DWORD AgentTag, void *Context)
Finishes the unload procedure, by resetting the state and the power-state spin wait.
Definition: vecore.c:1946
void IntDisasmGva(QWORD Gva, DWORD Length)
This function disassembles a code buffer (given its GVA) and then dumps the instructions (textual dis...
Definition: dumper.c:432
static INTSTATUS IntVeCompleteLoader(QWORD GuestVirtualAddress, DWORD ErrorCode, DWORD AgentTag, void *Context)
Called once the VE loader has finished execution.
Definition: vecore.c:1102
#define ALERT_FLAG_PROTECTED_VIEW
Definition: intro_types.h:685
BOOLEAN PtFilterFlagRemoved
Set to True if the INTRO_OPT_IN_GUEST_PT_FILTER was given, but it was removed.
Definition: guests.h:365
Execute-access hook.
Definition: glueiface.h:300
#define VE_TRAMPO_SIZE
Definition: vecore.c:141
INTSTATUS IntWinApiHookVeHandler(QWORD NewHandler, void **Cloak, QWORD *OldHandler, DWORD *ReplacedCodeLen, BYTE *ReplacedCode)
Hooks the #VE handler.
Definition: winapi.c:367
QWORD RCX
Definition: vecommon.h:70
char CHAR
Definition: intro_types.h:56
#define INTRO_OPT_IN_GUEST_PT_FILTER
Enable in-guest page-table filtering (64-bit Windows only).
Definition: intro_types.h:461
void * gVeHookObject
Hook object containing VE agent protection in the untrusted EPT.
Definition: vecore.c:107
DWORD PathHash
CRC32 hash value for the driver`s path.
Definition: windriver.h:25
QWORD gVeCache
The VE page-table cache.
Definition: vecore.c:111
#define MAX_INSTRUX_VE_KERNEL_OBJECTS_COUNT
The name is the #VE Agent.
Definition: exceptions.h:649
Write-access hook.
Definition: glueiface.h:299
INTSTATUS IntPhysMemUnmap(void **HostPtr)
Unmaps an address previously mapped with IntPhysMemMap.
Definition: glue.c:396
INTSTATUS IntVeHandleEPTViolationInProtectedView(IG_EPT_ACCESS AccessType, INTRO_ACTION *Action)
Handle an EPT violation inside the protected EPT view.
Definition: vecore.c:234
DWORD UntrustedEptIndex
The EPTP index of the untrusted EPT.
Definition: guests.h:397
INTSTATUS IntExceptKernelGetOriginator(EXCEPTION_KM_ORIGINATOR *Originator, DWORD Options)
This function is used to get the information about the kernel-mode originator.
INTSTATUS IntAlertFillExecContext(QWORD Cr3, INTRO_EXEC_CONTEXT *ExecContext)
Fills the current execution context.
Definition: alerts.c:31
#define ZONE_WRITE
Used for write violation.
Definition: exceptions.h:734
#define INT_STATUS_INVALID_PARAMETER_2
Definition: introstatus.h:65
INTRO_PROT_OPTIONS CoreOptions
The activation and protection options for this guest.
Definition: guests.h:271
QWORD Gla
The accessed guest virtual address. Valid only for EPT exits.
Definition: guests.h:102
INTSTATUS IntHookObjectCreate(DWORD ObjectType, QWORD Cr3, void **Object)
Create a new hook object.
Definition: hook_object.c:81
DWORD Reason
Same as the basic VM Exit reason.
Definition: vecommon.h:110
QWORD R15
Definition: vecommon.h:84
QWORD R9
Definition: vecommon.h:78
#define VE_HCALL_NOP
Definition: vecommon.h:31
#define FALSE
Definition: intro_types.h:34
#define INT_STATUS_INSUFFICIENT_RESOURCES
Definition: introstatus.h:281
void IntVeDumpVeInfoPages(void)
Dumps the VE info pages on all VCPUs.
Definition: vecore.c:2698
QWORD Index
VCPU index.
Definition: vecommon.h:145