Bitdefender Hypervisor Memory Introspection
callbacks.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 Bitdefender
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 #include "callbacks.h"
6 #include "decoder.h"
7 #include "gpacache.h"
8 #include "hook.h"
9 #include "hook_cr.h"
10 #include "hook_dtr.h"
11 #include "hook_msr.h"
12 #include "hook_xcr.h"
13 #include "memtables.h"
14 #include "ptfilter.h"
15 #include "rtlpvirtualunwind.h"
16 #include "swapmem.h"
17 #include "vecore.h"
18 #include "winprocesshp.h"
19 #include "winselfmap.h"
20 #include "wininfinityhook.h"
21 #include "exceptions.h"
22 #include "wincmdline.h"
23 #include "scan_engines.h"
24 #include "wintoken.h"
25 
29 
31 
32 
33 static BOOLEAN
35  _In_ PHOOK_GPA Hook
36  )
49 {
50  INTSTATUS status;
51  PHOOK_HEADER header;
52  PHOOK_GVA hookGva;
53  VA_TRANSLATION tr;
54 
55  header = (PHOOK_HEADER)Hook->Header.ParentHook;
56 
57  if (header == NULL)
58  {
59  // No parent, this is a plain GPA hook (page table, most likely).
60  return TRUE;
61  }
62 
63  if (header->HookType != hookTypeGva)
64  {
65  // Not a GVA hook, there's nothing to translate.
66  return TRUE;
67  }
68 
69  hookGva = (PHOOK_GVA)header;
70 
71  // The CR3 used when the GVA hook was placed should be used for translation. This is needed because we may have
72  // shared memory scenarios:
73  // Process 1: GVA_1 via CR3_1 => GPA
74  // Process 2: GVA_2 via CR3_2 => GPA
75  // If we get an exit from Process 2, trying to translate GVA_1 through CR3_2 will fail, so use CR3_1, to make sure
76  // it yields the expected GPA.
77  status = IntTranslateVirtualAddressEx(hookGva->GvaPage, hookGva->PtsHook->Cr3, 0, &tr);
78  if (!INT_SUCCESS(status) || 0 == (tr.Flags & PT_P))
79  {
80  // We couldn't translate this page - this may happen if the entry was invalidated due to a partial write
81  // (on PAE x86), and in the meantime, an exit was triggered in that GPA.
82  // In this case, we trigger the error if the PTS entry is NOT in a partial write state.
83  if (hookGva->PtsHook->Parent->WriteState.WrittenMask == 0)
84  {
85  ERROR("[ERROR] Failed translating GVA 0x%016llx (reported GLA 0x%016llx, GPA 0x%016llx). "
86  "Int entry: 0x%016llx, Real entry: 0x%016llx, error: 0x%08x\n",
87  hookGva->GvaPage, gVcpu->Gla, gVcpu->Gpa,
88  hookGva->PtsHook->Parent->WriteState.IntEntry, tr.Flags, status);
89  return FALSE;
90  }
91 
92  // We can bail out now, both entries are invalid, which is good.
93  return TRUE;
94  }
95 
97  {
98  // The translation succeeded, but we obtained a different GPA than the one we just got an exit for: this means
99  // that the known GPA is outdated, and as such, we've probably missed a translation.
100  ERROR("[ERROR] Translation mismatch for GVA 0x%016llx, translated GPA 0x%016llx "
101  "(reported GLA 0x%016llx, GPA 0x%016llx)!\n",
102  hookGva->GvaPage, tr.PhysicalAddress, gVcpu->Gla, gVcpu->Gpa);
103  return FALSE;
104  }
105 
106  return TRUE;
107 }
108 
109 
110 static void
112  _In_ QWORD LinearAddress,
113  _In_ QWORD PhysicalAddress,
114  _In_ DWORD Access
115  )
128 {
129  INTSTATUS status;
130  BYTE r, w, x;
131  CHAR text[ND_MIN_BUF_SIZE];
132 
133  // We need this for logging purposes.
134  NdToText(&gVcpu->Instruction, gVcpu->Regs.Rip, ND_MIN_BUF_SIZE, text);
135 
136  r = w = x = 0;
137 
138  WARNING("[WARNING] GPA 0x%016llx, GLA 0x%016llx, was accessed with type %d, but no hooks exist on it! "
139  "CR3 0x%016llx RIP 0x%016llx %s\n",
140  PhysicalAddress, LinearAddress, Access, gVcpu->Regs.Cr3, gVcpu->Regs.Rip, text);
141 
142  status = IntGetEPTPageProtection(gVcpu->EptpIndex, PhysicalAddress, &r, &w, &x);
143  if (!INT_SUCCESS(status))
144  {
145  ERROR("[ERROR] IntGetEPTPageProtection failed for 0x%016llx: 0x%08x\n", PhysicalAddress, status);
146  }
147  else
148  {
149  TRACE("[INFO] Old access rights: %c%c%c\n", r ? 'R' : '-', w ? 'W' : '-', x ? 'X' : '-');
150 
151  if (!!(Access & IG_EPT_HOOK_EXECUTE))
152  {
153  x = 1;
154  }
155 
156  if (!!(Access & IG_EPT_HOOK_WRITE))
157  {
158  w = 1;
159  }
160 
161  if (!!(Access & IG_EPT_HOOK_READ))
162  {
163  r = 1;
164  }
165 
166  TRACE("[INFO] New access rights: %c%c%c\n", r ? 'R' : '-', w ? 'W' : '-', x ? 'X' : '-');
167 
168  // This set has the role of fooling the integrator cache - if we set the exact same rights as last time,
169  // the integrator may optimize it, by silently discarding the request. Therefore, we will set some different
170  // rights before, in order to make sure we really end up calling the HV to modify the rights again.
171  // Note that the 0, 0, 0 rights are correct - since there's at least one access type for which we didn't
172  // find a hook, this means there is at least on access right present for the page, so at least one of the
173  // R, W or X is 1. Therefore, the RWX == 000 request will most certainly be different than the current
174  // actual rights.
175  status = IntSetEPTPageProtection(gVcpu->EptpIndex, PhysicalAddress, 0, 0, 0);
176  if (!INT_SUCCESS(status))
177  {
178  ERROR("[ERROR] IntSetEPTPageProtection failed for 0x%016llx: 0x%08x\n", PhysicalAddress, status);
179  }
180 
181  // Now the actual RWX rights.
182  status = IntSetEPTPageProtection(gVcpu->EptpIndex, PhysicalAddress, r, w, x);
183  if (!INT_SUCCESS(status))
184  {
185  ERROR("[ERROR] IntSetEPTPageProtection failed for 0x%016llx: 0x%08x\n", PhysicalAddress, status);
186  }
187  }
188 }
189 
190 
191 static INTSTATUS
193  _In_ QWORD LinearAddress,
194  _In_ QWORD PhysicalAddress,
195  _In_ DWORD Length,
196  _Inout_ INTRO_ACTION *Action,
197  _Inout_ BOOLEAN *CallbackFound,
198  _Inout_ BOOLEAN *PageHooked,
199  _In_ BOOLEAN ProbeOnly,
200  _In_ IG_EPT_ACCESS AccessType
201  )
235 {
236  INTSTATUS status;
237  DWORD hid;
238  QWORD physPage;
239  LIST_ENTRY *hooks, *list;
240  INTRO_ACTION action, finalAction;
241  IG_EPT_ACCESS access;
242  STAT_ID stat, statRip;
243 
244  action = finalAction = introGuestAllowed;
245  access = AccessType;
246 
247  if (NULL == gHooks)
248  {
250  }
251 
252  gVcpu->Gpa = PhysicalAddress;
253  gVcpu->Gla = LinearAddress;
254  gVcpu->AccessSize = Length;
255 
256  // Assume no callback handled this access.
257  *CallbackFound = FALSE;
258  *PageHooked = FALSE;
259 
260  hid = GPA_HOOK_ID(PhysicalAddress);
261 
262  physPage = PhysicalAddress & PHYS_PAGE_MASK;
263 
264 handle_next_access:
265  hooks = NULL;
266 
267  if (AccessType & IG_EPT_HOOK_EXECUTE)
268  {
269  hooks = &gHooks->GpaHooks.GpaHooksExecute[hid];
270  AccessType &= ~IG_EPT_HOOK_EXECUTE;
271  stat = statsEptExecute;
272  }
273  else if (AccessType & IG_EPT_HOOK_READ)
274  {
275  hooks = &gHooks->GpaHooks.GpaHooksRead[hid];
276  AccessType &= ~IG_EPT_HOOK_READ;
277  stat = statsEptRead;
278  }
279  else if (AccessType & IG_EPT_HOOK_WRITE)
280  {
281  hooks = &gHooks->GpaHooks.GpaHooksWrite[hid];
282  AccessType &= ~IG_EPT_HOOK_WRITE;
283  stat = statsEptWrite;
284  }
285  else
286  {
288  }
289 
290  if (hooks == NULL)
291  {
293  }
294 
295  if (0 != (gVcpu->Regs.Rip & (gGuest.Guest64 ? 0x8000000000000000 : 0x80000000)))
296  {
297  statRip = statsEptKernel;
298  }
299  else
300  {
301  statRip = statsEptUser;
302  }
303 
304  STATS_ENTER(stat);
305  STATS_ENTER(statRip);
307 
308  // Xen handles events single-threaded. There's no point in complicating the event handler with a shared lock.
309  // This way, we can add new hooks from existing hooks (no problem if we add hook while we iterate the list).
310  // However, no hooks can be removed from the list - it's safe to iterate the list while new hooks may be
311  // added, since we know for sure no entry will become invalid while we have gLock acquired.
312 
313  // Try to find a suitable hook to call
314  list = hooks->Flink;
315  while (list != hooks)
316  {
317  PHOOK_GPA pHook = CONTAINING_RECORD(list, HOOK_GPA, Link);
318 
319  if (pHook->GpaPage == physPage)
320  {
321  *PageHooked = TRUE;
322 
323  // Check if write took place outside this protected region
324  if ((pHook->GpaPage + pHook->Offset >= PhysicalAddress + Length) ||
325  (pHook->GpaPage + pHook->Offset + pHook->Length <= PhysicalAddress))
326  {
327  goto _hook_continue;
328  }
329 
330  if (0 == (pHook->Header.Flags & (HOOK_FLG_REMOVE | HOOK_FLG_DISABLED)))
331  {
332 #ifdef CFG_DEBUG_EPT_VIOLATIONS
333  TRACE("[DEBUG] Calling EPT handler for GPA 0x%016llx, hook address: 0x%016llx, callback 0x%016llx\n",
334  PhysicalAddress, pHook, pHook->Callback);
335 #endif
336 
337 #ifdef CHECK_PAGE_RIGHTS
338  if (!IntValidateTranslation(pHook))
339  {
340  ERROR("[ERROR] IntValidateTranslation failed: GLA 0x%016llx, GPA 0x%016llx!\n",
341  LinearAddress, PhysicalAddress);
342  IntHookPtsDump();
343  }
344 #endif
345 
346  // Indicate that we've found a callback.
347  *CallbackFound = TRUE;
348 
349  // If we don't want to actually handle the access, bail out now and don't invoke any handlers.
350  if (ProbeOnly)
351  {
352  AccessType = 0; // Stop access handling.
353  status = INT_STATUS_SUCCESS;
354  break;
355  }
356 
357  // Pre-process all the PT writes here, before calling any callback. We will then cache the old & new
358  // values.
359  // Note: we do this only if we find a callback; otherwise, we let the hypervisor emulate the access.
360  // If we don't have at least a callback registered for a page, there's no way to know if that page is
361  // a page table or not.
362  if ((pHook->Header.EptHookType == IG_EPT_HOOK_WRITE) &&
363  !!(pHook->Header.Flags & HOOK_PAGE_TABLE_FLAGS) &&
365  {
366  QWORD oldValue, newValue;
367 
368  status = IntHookPtwEmulateWrite(PhysicalAddress);
369  if (!INT_SUCCESS(status))
370  {
371  ERROR("[ERROR] IntHookPtwEmulateWrite failed: 0x%08x\n", status);
372  IntBugCheck();
373  }
374 
375  oldValue = gVcpu->PtEmuBuffer.Old;
376  newValue = gVcpu->PtEmuBuffer.New;
377 
378  // If the write didn't touch any relevant bits, we can bail out right now. There's no point in
379  // calling the callback, since it will bail out itself.
381  (((oldValue & HOOK_PTS_MONITORED_BITS) == (newValue & HOOK_PTS_MONITORED_BITS)) ||
382  (0 == ((oldValue & PT_P) + (newValue & PT_P)))))
383  {
384  status = INT_STATUS_SUCCESS;
385  break;
386  }
387  }
388 
390 
391  status = (pHook->Callback)(pHook->Header.Context, pHook, PhysicalAddress, &action);
392 
394 
395  if (!INT_SUCCESS(status))
396  {
397  ERROR("[ERROR] EPT callback failed: 0x%08x\n", status);
398 
399  goto _hook_continue;
400  }
401 
402  // Check if the callback requested to be removed.
403  if ((INT_STATUS_REMOVE_HOOK_ON_RET == status) || (INT_STATUS_REMOVE_AND_SKIP == status))
404  {
405  INTSTATUS status2;
406 
407  status2 = IntHookRemoveChain(pHook);
408  if (!INT_SUCCESS(status2))
409  {
410  ERROR("[ERROR] IntHookRemoveChain failed: 0x%08x\n", status2);
411  }
412  }
413 
414  // Check if the returned action should be forced on beta.
415  if (INT_STATUS_FORCE_ACTION_ON_BETA == status)
416  {
418  }
419 
420  // The verdicts priorities is the following:
421  // - allow - smallest priority; default for all callbacks that don't have security logic.
422  // - block - the action has been blocked by the introspection logic
423  // - allow virtual - the action has been emulated by the introspection logic
424  // - allow patched - the actual accessed data has been patched by the introspection logic
425  // - ignore - ignore and allow the action
426  // - retry - highest priority; the instruction will be literally re-executed (without modifying EPT)
427  finalAction = MAX(action, finalAction);
428 
429  // Check if the callback requested skipping all other callbacks.
430  if ((INT_STATUS_SKIP_OTHER_CALLBACKS == status) || (INT_STATUS_REMOVE_AND_SKIP == status))
431  {
432  break;
433  }
434  }
435  }
436 
437 _hook_continue:
438  // We must update the list entry after the callback is called, because new hooks may be added from existing
439  // callbacks. This way, we ensure a deterministic behavior, where new hooks added for the currently-faulted
440  // page will all be called.
441  list = list->Flink;
442  }
443 
445  STATS_EXIT(statRip);
446  STATS_EXIT(stat);
447 
448  if (0 != AccessType)
449  {
450  goto handle_next_access;
451  }
452 
453  // Only read accesses
454  if (!(*CallbackFound) && *PageHooked && (access == IG_EPT_HOOK_READ))
455  {
456  status = IntMtblCheckAccess();
457  if (INT_STATUS_INSTRUCTION_PATCHED == status)
458  {
459  // If we successfully patched the instruction, retry it right now.
460  finalAction = introGuestRetry;
461  }
462 
464  }
465 
466 #ifdef CHECK_PAGE_RIGHTS
467  // Handle special cases where permission set may have failed silently, leaving a page with no registered hooks,
468  // but with altered EPT permissions. Note that we are interested only in the exit GPA; in reality, the
469  // mem access handler may be called for more addresses - practically, for each individual address accessed
470  // by the instruction. Most of the times, those additional addresses point inside pages that are not hooked,
471  // so there's no need to restore the rights for them. Also, no need to do this if PT filtering is enabled, and we
472  // are in the context of a PT write raised from the agent, as in that case, the page-tables will be RWX by default.
473  // Furthermore, avoid calling the validation routine if the accessed linear address is the same as the page
474  // containing the RIP: if an execution exit was previously generated, we may be in a single-step/re-execution
475  // context inside the HV, so calling SetEPTPageProtection now may break whatever access rights the HV placed in
476  // order to properly re-execute the instruction.
477  if (!*PageHooked && // The page must not be hooked.
478  (PhysicalAddress == gVcpu->ExitGpa) && // The accessed GPA must be the one the exit was triggered on.
479  (access == gVcpu->ExitAccess) && // The access type must be the one generated at exit.
480  ((gVcpu->Regs.Rip & PAGE_MASK) != (LinearAddress & PAGE_MASK)) && // Not the same page with the RIP
481  !gVcpu->PtContext) // Not in PT context, as page tables are writable
482  {
483  IntValidatePageRights(LinearAddress, PhysicalAddress, access);
484  }
485 #endif
486 
487  *Action = MAX(*Action, finalAction);
488 
489  return INT_STATUS_SUCCESS;
490 }
491 
492 
493 static BOOLEAN
495  _In_ DWORD CpuNumber
496  )
511 {
512  INSTRUX instrux;
513  BYTE code[16];
514  DWORD cbread = 0, csType, ring;
515  QWORD rip = gVcpu->Regs.Rip;
516  QWORD cr3 = gVcpu->Regs.Cr3;
517 
518  // Optimistically read up to 16 bytes starting with the current RIP. This will be more than enough, and it may
519  // span the next page (even if the instruction doesn't), but in this case, the decode will simply succeed.
520  IntVirtMemRead(rip, sizeof(code), cr3, code, &cbread);
521  if (cbread > 0 && cbread < 16)
522  {
523  INTSTATUS status;
524  NDSTATUS ndstatus;
525 
526  // Get the current mode (again), as it's needed for the disassembler.
527  status = IntGetCurrentMode(CpuNumber, &csType);
528  if (!INT_SUCCESS(status))
529  {
530  ERROR("[ERROR] IntGetCurrentMode failed: 0x%08x\n", status);
531  return FALSE;
532  }
533 
534  // Get the current ring. It's needed for the US flag inside the #PF error code.
535  status = IntGetCurrentRing(CpuNumber, &ring);
536  if (!INT_SUCCESS(status))
537  {
538  ERROR("[ERROR] IntGetCurrentRing failed: 0x%08x\n", status);
539  return FALSE;
540  }
541 
542  // Attempt to decode whatever instruction lies at RIP. There are two main possibilities:
543  // 1. The decode succeeds. This means that one or two pages that contain the instruction have already been
544  // swapped in, and there's nothing more we need to do. This is less likely if we get here.
545  // 2. The decode fails because the buffer is too small. This means the instruction spills in the second page,
546  // which is still swapped out. We can inject a #PF on it, and retry. Not injecting a #PF on the second page
547  // would trigger an infinite loop, because the first page would continue generating EPT violations BEFORE the
548  // second page is accessed and gets to generate a #PF.
549  ndstatus = NdDecodeEx(&instrux, code, cbread,
550  (csType == IG_CS_TYPE_64B) ? ND_CODE_64 : ND_CODE_32,
551  (csType == IG_CS_TYPE_64B) ? ND_DATA_64 : ND_DATA_32);
552  if (ND_STATUS_BUFFER_TOO_SMALL == ndstatus)
553  {
554  // We read at least one byte from the page end, but the decode still failed with ND_STATUS_BUFFER_TOO_SMALL.
555  // This means only one thing: the instruction spills inside the next page, and the next page is not present.
556  // Page fault error code: instruction fetch from user/supervisor mode.
557  DWORD pfec = PFEC_ID | (ring == IG_CS_RING_3 ? PFEC_US : 0);
558 
559  status = IntInjectExceptionInGuest(VECTOR_PF, (rip + PAGE_SIZE) & PAGE_MASK, pfec, CpuNumber);
560  if (!INT_SUCCESS(status))
561  {
562  ERROR("[ERROR] IntInjectExceptionInGuest failed: 0x%08x\n", status);
563  return FALSE;
564  }
565 
566  TRACE("[INFO] Fetch retry at GLA 0x%016llx, CR3 0x%016llx\n", rip, cr3);
567 
568  return TRUE;
569  }
570 
571  // All other statuses can fall through, as we cannot (and shouldn't) inject a #PF for them.
572  }
573 
574  // Nothing read, or everything is successful - nothing we should do.
575  return FALSE;
576 }
577 
578 
579 static BOOLEAN
581  _In_ QWORD Gla,
582  _In_ DWORD CpuNumber,
583  _In_ BYTE AccessType
584  )
602 {
603  INTSTATUS status;
604  QWORD cr3 = gVcpu->Regs.Cr3;
605  VA_TRANSLATION tr;
606  DWORD ring;
607 
608  if (0 == (ND_ACCESS_ANY_WRITE & AccessType))
609  {
610  return FALSE;
611  }
612 
613  status = IntGetCurrentRing(CpuNumber, &ring);
614  if (!INT_SUCCESS(status))
615  {
616  ERROR("[ERROR] IntGetCurrentRing failed: 0x%08x\n", status);
617  return FALSE;
618  }
619 
620  if (IG_CS_RING_3 != ring)
621  {
622  return FALSE;
623  }
624 
625  status = IntTranslateVirtualAddressEx(Gla, cr3, TRFLG_NONE, &tr);
626  if (!INT_SUCCESS(status) &&
627  (INT_STATUS_NO_MAPPING_STRUCTURES != status) &&
628  (INT_STATUS_PAGE_NOT_PRESENT != status))
629  {
630  ERROR("[ERROR] IntTranslateVirtualAddressEx failed: 0x%08x\n", status);
631  return FALSE;
632  }
633 
634  if ((INT_STATUS_NO_MAPPING_STRUCTURES == status) ||
635  (INT_STATUS_PAGE_NOT_PRESENT == status) ||
636  (0 == (tr.Flags & PT_P)) ||
637  !tr.IsWritable)
638  {
639  DWORD pfec;
640 
641  // User-mode fault, write access, page may or may not be present.
642  if ((INT_STATUS_NO_MAPPING_STRUCTURES == status) ||
643  (INT_STATUS_PAGE_NOT_PRESENT == status) ||
644  (0 == (tr.Flags & PT_P)))
645  {
646  pfec = 0;
647  }
648  else
649  {
650  pfec = PFEC_P;
651  }
652 
653  pfec |= PFEC_US | PFEC_RW;
654 
655  status = IntInjectExceptionInGuest(VECTOR_PF, Gla, pfec, CpuNumber);
656  if (!INT_SUCCESS(status))
657  {
658  ERROR("[ERROR] IntInjectExceptionInGuest failed: 0x%08x\n", status);
659  return FALSE;
660  }
661 
662  TRACE("[INFO] Xen workaround at GLA 0x%016llx/0x%016llx, CR3 0x%016llx\n", Gla, Gla, cr3);
663 
664  return TRUE;
665  }
666 
667  return FALSE;
668 }
669 
670 
671 static BOOLEAN
673  _In_ QWORD Gla,
674  _In_ DWORD AccessSize,
675  _In_ BYTE AccessType,
676  _In_ DWORD CpuNumber
677  )
690 {
691  QWORD secpg;
692 
693  if ((Gla & 0xFFF) <= ((Gla + AccessSize - 1) & 0xFFF))
694  {
695  return FALSE;
696  }
697 
698  secpg = (Gla + AccessSize - 1) & PAGE_MASK;
699 
700  return IntHandleCowOnPage(secpg, CpuNumber, AccessType);
701 }
702 
703 
704 INTSTATUS
706  _In_ void *GuestHandle,
707  _In_ QWORD PhysicalAddress,
708  _In_ DWORD Length,
709  _In_ QWORD LinearAddress,
710  _In_ DWORD CpuNumber,
711  _Out_ INTRO_ACTION *Action,
712  _In_ IG_EPT_ACCESS AccessType
713  )
768 {
769  INTSTATUS status;
770  INTRO_ACTION action;
771  DWORD glacount, glaidx, pgcnt, pgidx, tsize, asize;
772  QWORD tgla;
773  BOOLEAN cbkFound, probe, pageHooked, cacheuse, cachehit, cacheadd, fetchfail;
774  IG_EPT_ACCESS access;
775 #define MAX_GLAS 32
776  MEMADDR glas[MAX_GLAS] = {0};
777  struct
778  {
779  QWORD gla, gpa;
780  DWORD size;
781  } pages[2];
782 
783  if (GuestHandle == NULL)
784  {
786  }
787 
788  if (Action == NULL)
789  {
791  }
792 
793  action = introGuestAllowed;
794  glacount = glaidx = pgcnt = pgidx = tsize = 0;
795  probe = FALSE;
796  cbkFound = FALSE;
797  pageHooked = FALSE;
798  cachehit = FALSE;
799  cacheadd = FALSE;
800  fetchfail = FALSE;
801 
802  *Action = introGuestAllowed;
803 
805 
806  gEptEvents++;
807 
808  gEventId++;
809 
810 #ifdef CFG_PAUSE_VCPUS_ON_EVENTS
811  IntPauseVcpus();
812 #endif
813 
814 #ifdef CFG_DEBUG_EPT_VIOLATIONS
815  TRACE("[DEBUG] EPT violation for GPA 0x%016llx, GLA 0x%016llx, on CPU %d, type %d\n",
816  PhysicalAddress, LinearAddress, CpuNumber, AccessType);
817 #endif
818 
820  {
821  // We need to exit with success, since most likely the introspection was disabled in the meantime.
823  goto _exit_release;
824  }
825 
826  if (__unlikely(CpuNumber >= gGuest.CpuCount))
827  {
828  ERROR("[ERROR] An EPT exit came for cpu %d, but we have only %d\n", CpuNumber, gGuest.CpuCount);
830  goto _exit_release;
831  }
832 
833  gVcpu = &gGuest.VcpuArray[CpuNumber];
835 
837 
838  status = IntGetGprs(CpuNumber, &gVcpu->Regs);
839  if (!INT_SUCCESS(status))
840  {
841  ERROR("[ERROR] IntGetGprs failed: 0x%08x\n", status);
842  goto _exit_stop_count;
843  }
844 
845  // Get the EPT index in order to determine whether we are in protected view or not.
846  status = IntGetCurrentEptIndex(CpuNumber, &gVcpu->EptpIndex);
847  if (!INT_SUCCESS(status))
848  {
849  ERROR("[ERROR] IntGetCurrentEptIndex failed: 0x%08x\n", status);
850  goto _exit_stop_count;
851  }
852 
854 
855  cacheuse = ((gGuest.Mm.SystemCr3 != 0) && (AccessType != IG_EPT_HOOK_EXECUTE));
856 
858  CpuNumber,
859  &gVcpu->Regs,
860  &gVcpu->Instruction,
861  cacheuse ? 0 : DEC_OPT_NO_CACHE,
862  &cachehit, &cacheadd);
863  if ((INT_STATUS_PAGE_NOT_PRESENT == status) || (INT_STATUS_NO_MAPPING_STRUCTURES == status))
864  {
865  TRACE("[INFO] The page containing the RIP %llx has been swapped out; will retry the instruction.\n",
866  gVcpu->Regs.Cr3);
867 
868  action = introGuestRetry;
869  fetchfail = TRUE;
870  status = INT_STATUS_SUCCESS;
871  goto _exit_pre_ret;
872  }
873  else if (!INT_SUCCESS(status))
874  {
875  ERROR("[ERROR] IntDecDecodeInstructionAtRipWithCache failed: 0x%08x\n", status);
876  goto _exit_stop_count;
877  }
878 
880 
881  // If we use the instruction cache, and we could add the instruction on the cache but it wasn't previously cached
882  // then retry the instruction, in order to mitigate ToCvsToS attacks.
883  if (cacheuse && cacheadd && !cachehit)
884  {
885  action = introGuestRetry;
886  goto _exit_pre_ret;
887  }
888 
889 _process_again:
890  // Fill in the original access information, as generated at VM exit.
891  gVcpu->ExitGpa = PhysicalAddress;
892  gVcpu->ExitGla = LinearAddress;
893  gVcpu->ExitAccess = AccessType;
894 
895  // Make sure we reset this on each EPT violation.
901 
902 
903  if ((ND_ACCESS_READ | ND_ACCESS_WRITE) == (gVcpu->Instruction.MemoryAccess & (ND_ACCESS_READ | ND_ACCESS_WRITE)))
904  {
907  }
908 
909 
910  // Sometimes, we end up modifying instructions inside the guest memory:
911  // 1. Instructions that access a switch - case branch table (memtable instructions)
912  // 2. Instructions that access a PT, when we deploy the PT filtering agent
913  // In these cases, we may get an exit before the instruction is modified, but by the time we get here, another
914  // CPU got to modify that instruction. Therefore, we'll have an EPT violation with a peculiar instruction, such
915  // as a JMP, INT3 or INT 20. It's easy to handle them - simply re-enter the guest and let the new course of action
916  // take place. However, we must ensure that we avoid hangs, in case one of these instructions legitimately
917  // causes a read/write EPT violation.
918  // For JMP and INT 20 on memtables, the treatment is simple, since there will be no match inside the hooks.
919  if (((gVcpu->Instruction.Instruction == ND_INS_INT3) ||
920  ((gVcpu->Instruction.Instruction == ND_INS_INT) && (gVcpu->Instruction.Immediate1 == 20))) &&
922  {
923  TRACE("[INFO] The instruction at RIP seems to have been modified, will retry the instruction.\n");
924  action = introGuestRetry;
925  status = INT_STATUS_SUCCESS;
926  goto _exit_pre_ret;
927  }
928 
929  // If this instruction is a JMP or a INT 20, check if it was a replace memtable instruction, in which case we
930  // can safely retry it.
931  if (((gVcpu->Instruction.Instruction == ND_INS_JMPNR) ||
932  ((gVcpu->Instruction.Instruction == ND_INS_INT) && (gVcpu->Instruction.Immediate1 == 20))) &&
933  (IG_EPT_HOOK_EXECUTE != AccessType) && (IntMtblInsRelocated(gVcpu->Regs.Rip)))
934  {
935  TRACE("[INFO] The instruction at RIP seems to have been relocated, will retry the instruction.\n");
936  action = introGuestRetry;
937  status = INT_STATUS_SUCCESS;
938  goto _exit_pre_ret;
939  }
940 
941  // Xen always sets the R flag, for every W violation. See if the instruction indeed does read access to the
942  // memory, and clear the R flag if it doesn't.
943  if (0 == (gVcpu->Instruction.MemoryAccess & ND_ACCESS_ANY_READ))
944  {
945  AccessType &= ~IG_EPT_HOOK_READ;
946  }
947 
948  // Usually, only one GLA will be accessed with a simple Write instruction. In rare cases, however, any combination
949  // of the following may take place:
950  // - multiple GLAs accessed (such as PUSH [mem], POP [mem], MOVSD, etc.).
951  // - RMW or RW access (XOR [mem], r, ADD [mem], r, MOVSD, etc.)
952  // - page boundary accesses
953 
954  if (gVcpu->Instruction.IsRepeated)
955  {
956  // If the REP optimizations are disabled, we can handle the access. Otherwise, we will probe the entire access.
957  probe = (0 == gGuest.RepOptsDisableCount);
958  }
959  else
960  {
961  // No REP instruction, check if we can re-enable the REP optimizations.
962  if (gVcpu->RepOptDisabled)
963  {
965 
966  if (0 == --gGuest.RepOptsDisableCount)
967  {
969  }
970  }
971  }
972 
973  // Check if we are in protected EPT, if so, send an alert.
975  {
976  // Make sure we have the right thing in CurrVcpu
977  gVcpu->Gla = LinearAddress;
978  gVcpu->Gpa = PhysicalAddress;
979 
980  status = IntVeHandleEPTViolationInProtectedView(AccessType, &action);
981  if (!INT_SUCCESS(status))
982  {
983  ERROR("[ERROR] IntVeHandleEPTViolationInProtectedView failed: 0x%08x\n", status);
984  }
985 
986  goto done_handling_instruction;
987  }
988 
989  // Handle execution violations. These are special, since an execution fault will be signaled during the fetch phase,
990  // so no other read or write fault will be reported.
991  if (AccessType & IG_EPT_HOOK_EXECUTE)
992  {
993  // Handle the execute fault, if any. Note that there's no need to handle split-page access for execution faults.
994  status = IntHandleMemAccess(LinearAddress, PhysicalAddress, gVcpu->Instruction.Length,
995  &action, &cbkFound, &pageHooked, FALSE, IG_EPT_HOOK_EXECUTE);
996  if (!INT_SUCCESS(status))
997  {
998  ERROR("[ERROR] IntHandleMemAccess failed for 0x%016llx/0x%016llx with size 0x%x for type %d: %08x\n",
999  PhysicalAddress, LinearAddress, Length, AccessType, status);
1000  }
1001 
1002  // If the restriction has been removed, we can bail out, No need to check other accesses, as they will cause
1003  // fault upon retrying the instruction.
1004  if (introGuestRetry == action)
1005  {
1006  goto done_handling_instruction;
1007  }
1008  }
1009 
1010  // Decode the number of memory locations accessed.
1011  status = IntDecGetAccessedMemCount(&gVcpu->Instruction, &glacount);
1012  if (!INT_SUCCESS(status))
1013  {
1014  ERROR("[ERROR] IntDecGetAccessedMemCount failed: 0x%x\n", status);
1015  status = INT_STATUS_NOT_SUPPORTED;
1016  goto _exit_stop_count;
1017  }
1018 
1019  // Handle memory accesses.
1020  if (glacount == 0)
1021  {
1022  // No access - we can leave now.
1023  goto done_handling_instruction;
1024  }
1025  else if ((glacount == 1) && !(AccessType & IG_EPT_HOOK_EXECUTE))
1026  {
1029 
1030  // A single address is accessed, this is easy to handle. The accessed GLA must be the provided LinearAddress.
1031  glas[0].Gla = LinearAddress;
1032  // Use the access as indicated by the Instrux - Xen always send RW access for every W fault.
1033  glas[0].Access = AccessType;
1034 
1036  &gVcpu->Regs, LinearAddress,
1037  gVcpu->Instruction.MemoryAccess, &glas[0].Size);
1038  if (!INT_SUCCESS(status))
1039  {
1040  char text[ND_MIN_BUF_SIZE];
1041  NdToText(&gVcpu->Instruction, 0, ND_MIN_BUF_SIZE, text);
1042  ERROR("[ERROR] IntDecDecodeAccessSize failed: 0x%08x for instruction '%s' "
1043  "with access %d GLA = 0x%016llx, GPA = 0x%016llx\n",
1044  status,
1045  text,
1046  AccessType,
1047  LinearAddress,
1048  PhysicalAddress);
1049 
1053  IntHookGpaDump();
1054  status = INT_STATUS_NOT_SUPPORTED;
1055  goto _exit_stop_count;
1056  }
1057 
1058  if (0 == glas[0].Size)
1059  {
1060  char text[ND_MIN_BUF_SIZE];
1061  NdToText(&gVcpu->Instruction, 0, ND_MIN_BUF_SIZE, text);
1062  WARNING("[WARNING] Access size 0 returned for instruction '%s' "
1063  "with access %d GLA = 0x%016llx, GPA = 0x%016llx\n",
1064  text,
1065  AccessType,
1066  LinearAddress,
1067  PhysicalAddress);
1068 
1072  goto done_handling_instruction;
1073  }
1074  }
1075  else
1076  {
1077  // Multiple addresses accessed, decode each and every one of them.
1078  glacount = MAX_GLAS;
1079 
1081  &gVcpu->Regs, NULL, glas, &glacount);
1082  if (!INT_SUCCESS(status))
1083  {
1084  ERROR("[ERROR] IntDecGetAccessedMem failed: 0x%x\n", status);
1085  status = INT_STATUS_NOT_SUPPORTED;
1086  goto _exit_stop_count;
1087  }
1088  }
1089 
1090  // Handle each accessed address.
1091  for (glaidx = 0; glaidx < glacount; glaidx++)
1092  {
1093  // This is REP instruction and the REP optimization is enabled. Handle up until the end of page.
1094  if (probe)
1095  {
1096  glas[glaidx].Size = (DWORD)MIN(glas[glaidx].Size * gVcpu->Regs.Rcx,
1097  PAGE_REMAINING(glas[glaidx].Gla));
1098  }
1099 
1100  tgla = glas[glaidx].Gla;
1101  tsize = glas[glaidx].Size;
1102  asize = 0;
1103  pgidx = pgcnt = 0;
1104 
1105  // Workaround for the RMW at page boundary emulation issue:
1106  // - The access must be write (specifically it must be RMW, but we handle any write)
1107  // - The access must be at a page boundary
1108  // - The second page must be read-only or COW
1109  // - We must be in user mode (there is no COW in kernel)
1110 
1111  if (IntHandlePageBoundaryCow(glas[glaidx].Gla, glas[glaidx].Size, glas[glaidx].Access, CpuNumber))
1112  {
1113  action = introGuestRetry;
1114 
1115  // we can skip processing the rest of the instruction, since we will retry it & we injected a #PF.
1116  goto done_handling_instruction;
1117  }
1118 
1119  // Translate each accessed page for this memory access.
1120  while (tsize != 0)
1121  {
1122  pages[pgidx].gla = tgla;
1123  pages[pgidx].size = tsize;
1124 
1125  if ((tgla & PAGE_MASK) == (LinearAddress & PAGE_MASK))
1126  {
1127  // We don't have to do the page walk if the linear address is the same page as the one passed to the
1128  // callback. We can use the provided PhysicalAddress page instead.
1129  pages[pgidx].gpa = (PhysicalAddress & PHYS_PAGE_MASK) + (tgla & PAGE_OFFSET);
1130  }
1131  else
1132  {
1133  status = IntTranslateVirtualAddress(tgla, gVcpu->Regs.Cr3, &pages[pgidx].gpa);
1134  if (!INT_SUCCESS(status))
1135  {
1136  // We do not care about translation failures; if there would be a #PF generated for this access,
1137  // it would not bother us, no matter what action we return:
1138  // 1. allow -> emulation (and injection of #PF if needed) or single-step (#PF would be generated
1139  // naturally)
1140  // 2. allowed virtual -> we handled the instruction entirely, integrator/HV does nothing
1141  // 3. allowed patched -> emulation with context setting (and injection of #PF if needed)
1142  // 4. not allowed -> we will skip the instruction, no matter what
1143  // 5. ignored -> we shouldn't do anything anyway
1144  // 6. retry -> the instruction will be retried normally; eventually, some different action will
1145  // be returned; this only happens if the current faulted page is not present or if the
1146  // page restrictions have been removed. Note that retry will be returned only when explicitly
1147  // removing protection from the current page after an execution attempt.
1148  goto done_handling_instruction;
1149  }
1150  }
1151 
1152  asize = MIN(tsize, PAGE_REMAINING(tgla));
1153  tgla += asize;
1154  tsize -= asize;
1155 
1156  pgidx++, pgcnt++;
1157  }
1158 
1159  // If the instruction does a RMW access on the address, we need to call both the read and the write handlers.
1160  access = 0;
1161 
1162  if (glas[glaidx].Access & ND_ACCESS_ANY_READ)
1163  {
1164  access |= IG_EPT_HOOK_READ;
1165  }
1166 
1167  if (glas[glaidx].Access & ND_ACCESS_ANY_WRITE)
1168  {
1169  access |= IG_EPT_HOOK_WRITE;
1170  }
1171 
1172  // Handle the access.
1173  for (pgidx = 0; pgidx < pgcnt; pgidx++)
1174  {
1175  status = IntHandleMemAccess(pages[pgidx].gla, pages[pgidx].gpa, glas[glaidx].Size,
1176  &action, &cbkFound, &pageHooked, probe, access);
1177  if (!INT_SUCCESS(status))
1178  {
1179  ERROR("[ERROR] IntHandleMemAccess failed for 0x%016llx/0x%016llx with size 0x%x for type %d: %08x\n",
1180  pages[pgidx].gpa, glas[glaidx].Gla, glas[glaidx].Size, access, status);
1181  }
1182 
1183  // We asked for probe only (for REPed instruction) and a callback was found. Disable the REP optimization
1184  // and retry the REPed instruction step by step.
1185  if (probe && cbkFound)
1186  {
1188 
1189  if (0 == gGuest.RepOptsDisableCount++)
1190  {
1192 
1193  action = introGuestRetry;
1194 
1195  goto done_handling_instruction;
1196  }
1197  }
1198  }
1199  }
1200 
1201 done_handling_instruction:
1202 
1203  // Handle patched accesses.
1204  if (introGuestAllowedPatched == action)
1205  {
1207 
1208  // The patch buffer is reset on each EPT violation callback invocation.
1209  if (pb->Valid)
1210  {
1211  pb->Valid = FALSE;
1212 
1213 #ifndef USER_MODE
1214  // Try to emulate the read
1215  status = IntDecEmulateRead(&gVcpu->Instruction, pb->Data);
1216  if (!INT_SUCCESS(status))
1217  {
1218  ERROR("[ERROR] IntDecEmulateRead failed: 0x%08x\n", status);
1219  }
1220  else
1221  {
1222  action = introGuestAllowedVirtual;
1223  goto _skip_emu_ctx;
1224  }
1225 #endif // !USER_MODE
1226 
1227  status = IntSetIntroEmulatorContext(CpuNumber, pb->Gla, pb->Size, pb->Data);
1228  if (!INT_SUCCESS(status))
1229  {
1230  ERROR("[ERROR] IntSetIntroEmulatorContext failed: 0x%08x\n", status);
1232  goto _exit_stop_count;
1233  }
1234 
1235 #ifndef USER_MODE
1236 _skip_emu_ctx:
1237  ;
1238 #endif // !USER_MODE
1239  }
1240  else
1241  {
1242  ERROR("[ERROR] IntroGuestAllowedPatched is requested, but the patch buffer is not valid!\n");
1245  goto _exit_stop_count;
1246  }
1247  }
1248 
1249  // Handle PT accesses. These will be emulated in-place.
1250  if (gVcpu->PtEmuBuffer.Emulated)
1251  {
1254 
1255  // On PAE, PT writes are done in two steps, by two instructions (one for the low DWORD, one for the high DWORD)
1256  // In this case we will have two exits; avoid that by checking that the next instruction completes the PT
1257  // write started by the current one
1259  {
1260  QWORD nextGla = 0;
1261 
1262  // At this point we already emulated the first instruction so the RIP is pointing to the next one
1264  CpuNumber,
1265  &gVcpu->Regs,
1266  &gVcpu->Instruction,
1267  gGuest.Mm.SystemCr3 != 0 ? 0 : DEC_OPT_NO_CACHE,
1268  NULL,
1269  NULL);
1270  if (!INT_SUCCESS(status))
1271  {
1272  goto _bail_out_of_next_emu;
1273  }
1274 
1275  if ((gVcpu->Instruction.Instruction != ND_INS_MOV) &&
1276  (gVcpu->Instruction.Instruction != ND_INS_XCHG) &&
1277  (gVcpu->Instruction.Instruction != ND_INS_CMPXCHG))
1278  {
1279  goto _bail_out_of_next_emu;
1280  }
1281 
1282  // Make sure that the second instruction is indeed one that modifies the
1283  // memory and that the access size is 4
1284  if ((gVcpu->Instruction.Operands[0].Type != ND_OP_MEM) ||
1285  (gVcpu->Instruction.Operands[0].Size != 4))
1286  {
1287  goto _bail_out_of_next_emu;
1288  }
1289 
1291  &gVcpu->Regs, &nextGla);
1292  if (!INT_SUCCESS(status))
1293  {
1294  goto _bail_out_of_next_emu;
1295  }
1296 
1297  // There are two cases that interest us:
1298  // MOV dword ptr [edi], ecx
1299  // MOV dword ptr [edi + 4], ecx
1300  // Or:
1301  // MOV dword ptr [edi + 4], ecx
1302  // MOV dword ptr [edi], ecx
1303  // Check that we are in one of these, bail out if not
1304  if ((nextGla & ~7ull) != (LinearAddress & ~7ull))
1305  {
1306  goto _bail_out_of_next_emu;
1307  }
1308 
1309  // The gla was already computed by IntDecDecodeDestinationLinearAddressFromInstruction
1310  // Depending on the two cases from above, the pair of instructions already updated the low DWORD or the
1311  // high DWORD, update the PhysicalAddress to point to the right part of the PT entry
1312  LinearAddress = nextGla;
1313  PhysicalAddress = (PhysicalAddress & PHYS_PAGE_MASK) + (nextGla & PAGE_OFFSET);
1314 
1315  goto _process_again;
1316  }
1317 
1318 _bail_out_of_next_emu:
1319 
1320  action = introGuestAllowedVirtual;
1321  }
1322 
1323 _exit_pre_ret:
1324  // Special handling for introGuestRetry on EPT exec violations. We may request a retry when we cannot fetch the RIP,
1325  // for example. Sometimes, the instruction may be situated at a page boundary and be contained inside both pages.
1326  // In this case, if at least the first page is exec hooked and the second one is not present, we will induce an
1327  // infinite loop by requesting introGuestRetry, because the #PF on the second page will never be triggered since the
1328  // EPT violation on the first one will always take place first. In this case, we will manually inject the #PF on
1329  // the second page.
1330  if (action == introGuestRetry && // Retry requested.
1331  AccessType == IG_EPT_HOOK_EXECUTE && // Execute fault.
1332  fetchfail && // Instruction fetch failed due to page miss.
1333  (gVcpu->Regs.Rip & 0xFFF) + 15 > 0x1000) // There are chances the instruction spills.
1334  {
1335  // NOTE: This does not conflict with IntHandlePageBoundaryCow, as that one is triggered on write faults.
1337  }
1338 
1339  // Handle pre-return events. Don't inject #PF if we already injected one for the Xen workaround.
1341  if (!INT_SUCCESS(status))
1342  {
1343  ERROR("[ERROR] IntGuestPreReturnCallback failed: 0x%08x\n", status);
1344  }
1345 
1346  status = INT_STATUS_SUCCESS;
1347 
1349  {
1351  }
1352 
1353 _exit_stop_count:
1355 
1357 
1358 _exit_release:
1360  {
1361  ERROR("[ERROR] EPT callback set DisableOnReturn... We will try to disable introcore...\n");
1362 
1364 
1365  status = INT_STATUS_FATAL_ERROR;
1366  }
1367 
1368 #ifdef CFG_PAUSE_VCPUS_ON_EVENTS
1369  IntResumeVcpus();
1370 #endif
1371 
1373 
1374  *Action = action;
1375 
1376  return status;
1377 }
1378 
1379 
1380 INTSTATUS
1382  _In_ void *GuestHandle,
1383  _In_ DWORD Msr,
1384  _In_ IG_MSR_HOOK_TYPE Flags,
1385  _Out_ INTRO_ACTION *Action,
1386  _In_opt_ QWORD OriginalValue,
1387  _Inout_opt_ QWORD *NewValue,
1388  _In_ DWORD CpuNumber
1389  )
1413 {
1414  INTSTATUS status;
1415  BOOLEAN found;
1416  BOOLEAN reinjectPerfAgent;
1417 
1418  if (GuestHandle == NULL)
1419  {
1421  }
1422 
1423  if (Action == NULL)
1424  {
1426  }
1427 
1428  found = FALSE;
1429 
1430  *Action = introGuestAllowed;
1431 
1433 
1434  gEventId++;
1435 
1436 #ifdef CFG_PAUSE_VCPUS_ON_EVENTS
1437  IntPauseVcpus();
1438 #endif
1439 
1441  {
1442  // We need to exit with success, since most likely the introspection was disabled in the meantime.
1444  goto _exit_release;
1445  }
1446 
1447  if (__unlikely(CpuNumber >= gGuest.CpuCount))
1448  {
1449  ERROR("[ERROR] A MSR exit came for cpu %d, but we have only %d\n", CpuNumber, gGuest.CpuCount);
1451  goto _exit_release;
1452  }
1453 
1454  gVcpu = &gGuest.VcpuArray[CpuNumber];
1456 
1457  status = IntGetGprs(CpuNumber, &gVcpu->Regs);
1458  if (!INT_SUCCESS(status))
1459  {
1460  ERROR("[ERROR] IntGetGprs failed: 0x%08x\n", status);
1461  goto _exit_release;
1462  }
1463 
1465 
1467  {
1468  if (Msr == pHook->Msr)
1469  {
1470  found = TRUE;
1471 
1472  if (pHook->Disabled)
1473  {
1474  continue;
1475  }
1476 
1477  status = pHook->Callback(Msr, Flags, Action, pHook->Context, OriginalValue, NewValue);
1478 
1479  if (INT_STATUS_REMOVE_HOOK_ON_RET == status)
1480  {
1481  status = IntHookMsrRemoveHook(pHook);
1482  if (!INT_SUCCESS(status))
1483  {
1484  ERROR("[ERROR] IntHookMsrRemoveHook failed: 0x%08x\n", status);
1485  }
1486  }
1487  }
1488  }
1489 
1490  // Only try to re-inject the PT Filter if the LSTAR was initialized
1491  reinjectPerfAgent = (IG_IA32_LSTAR == Msr) && (0 == OriginalValue) && (NULL != NewValue) && (0 != *NewValue);
1492 
1494 
1495  // Handle pre-return events.
1497  POST_COMMIT_MSR |
1498  POST_INJECT_PF |
1499  (reinjectPerfAgent ? POST_RETRY_PERFAGENT : 0));
1500  if (!INT_SUCCESS(status))
1501  {
1502  ERROR("[ERROR] IntGuestPreReturnCallback failed: 0x%08x\n", status);
1503  }
1504 
1505  if (found)
1506  {
1507  status = INT_STATUS_SUCCESS;
1508  }
1509  else
1510  {
1511  status = INT_STATUS_NOT_FOUND;
1512  }
1513 
1515 
1516 _exit_release:
1518  {
1519  ERROR("[ERROR] MSR callback set DisableOnReturn... We will try to disable introcore...\n");
1520 
1522 
1523  status = INT_STATUS_FATAL_ERROR;
1524  }
1525 
1526 #ifdef CFG_PAUSE_VCPUS_ON_EVENTS
1527  IntResumeVcpus();
1528 #endif
1529 
1531 
1532  return status;
1533 }
1534 
1535 
1536 INTSTATUS
1538  _In_ void *GuestHandle,
1539  _In_ DWORD Cr,
1540  _In_ DWORD CpuNumber,
1541  _In_ QWORD OldValue,
1542  _In_ QWORD NewValue,
1543  _Out_ INTRO_ACTION *Action
1544  )
1565 {
1566  INTSTATUS status;
1567  BOOLEAN found;
1568  INTRO_ACTION action;
1569 
1570  if (GuestHandle == NULL)
1571  {
1573  }
1574 
1575  if (Action == NULL)
1576  {
1578  }
1579 
1580  found = FALSE;
1581  action = introGuestAllowed;
1582 
1583  *Action = introGuestAllowed;
1584 
1586 
1587  gEventId++;
1588 
1589 #ifdef CFG_PAUSE_VCPUS_ON_EVENTS
1590  IntPauseVcpus();
1591 #endif
1592 
1594  {
1595  // We need to exit with success, since most likely the introspection was disabled in the meantime.
1597  goto _exit_release;
1598  }
1599 
1600  if (__unlikely(CpuNumber >= gGuest.CpuCount))
1601  {
1602  ERROR("[ERROR] A CR exit came for cpu %d, but we have only %d\n", CpuNumber, gGuest.CpuCount);
1604  goto _exit_release;
1605  }
1606 
1607  gVcpu = &gGuest.VcpuArray[CpuNumber];
1609 
1610  status = IntGetGprs(CpuNumber, &gVcpu->Regs);
1611  if (!INT_SUCCESS(status))
1612  {
1613  ERROR("[ERROR] IntGetGprs failed: 0x%08x\n", status);
1614  goto _exit_release;
1615  }
1616 
1618 
1620  {
1621  if (Cr == pHook->Cr)
1622  {
1623  found = TRUE;
1624 
1625  if (pHook->Disabled)
1626  {
1627  continue;
1628  }
1629 
1630  status = pHook->Callback(pHook->Context, Cr, OldValue, NewValue, &action);
1631 
1632  if (INT_STATUS_REMOVE_HOOK_ON_RET == status)
1633  {
1634  status = IntHookCrRemoveHook(pHook);
1635  if (!INT_SUCCESS(status))
1636  {
1637  ERROR("[ERROR] IntHookCrRemoveHook failed: 0x%08x\n", status);
1638  }
1639  }
1640 
1641  if (action > *Action)
1642  {
1643  *Action = action;
1644  }
1645  }
1646  }
1647 
1649 
1651  if (!INT_SUCCESS(status))
1652  {
1653  ERROR("[ERROR] IntGuestPreReturnCallback failed: 0x%08x\n", status);
1654  }
1655 
1656  if (found)
1657  {
1658  status = INT_STATUS_SUCCESS;
1659  }
1660  else
1661  {
1662  status = INT_STATUS_NOT_FOUND;
1663  }
1664 
1666 
1667 _exit_release:
1669  {
1670  ERROR("[ERROR] CR%d callback set DisableOnReturn... We will try to disable introcore...\n", Cr);
1671 
1673 
1674  status = INT_STATUS_FATAL_ERROR;
1675  }
1676 
1677 #ifdef CFG_PAUSE_VCPUS_ON_EVENTS
1678  IntResumeVcpus();
1679 #endif
1680 
1682 
1683  return status;
1684 }
1685 
1686 
1687 static INTSTATUS
1689  void
1690  )
1705 {
1706  INTSTATUS status;
1707  BOOLEAN found, hooked;
1708  INTRO_ACTION action;
1709 
1711 
1712  action = introGuestAllowed;
1713  found = hooked = FALSE;
1714 
1715  gVcpu->PtContext = TRUE;
1716 
1719 
1724  gVcpu->Gpa = gVcpu->Regs.R9;
1725  gVcpu->Gla = gVcpu->Regs.R8;
1726  gVcpu->AccessSize = 8;
1727 
1728  // Handle EPT violations that came as #VE at our in-guest agent. We treat them as direct EPT violations inside the
1729  // mem access handler. Note that we don't handle multi-accesses here, since a #VE will only be triggered on single
1730  // known accesses - page tables, for now.
1731  status = IntHandleMemAccess(gVcpu->Gla, gVcpu->Gpa, 8, &action, &found, &hooked, FALSE, IG_EPT_HOOK_WRITE);
1732  if (!INT_SUCCESS(status))
1733  {
1734  ERROR("[ERROR] IntHandleEptViolation failed: 0x%08x\n", status);
1736  return status;
1737  }
1738 
1739  if (!hooked)
1740  {
1741  status = IntPtiCacheAdd(gVcpu->Gpa);
1742  if (!INT_SUCCESS(status))
1743  {
1744  ERROR("[ERROR] IntPtsInt3CacheAdd failed for 0x%016llx: 0x%08x\n", gVcpu->Gpa, status);
1745  }
1746  }
1747 
1751 
1752  gVcpu->PtContext = FALSE;
1753 
1755 
1756  return INT_STATUS_SUCCESS;
1757 }
1758 
1759 
1760 static INTSTATUS
1762  void
1763  )
1783 {
1784  INTSTATUS status;
1785  BOOLEAN found, hooked, paused;
1786  QWORD eptvGpa, eptvGla;
1787  DWORD violType, mode;
1788  BYTE accessType;
1789  INTRO_ACTION action;
1790 
1791  action = introGuestAllowed;
1792  found = hooked = paused = FALSE;
1793 
1794  if (0 == (gVcpu->VeInfoPage->Qualification & (1ULL << 8)))
1795  {
1796  // This is a page-walk. The only reason the #VE agent asks for a page walk is to set the A bit inside the
1797  // user-mode CR3 of the current process. This happens because #VE agent makes the page-walk in the context
1798  // of the current CR3, but since it runs in kernel, the current CR3 will be the kernel CR3 (if KPTI is on).
1799  // Although normally all non-leaf entries are marked A/D, it seems that sometimes, an entry inside the user
1800  // CR3 PML4 is NOT accessed, causing an infinite loop due to the page-walker.
1801  PWIN_PROCESS_OBJECT pProc;
1802  PQWORD pPml4e;
1803 
1805  {
1806  ERROR("[ERROR] #VE is supported only on Windows, how did we end up here?\n");
1807  return INT_STATUS_NOT_SUPPORTED;
1808  }
1809 
1810  TRACE("[#VE] Handling special user-mode page-walk, CR3 0x%016llx, GLA 0x%016llx\n",
1812 
1814  if (NULL == pProc)
1815  {
1816  ERROR("[ERROR] No process found for CR3 0x%016llx!\n", gVcpu->Regs.Cr3);
1817  return INT_STATUS_NOT_FOUND;
1818  }
1819 
1820  if ((pProc->UserCr3 != pProc->Cr3) && (pProc->UserCr3 >= 0x1000))
1821  {
1822  QWORD oldVal;
1823 
1825  if (!INT_SUCCESS(status))
1826  {
1827  ERROR("[ERROR] IntGpaCacheFindAndAdd failed for GPA 0x%016llx: 0x%08x\n",
1828  CLEAN_PHYS_ADDRESS64(pProc->UserCr3), status);
1829  return status;
1830  }
1831 
1833 
1834  oldVal = *pPml4e;
1835 
1836  if (0 != (oldVal & PML4_P))
1837  {
1838  QWORD newVal;
1839 
1840  newVal = oldVal | PML4_A;
1841 
1842  _InterlockedCompareExchange64((INT64 *)pPml4e, (INT64)newVal, (INT64)oldVal);
1843  }
1844 
1846  }
1847 
1848  return INT_STATUS_SUCCESS;
1849  }
1850 
1851  // From here on, we're inside #VE context.
1852  gVcpu->VeContext = TRUE;
1853 
1854  // Fake the current EPTP index to be the default EPT view - we are normally in the protected view now, but the #VE
1855  // took place in the default view.
1856  gVcpu->EptpIndex = 0;
1857 
1858  // Fetch the GPA and GLA from the guest.
1860  eptvGla = gVcpu->VeInfoPage->GuestLinearAddress;
1861  violType = (DWORD)gVcpu->VeInfoPage->Qualification;
1862 
1863  // Copy the #VE registers from the #VE info page; these are the real registers that were loaded when the violation
1864  // was triggered. Note that we do not save the old registers, because while we're inside #VE context, we inhibit
1865  // the modification of the real, global registers. Any SetGprs made from within the #VE context will only affect
1866  // the local cache (gVcpu->Regs) which will be used to propagate the new values in the #VE info page.
1885 
1886  violType &= 0x7;
1887 
1888  if (violType & 4)
1889  {
1890  accessType = IG_EPT_HOOK_EXECUTE;
1891  }
1892  else if (violType & 2)
1893  {
1894  accessType = IG_EPT_HOOK_READ | IG_EPT_HOOK_WRITE;
1895  }
1896  else
1897  {
1898  accessType = IG_EPT_HOOK_READ;
1899  }
1900 
1901  // Get the current operating mode. This should always be 64 bit, since we support #VE on 64 bit only.
1902  status = IntGetCurrentMode(gVcpu->Index, &mode);
1903  if (!INT_SUCCESS(status))
1904  {
1905  ERROR("[ERROR] IntGetCurrentMode failed: 0x%08x\n", status);
1906  goto cleanup_and_exit;
1907  }
1908 
1909  // Decode the already fetched instruction from the #VE info page.
1911  if (!INT_SUCCESS(status))
1912  {
1913  ERROR("[ERROR] IntDecDecodeInstructionFromBuffer failed: 0x%08x\n", status);
1914  goto cleanup_and_exit;
1915  }
1916 
1917  if (IntVeIsAgentRemapped(eptvGla))
1918  {
1919  IntPauseVcpus();
1920  paused = TRUE;
1921  }
1922 
1924 
1929 
1931  if (!INT_SUCCESS(status))
1932  {
1933  ERROR("[ERROR] IntGpaCachePatchAndAdd failed: 0x%08x\n", status);
1934  goto cleanup_and_exit;
1935  }
1936 
1937  // Handle EPT violations that came as #VE at our in-guest agent. We treat them as direct EPT violations inside the
1938  // mem access handler. Note that we don't handle multi-accesses here, since a #VE will only be triggered on single
1939  // known accesses - page tables, for now.
1940  status = IntHandleMemAccess(eptvGla, eptvGpa, 8, &action, &found, &hooked, FALSE, accessType);
1941  if (!INT_SUCCESS(status))
1942  {
1943  ERROR("[ERROR] IntHandleEptViolation failed: 0x%08x\n", status);
1944  goto cleanup_and_exit;
1945  }
1946 
1947 cleanup_and_exit:
1948  if (paused)
1949  {
1950  IntResumeVcpus();
1951  }
1952 
1953  // Copy the (possibly) modified VE regs back to the #VE info page.
1972 
1973  // Done with the #VE context.
1977 
1978  gVcpu->VeContext = FALSE;
1979 
1980  return INT_STATUS_SUCCESS;
1981 }
1982 
1983 
1984 INTSTATUS
1986  _In_ void *GuestHandle,
1987  _In_ QWORD Rip,
1988  _In_ DWORD CpuNumber
1989  )
2017 {
2018  INTSTATUS status;
2019  BOOLEAN bFound, bRaiseEptPt, bRaiseEptVe;
2020 
2021  bFound = bRaiseEptPt = bRaiseEptVe = FALSE;
2022 
2023  if (NULL == GuestHandle)
2024  {
2026  }
2027 
2029 
2030  gEventId++;
2031 
2032 #ifdef CFG_PAUSE_VCPUS_ON_EVENTS
2033  IntPauseVcpus();
2034 #endif
2035 
2037  {
2038  // We need to exit with success, since most likely the introspection was disabled in the meantime.
2040  goto _exit_release;
2041  }
2042 
2043  if (__unlikely(CpuNumber >= gGuest.CpuCount))
2044  {
2045  ERROR("[ERROR] A VMCALL exit came for cpu %d, but we have only %d\n", CpuNumber, gGuest.CpuCount);
2047  goto _exit_release;
2048  }
2049 
2050  gVcpu = &gGuest.VcpuArray[CpuNumber];
2052 
2053  status = IntGetGprs(CpuNumber, &gVcpu->Regs);
2054  if (!INT_SUCCESS(status))
2055  {
2056  ERROR("[ERROR] IntGetGprs failed: 0x%08x\n", status);
2057  goto _exit_release;
2058  }
2059 
2061 
2062  // Check the #VE agent.
2063  if (!bFound)
2064  {
2066  {
2067  bFound = TRUE;
2068 
2069  status = IntVeHandleHypercall(CpuNumber);
2070  if (!INT_SUCCESS(status))
2071  {
2072  ERROR("[ERROR] IntVeHandleHypercall failed: 0x%08x\n", status);
2073  }
2074 
2075  if (INT_STATUS_RAISE_EPT == status)
2076  {
2077  bRaiseEptVe = TRUE;
2078  }
2079  }
2080  }
2081 
2082  // Check the PT cache agent.
2083  if (!bFound)
2084  {
2086  {
2087  // We assume only the raise-EPT VMCALL can be issued.
2088  bFound = bRaiseEptPt = TRUE;
2089  }
2090  }
2091 
2092  if (!bFound)
2093  {
2094  // Call the guest detours.
2095  status = IntDetCallCallback();
2096  if (!INT_SUCCESS(status))
2097  {
2098  if (INT_STATUS_NOT_FOUND != status)
2099  {
2100  ERROR("[ERROR] IntDetourCallCallback failed: 0x%08x\n", status);
2101  }
2102  }
2103  else
2104  {
2105  bFound = TRUE;
2106  }
2107  }
2108 
2109  if (!bFound)
2110  {
2111  // Call the generic agent handler
2112  status = IntAgentHandleVmcall(Rip);
2113  if (!INT_SUCCESS(status))
2114  {
2115  ERROR("[ERROR] IntAgentHandleVmcall failed: 0x%08x\n", status);
2116  }
2117  else
2118  {
2119  bFound = TRUE;
2120  }
2121  }
2122 
2123  if (bRaiseEptPt)
2124  {
2126 
2127  status = IntDispatchPtAsEpt();
2128  if (!INT_SUCCESS(status))
2129  {
2130  ERROR("[ERROR] IntDispatchPtAsEpt failed: 0x%08x\n", status);
2131  }
2132  }
2133  else if (bRaiseEptVe)
2134  {
2136 
2137  status = IntDispatchVeAsEpt();
2138  if (!INT_SUCCESS(status))
2139  {
2140  ERROR("[ERROR] IntDispatchVeAsEpt failed: 0x%08x\n", status);
2141  }
2142  }
2143 
2145 
2146  // Handle pre-return events.
2148  if (!INT_SUCCESS(status))
2149  {
2150  ERROR("[ERROR] IntGuestPreReturnCallback failed: 0x%08x\n", status);
2151  }
2152 
2154 
2155  if (bFound)
2156  {
2157  status = INT_STATUS_SUCCESS;
2158  }
2159  else
2160  {
2161  status = INT_STATUS_NOT_FOUND;
2162  }
2163 
2164 _exit_release:
2166  {
2167  ERROR("[ERROR] VMCALL callback set DisableOnReturn... We will try to disable introcore...\n");
2168 
2170 
2171  status = INT_STATUS_FATAL_ERROR;
2172  }
2173 
2174  if (INT_SUCCESS(status) && gGuest.BugCheckInProgress)
2175  {
2176  LOG("[INFO] VMCALL callback set BugCheckInProgress... We will try to disable introcore...\n");
2177 
2179 
2180  status = INT_STATUS_UNINIT_BUGCHECK;
2181  }
2182 
2183 #ifdef CFG_PAUSE_VCPUS_ON_EVENTS
2184  IntResumeVcpus();
2185 #endif
2186 
2188 
2189  return status;
2190 }
2191 
2192 
2193 INTSTATUS
2195  _In_ void *GuestHandle
2196  )
2228 {
2229  INTSTATUS status;
2230 
2231  if (NULL == GuestHandle)
2232  {
2234  }
2235 
2237 
2238  gEventId++;
2239 
2240 #if defined(CFG_PAUSE_VCPUS_ON_EVENTS)
2241  IntPauseVcpus(); // Must pause on Xen, as the VCPUs run when the timer events comes.
2242 #endif
2243 
2244  if (!gGuest.Initialized)
2245  {
2246  // We need to exit with success, since most likely the introspection was disabled in the meantime.
2248  goto release_and_exit;
2249  }
2250 
2251  gVcpu = &gGuest.VcpuArray[0];
2252 
2253  if (gGuest.ShutDown)
2254  {
2255  status = INT_STATUS_SUCCESS;
2256  goto release_and_exit;
2257  }
2258 
2259  gGuest.TimerCalls++;
2260 
2262 
2264  {
2269 
2270  // Don't check anything until the exceptions are not loaded, we may end up excepting unwanted writes
2272  {
2273  // Once every timer tick, on static init, try to initialize Infinity Hook protection
2275  {
2276  status = IntWinInfHookProtect();
2277  if (!INT_SUCCESS(status))
2278  {
2279  ERROR("[ERROR] IntWinInfHookProtect failed: 0x%08x\n", status);
2280  }
2281  }
2282 
2283  // Once every timer tick, do the integrity checks.
2284  status = IntIntegrityCheckAll();
2285  if (!INT_SUCCESS(status))
2286  {
2287  ERROR("[ERROR] IntIntegrityCheckAll failed: 0x%08x\n", status);
2288  }
2289 
2290  // Once every timer tick, make sure no process tokens have been stolen by another processes,
2291  // and no token privileges have been modified in a malicious way, indicating a LPE.
2292  status = IntWinTokenCheckIntegrity();
2293  if (!INT_SUCCESS(status))
2294  {
2295  ERROR("[ERROR] IntWinTokenCheckIntegrity failed: 0x%x\n", status);
2296  }
2297 
2298  // Once every timer tick, make sure that the System CR3 remained the same.
2299  status = IntWinProcValidateSystemCr3();
2300  if (!INT_SUCCESS(status))
2301  {
2302  ERROR("[ERROR] IntWinProcValidateSystemCr3 failed: 0x%08x\n", status);
2303  }
2304 
2305  // Once every timer tick, validate self map entries
2307  if (!INT_SUCCESS(status))
2308  {
2309  ERROR("[ERROR] IntWinProcValidateSelfMapEntries failed: 0x%08x\n", status);
2310  }
2311 
2312  }
2313 
2314  // Re-schedule timed-out page-faults.
2316  }
2317 
2318 #ifdef DEBUG
2319  // Dump the #VE statistics every one minute on debug.
2320  if (0 == gGuest.TimerCalls % (IG_TIMER_FREQUENCY * 60))
2321  {
2322  IntVeDumpStats();
2323  }
2324 #endif
2325 
2326  // Check PTS integrity once every 5 seconds
2327 #ifdef USER_MODE
2328  if (0 == gGuest.TimerCalls % (IG_TIMER_FREQUENCY * 5))
2329  {
2330  status = IntHookPtsCheckIntegrity();
2331  if (!INT_SUCCESS(status))
2332  {
2333  ERROR("[ERROR] IntHookPtsCheckIntegrity failed: 0x%08x\n", status);
2334  }
2335 
2336  }
2337 #endif // USER_MODE
2338 
2339  // Handle pre-return events.
2341  if (!INT_SUCCESS(status))
2342  {
2343  ERROR("[ERROR] IntGuestPreReturnCallback failed: 0x%08x\n", status);
2344  }
2345 
2346  // Unless we made an explicit goto release_and_exit, we assume we don't want to propagate an error.
2347  status = INT_STATUS_SUCCESS;
2348 
2350 
2351  if (0 == gGuest.TimerCalls % (IG_TIMER_FREQUENCY * 3600))
2352  {
2353  // Dump performance stats every 60 minutes.
2354  IntStatsDumpAll();
2355  IntVeDumpStats();
2356 
2357  // Reset the number of NT EAT reads every 60 minutes.
2359  {
2361  }
2362  }
2363 
2364  if (gInjectVeLoader)
2365  {
2368  }
2369  if (gInjectVeUnloader)
2370  {
2373  }
2374 
2375  if (gLoadPtDriver)
2376  {
2378  gLoadPtDriver = FALSE;
2379  }
2380  if (gUnloadPtDriver)
2381  {
2384  }
2385 
2386 
2387 release_and_exit:
2388 
2389 #if defined(CFG_PAUSE_VCPUS_ON_EVENTS)
2390  IntResumeVcpus();
2391 #endif
2392 
2394 
2395  return status;
2396 }
2397 
2398 
2399 INTSTATUS
2401  _In_ void *GuestHandle,
2402  _In_ DWORD CpuNumber,
2403  _Out_ INTRO_ACTION *Action
2404  )
2422 {
2423  INTSTATUS status;
2424  BOOLEAN found;
2425  QWORD newValue;
2426  DWORD xcr;
2427  INTRO_ACTION action;
2428 
2429  if (GuestHandle == NULL)
2430  {
2432  }
2433 
2434  if (Action == NULL)
2435  {
2437  }
2438 
2439  found = FALSE;
2440  *Action = introGuestAllowed;
2441  action = introGuestAllowed;
2442 
2444 
2445  gEventId++;
2446 
2447 #ifdef CFG_PAUSE_VCPUS_ON_EVENTS
2448  IntPauseVcpus();
2449 #endif
2450 
2452  {
2453  // We need to exit with success, since most likely the introspection was disabled in the meantime.
2455  goto _exit_release;
2456  }
2457 
2458  if (__unlikely(CpuNumber >= gGuest.CpuCount))
2459  {
2460  ERROR("[ERROR] A XCR exit came for cpu %d, but we have only %d\n", CpuNumber, gGuest.CpuCount);
2462  goto _exit_release;
2463  }
2464 
2465  gVcpu = &gGuest.VcpuArray[CpuNumber];
2467 
2468  status = IntGetGprs(CpuNumber, &gVcpu->Regs);
2469  if (!INT_SUCCESS(status))
2470  {
2471  ERROR("[ERROR] IntGetGprs failed: 0x%08x\n", status);
2472  goto _exit_release;
2473  }
2474 
2475  xcr = (DWORD)gVcpu->Regs.Rcx;
2476 
2477  newValue = ((QWORD)(gVcpu->Regs.Rdx & 0xFFFFFFFF) << 32) |
2478  (gVcpu->Regs.Rax & 0xFFFFFFFF);
2479 
2481 
2482  gVcpu->Xcr0 = newValue;
2483 
2485  {
2486  if (xcr == pHook->Xcr)
2487  {
2488  found = TRUE;
2489 
2490  if (pHook->Disabled)
2491  {
2492  continue;
2493  }
2494 
2495  status = pHook->Callback(pHook->Context, xcr, &action);
2496 
2497  if (INT_STATUS_REMOVE_HOOK_ON_RET == status)
2498  {
2499  status = IntHookXcrRemoveHook(pHook);
2500  if (!INT_SUCCESS(status))
2501  {
2502  ERROR("[ERROR] IntHookXcrRemoveHook failed: 0x%08x\n", status);
2503  }
2504  }
2505 
2506  if (action > *Action)
2507  {
2508  *Action = action;
2509  }
2510  }
2511  }
2512 
2514 
2515  // Handle pre-return events.
2517  if (!INT_SUCCESS(status))
2518  {
2519  ERROR("[ERROR] IntGuestPreReturnCallback failed: 0x%08x\n", status);
2520  }
2521 
2522  if (found)
2523  {
2524  status = INT_STATUS_SUCCESS;
2525  }
2526  else
2527  {
2528  status = INT_STATUS_NOT_FOUND;
2529  }
2530 
2532 
2533 _exit_release:
2535  {
2536  ERROR("[ERROR] XCR callback set DisableOnReturn... We will try to disable introcore...\n");
2537 
2539 
2540  status = INT_STATUS_FATAL_ERROR;
2541  }
2542 
2543 #ifdef CFG_PAUSE_VCPUS_ON_EVENTS
2544  IntResumeVcpus();
2545 #endif
2546 
2548 
2549  return status;
2550 }
2551 
2552 
2553 INTSTATUS
2555  _In_ void *GuestHandle,
2556  _In_ QWORD GuestPhysicalAddress,
2557  _In_ DWORD CpuNumber
2558  )
2588 {
2589  INTSTATUS status;
2590  PIG_ARCH_REGS regs;
2591  BOOLEAN found, emulated, noemu;
2592 
2593  UNREFERENCED_PARAMETER(GuestPhysicalAddress);
2594 
2595  if (GuestHandle == NULL)
2596  {
2598  }
2599 
2600  found = emulated = noemu = FALSE;
2601 
2603 
2604  gEventId++;
2605 
2606 #ifdef CFG_PAUSE_VCPUS_ON_EVENTS
2607  IntPauseVcpus();
2608 #endif
2609 
2611  {
2612  // We need to exit with success, since most likely the introspection was disabled in the meantime.
2614  goto _exit_release;
2615  }
2616 
2618  {
2619  WARNING("[WARNING] A BP exit came for cpu %d while the guest was not initialized. Will ignore.\n", CpuNumber);
2620  // Here we have to return an error to signal the fact that this #BP was not set by introcore
2621  status = INT_STATUS_NOT_INITIALIZED;
2622  goto _exit_release;
2623  }
2624 
2625  if (__unlikely(CpuNumber >= gGuest.CpuCount))
2626  {
2627  ERROR("[ERROR] A BP exit came for cpu %d, but we have only %d\n", CpuNumber, gGuest.CpuCount);
2629  goto _exit_release;
2630  }
2631 
2632  gVcpu = &gGuest.VcpuArray[CpuNumber];
2634 
2635  status = IntGetGprs(CpuNumber, &gVcpu->Regs);
2636  if (!INT_SUCCESS(status))
2637  {
2638  ERROR("[ERROR] IntGetGprs failed: 0x%08x\n", status);
2639  goto _exit_release;
2640  }
2641 
2642  regs = &gVcpu->Regs;
2643 
2645 
2646  // Handle guest detours.
2647  if (!found)
2648  {
2649  status = IntDetCallCallback();
2650  if (INT_SUCCESS(status))
2651  {
2652  found = TRUE;
2653 
2654  if (INT_STATUS_NO_DETOUR_EMU == status)
2655  {
2656  noemu = TRUE;
2657  }
2658  }
2659  else
2660  {
2661  if (INT_STATUS_NOT_FOUND != status)
2662  {
2663  ERROR("[ERROR] IntDetCallCallback failed: 0x%08x\n", status);
2664  }
2665  }
2666  }
2667 
2668  // If no INT3 handler found, call the generic agents handler.
2669  if (!found)
2670  {
2671  status = IntAgentHandleInt3(regs->Rip, CpuNumber);
2672  if (INT_SUCCESS(status))
2673  {
2674  // An agent handled this, we're good.
2675  found = TRUE;
2676 
2677 
2678  if (INT_STATUS_NO_DETOUR_EMU == status)
2679  {
2680  noemu = TRUE;
2681  }
2682  }
2683  else
2684  {
2685  if (INT_STATUS_NOT_FOUND != status)
2686  {
2687  ERROR("[ERROR] IntAgentHandleInt3 failed: 0x%08x\n", status);
2688  }
2689  }
2690  }
2691 
2692  // If no detour or agent handler found, check the PT write candidates.
2693  if (!found)
2694  {
2695  status = IntPtiHandleInt3();
2696  if (INT_SUCCESS(status))
2697  {
2698  // An agent handled this, we're good.
2699  found = TRUE;
2700 
2701 
2702  if (INT_STATUS_NO_DETOUR_EMU == status)
2703  {
2704  noemu = TRUE;
2705  }
2706  }
2707  else
2708  {
2709  if (INT_STATUS_NOT_FOUND != status)
2710  {
2711  ERROR("[ERROR] IntAgentHandleInt3 failed: 0x%08x\n", status);
2712  }
2713  }
2714  }
2715 
2716  if (!found)
2717  {
2718  // So we don't have agents, detours or pt filter; however, this may happen after we remove candidate
2719  // PT instructions, since an INT3 may remain pending while we remove all the breakpoints from memory.
2720  // We can easily handle this may reading the byte at RIP and checking if it is indeed an INT3. If it is,
2721  // we will reinject it, otherwise, we'll ignore it.
2722  // Note that we have to really decode the instruction, because someone may use encodings such CD03 or 48CC.
2723  INSTRUX instrux;
2724 
2725  status = IntDecDecodeInstructionAtRip(CpuNumber, regs, NULL, &instrux);
2726  if (INT_SUCCESS(status))
2727  {
2728  if (instrux.Instruction == ND_INS_INT3 || (instrux.Instruction == ND_INS_INT && instrux.Immediate1 == 3))
2729  {
2730  // This is a legit int3.
2731  TRACE("[INFO] We have a breakpoint exit with instruction %s at RIP %llx, will reinject\n",
2732  instrux.Mnemonic, regs->Rip);
2733  }
2734  else
2735  {
2736  // Not really INT3 there in memory, we can ignore it.
2737  TRACE("[INFO] We have a breakpoint exit with instruction %s at RIP %llx, will ignore\n",
2738  instrux.Mnemonic, regs->Rip);
2739  found = noemu = TRUE;
2740  }
2741  }
2742  }
2743 
2744  // Skip the INT3 instruction if we haven't already emulated it and we do require emulation.
2745  if (found && !emulated && !noemu)
2746  {
2747  regs->Rip++;
2748 
2749  status = IntSetGprs(CpuNumber, regs);
2750  if (!INT_SUCCESS(status))
2751  {
2752  ERROR("[ERROR] IntSetGprs failed: 0x%08x\n", status);
2753  }
2754  }
2755 
2757 
2758  // Handle pre-return events.
2760  if (!INT_SUCCESS(status))
2761  {
2762  ERROR("[ERROR] IntGuestPreReturnCallback failed: 0x%08x\n", status);
2763  }
2764 
2765  if (found)
2766  {
2767  status = INT_STATUS_SUCCESS;
2768  }
2769  else
2770  {
2771  status = INT_STATUS_NOT_FOUND;
2772  }
2773 
2775 
2776 _exit_release:
2778  {
2779  ERROR("[ERROR] BP callback set DisableOnReturn... We will try to disable introcore...\n");
2780 
2782 
2783  status = INT_STATUS_FATAL_ERROR;
2784  }
2785 
2786  if (INT_SUCCESS(status) && gGuest.BugCheckInProgress)
2787  {
2788  ERROR("[ERROR] BP callback set BugCheckInProgress... We will try to disable introcore...\n");
2789 
2791 
2792  status = INT_STATUS_UNINIT_BUGCHECK;
2793  }
2794 
2795 #ifdef CFG_PAUSE_VCPUS_ON_EVENTS
2796  IntResumeVcpus();
2797 #endif
2798 
2800 
2801  return status;
2802 }
2803 
2804 
2805 static INTSTATUS
2807  _In_ void *GuestHandle,
2808  _In_ DWORD Vector,
2809  _In_ QWORD ErrorCode,
2810  _In_ QWORD Cr2,
2811  _In_ DWORD CpuNumber
2812  )
2832 {
2833  INTSTATUS status;
2834 
2835  UNREFERENCED_PARAMETER(ErrorCode);
2836 
2837  if (GuestHandle == NULL)
2838  {
2840  }
2841 
2843 
2844  gEventId++;
2845 
2846 #ifdef CFG_PAUSE_VCPUS_ON_EVENTS
2847  IntPauseVcpus();
2848 #endif
2849 
2851  {
2852  // We need to exit with success, since most likely the introspection was disabled in the meantime.
2854  goto _exit_release;
2855  }
2856 
2857  if (__unlikely(CpuNumber >= gGuest.CpuCount))
2858  {
2859  ERROR("[ERROR] A VMCALL exit came for cpu %d, but we have only %d\n", CpuNumber, gGuest.CpuCount);
2861  goto _exit_release;
2862  }
2863 
2864  gVcpu = &gGuest.VcpuArray[CpuNumber];
2866 
2867  status = IntGetGprs(CpuNumber, &gVcpu->Regs);
2868  if (!INT_SUCCESS(status))
2869  {
2870  ERROR("[ERROR] IntGetGprs failed: 0x%08x\n", status);
2871  goto _exit_release;
2872  }
2873 
2874  TRACE("[INFO] Injected vector 0x%02x, CR2 0x%016llx, ErrorCode %llx, CPU %d\n", Vector, Cr2, ErrorCode, CpuNumber);
2875 
2877 
2878  // We don't expect this callback to be called with an invalid exception state.
2879  if (!gVcpu->Exception.Valid)
2880  {
2881  WARNING("[WARNING] IntHandleEventInjection was called, but no injection was done!\n");
2882  }
2883 
2884  if (((gVcpu->Exception.Vector != Vector) && (gVcpu->Exception.Vector == VECTOR_PF)) ||
2885  (gVcpu->Exception.Cr2 & PAGE_MASK) != (Cr2 & PAGE_MASK))
2886  {
2887  // Something was injected, but it either wasn't a #PF, or the CR2 did not match.
2889  }
2890 
2891  if (gVcpu->Exception.Vector == Vector && Vector == VECTOR_UD)
2892  {
2893  if (NULL == gVcpu->CurrentUD)
2894  {
2895  ERROR("[ERROR] UD INFO is NULL\n");
2896  }
2897  else
2898  {
2900  }
2901  }
2902 
2903 
2904  // Something got injected, reset this exception.
2906 
2907  // We always set this to NULL, as it is set with the proper value on every #UD request
2908  gVcpu->CurrentUD = NULL;
2909 
2911 
2912  // Handle pre-return events.
2914  if (!INT_SUCCESS(status))
2915  {
2916  ERROR("[ERROR] IntGuestPreReturnCallback failed: 0x%08x\n", status);
2917  }
2918 
2919  status = INT_STATUS_SUCCESS;
2920 
2922 
2923 _exit_release:
2924 
2925 #ifdef CFG_PAUSE_VCPUS_ON_EVENTS
2926  IntResumeVcpus();
2927 #endif
2928 
2930 
2931  return status;
2932 }
2933 
2934 
2935 INTSTATUS
2937  _In_ void *GuestHandle,
2938  _In_ DWORD Flags,
2939  _In_ DWORD CpuNumber,
2940  _Out_ INTRO_ACTION *Action
2941  )
2968 {
2969  INTSTATUS status;
2970  PIG_ARCH_REGS regs;
2971  PINSTRUX instruction;
2972  QWORD gla, gpa, gla2, base;
2973  DTR newDtr = {0}, oldDtr = {0};
2974  BOOLEAN cacheuse, cbkfound, pagefound;
2975  WORD limit;
2976 
2977  if (NULL == GuestHandle)
2978  {
2980  }
2981 
2982  if (NULL == Action)
2983  {
2985  }
2986 
2987  *Action = introGuestAllowed;
2988  gla = base = 0;
2989  limit = 0;
2990  cbkfound = pagefound = FALSE;
2991 
2993 
2994  gEventId++;
2995 
2996 #ifdef CFG_PAUSE_VCPUS_ON_EVENTS
2997  IntPauseVcpus(GuestHandle);
2998 #endif
2999 
3001  {
3003  goto _exit_release;
3004  }
3005 
3006  if (__unlikely(CpuNumber >= gGuest.CpuCount))
3007  {
3008  ERROR("[ERROR] A dtr violation came for cpu %d, but we have only %d\n", CpuNumber, gGuest.CpuCount);
3010  goto _exit_release;
3011  }
3012 
3013  gVcpu = &gGuest.VcpuArray[CpuNumber];
3014  regs = &gVcpu->Regs;
3015  instruction = &gVcpu->Instruction;
3017 
3018  status = IntGetGprs(CpuNumber, regs);
3019  if (!INT_SUCCESS(status))
3020  {
3021  ERROR("[ERROR] IntGetGprs failed: 0x%08x\n", status);
3022  goto done_handling_dtr_violation;
3023  }
3024 
3025  cacheuse = (gGuest.Mm.SystemCr3 != 0);
3027  CpuNumber,
3028  regs,
3029  instruction,
3030  cacheuse ? 0 : DEC_OPT_NO_CACHE,
3031  NULL,
3032  NULL);
3033  if ((INT_STATUS_PAGE_NOT_PRESENT == status) || (INT_STATUS_NO_MAPPING_STRUCTURES == status))
3034  {
3035  TRACE("[INFO] The page containing the RIP has been swapped out; will retry the instruction.\n");
3036  *Action = introGuestRetry;
3037  status = INT_STATUS_SUCCESS;
3038  goto done_handling_dtr_violation;
3039  }
3040  else if (!INT_SUCCESS(status))
3041  {
3042  ERROR("[ERROR] IntDecDecodeInstructionAtRipWithCache failed: 0x%08x\n", status);
3043  goto done_handling_dtr_violation;
3044  }
3045 
3046  // Exits triggered by other instructions are fishy - only LIDT, LGDT, SIDT, SGDT can trigger such exits.
3047  // If, however, we get other instructions, we will retry them, as they were probably been mangled inside the guest.
3048  if (instruction->Instruction != ND_INS_LIDT && instruction->Instruction != ND_INS_SIDT &&
3049  instruction->Instruction != ND_INS_LGDT && instruction->Instruction != ND_INS_SGDT &&
3050  instruction->Instruction != ND_INS_LLDT && instruction->Instruction != ND_INS_SLDT &&
3051  instruction->Instruction != ND_INS_LTR && instruction->Instruction != ND_INS_STR)
3052  {
3053  ERROR("[ERROR] We have a DTR exit, but the instruction is not appropriate: %s\n", instruction->Mnemonic);
3054  *Action = introGuestRetry;
3055  goto done_handling_dtr_violation;
3056  }
3057 
3058  // Not operating on memory, we can bail out. Note that LIDT, LGDT, SIDT, SGDT (the instructions that interest us)
3059  // can only operate on memory, so this condition basically covers register access for LLDT, LTR, SLDT, STR.
3060  if (instruction->Operands[0].Type != ND_OP_MEM)
3061  {
3062  goto done_handling_dtr_violation;
3063  }
3064 
3065  // Get the new value from the instruction by getting the address which stores the value and then reading it. We also
3066  // need it in order to handle the memory access caused by this instruction.
3067  status = IntDecComputeLinearAddress(instruction, &instruction->Operands[0], regs, &gla);
3068  if (!INT_SUCCESS(status))
3069  {
3070  ERROR("[ERROR] IntDecComputeLinearAddress failed: 0x%08x\n", status);
3071  goto done_handling_dtr_violation;
3072  }
3073 
3074  if (IntHandleCowOnPage(gla & PAGE_MASK, CpuNumber, instruction->Operands[0].Access.Access))
3075  {
3076  *Action = introGuestRetry;
3077 
3078  // we can skip processing the rest of the violation, since we will retry it & we injected a #PF.
3079  goto done_handling_dtr_violation;
3080  }
3081 
3082  if (IntHandlePageBoundaryCow(gla, instruction->Operands[0].Size, instruction->Operands[0].Access.Access, CpuNumber))
3083  {
3084  *Action = introGuestRetry;
3085 
3086  // we can skip processing the rest of the violation, since we will retry it & we injected a #PF.
3087  goto done_handling_dtr_violation;
3088  }
3089 
3090  status = IntTranslateVirtualAddress(gla, regs->Cr3, &gpa);
3091  if (!INT_SUCCESS(status))
3092  {
3093  ERROR("[ERROR] IntTranslateVirtualAddress failed: 0x%08x\n", status);
3094  goto done_handling_dtr_violation;
3095  }
3096 
3097  status = IntHandleMemAccess(gla, gpa, instruction->Operands[0].Size, Action, &cbkfound, &pagefound, FALSE,
3098  (instruction->Operands[0].Access.Write ? IG_EPT_HOOK_WRITE : IG_EPT_HOOK_READ));
3099  if (!INT_SUCCESS(status))
3100  {
3101  ERROR("[ERROR] IntHandleMemAccess failed: 0x%08x\n", status);
3102  goto done_handling_dtr_violation;
3103  }
3104 
3105  if (((gla + instruction->Operands[0].Size) & PAGE_MASK) != (gla & PAGE_MASK))
3106  {
3107  // Page boundary access.
3108  gla2 = (gla + instruction->Operands[0].Size) & PAGE_MASK;
3109 
3110  status = IntTranslateVirtualAddress(gla2, regs->Cr3, &gpa);
3111  if (!INT_SUCCESS(status))
3112  {
3113  ERROR("[ERROR] IntTranslateVirtualAddress failed: 0x%08x\n", status);
3114  goto done_handling_dtr_violation;
3115  }
3116 
3117  status = IntHandleMemAccess(gla2, gpa, instruction->Operands[0].Size, Action, &cbkfound, &pagefound, FALSE,
3118  (instruction->Operands[0].Access.Write ? IG_EPT_HOOK_WRITE : IG_EPT_HOOK_READ));
3119  if (!INT_SUCCESS(status))
3120  {
3121  ERROR("[ERROR] IntHandleMemAccess failed: 0x%08x\n", status);
3122  goto done_handling_dtr_violation;
3123  }
3124  }
3125 
3126  // If the memory handler returns anything other than allowed, we will obey that status;
3127  if (*Action != introGuestAllowed)
3128  {
3129  LOG("[INFO] The memory handling callback returned action %d for instruction %s!\n",
3130  *Action,
3131  instruction->Mnemonic);
3132 
3133  goto done_handling_dtr_violation;
3134  }
3135 
3136  // We can bail out on SIDT, SGDT, STR, SLDT, LTR, LLDT - they do not interest us.
3137  if (ND_INS_LIDT != instruction->Instruction && ND_INS_LGDT != instruction->Instruction)
3138  {
3139  //TRACE("[INFO] SIDT/SGDT (instruction code: %04d) came. Will allow.\n", instruction->Instruction);
3140  goto done_handling_dtr_violation;
3141  }
3142 
3143  // Get the original value from DTR (also, here we do the specific DTR stuff)
3144  if (ND_INS_LIDT == instruction->Instruction)
3145  {
3146  status = IntIdtFindBase(CpuNumber, &base, &limit);
3147  if (!INT_SUCCESS(status))
3148  {
3149  ERROR("[ERROR] IntIdtFindBase failed: 0x%08x\n", status);
3150  goto done_handling_dtr_violation;
3151  }
3152  }
3153  else if (ND_INS_LGDT == instruction->Instruction)
3154  {
3155  status = IntGdtFindBase(CpuNumber, &base, &limit);
3156  if (!INT_SUCCESS(status))
3157  {
3158  ERROR("[ERROR] IntGdtFindBase failed: 0x%08x\n", status);
3159  goto done_handling_dtr_violation;
3160  }
3161  }
3162  else
3163  {
3164  WARNING("[WARNING] Unknown instruction on DTR violation callback. Instruction code: %04d. Rip: 0x%016llx\n",
3165  instruction->Instruction, regs->Rip);
3166  goto done_handling_dtr_violation;
3167  }
3168 
3169  oldDtr.Base = base;
3170  oldDtr.Limit = limit;
3171 
3172  status = IntVirtMemRead(gla, gGuest.WordSize + sizeof(WORD), regs->Cr3, &newDtr, NULL);
3173  if (!INT_SUCCESS(status))
3174  {
3175  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x\n", status);
3176  goto done_handling_dtr_violation;
3177  }
3178 
3179  // If the old value is the same as the new one, then we will allow it.
3180  // If the base of the old value is 0 and the base of the new value is different than 0, then we will allow it.
3181  if ((0 == oldDtr.Base && 0 != newDtr.Base) ||
3182  (oldDtr.Base == newDtr.Base && oldDtr.Limit == newDtr.Limit))
3183  {
3184  goto done_handling_dtr_violation;
3185  }
3186 
3188 
3190  {
3191  if (pHook->Disabled)
3192  {
3193  continue;
3194  }
3195 
3196  // We compare the flags for which the hook was set vs. the flags reported to us. They have to be identical
3198  if (pHook->Flags == Flags)
3199  {
3200  status = pHook->Callback(&oldDtr, &newDtr, Flags, Action);
3201 
3202  if (INT_STATUS_REMOVE_HOOK_ON_RET == status)
3203  {
3204  status = IntHookDtrRemoveHook(pHook);
3205  if (!INT_SUCCESS(status))
3206  {
3207  ERROR("[ERROR] IntHookIdtrRemoveHook failed: 0x%08x\n", status);
3208  }
3209  }
3210  }
3211  }
3212 
3214 
3215 done_handling_dtr_violation:
3217 
3218  // Handle pre-return events.
3220  if (!INT_SUCCESS(status))
3221  {
3222  ERROR("[ERROR] IntGuestPreReturnCallback failed: 0x%08x\n", status);
3223  }
3224 
3225 _exit_release:
3227  {
3228  ERROR("[ERROR] DTR callback set DisableOnReturn... We will try to disable introcore...\n");
3229 
3231 
3232  status = INT_STATUS_FATAL_ERROR;
3233  }
3234 
3235 #ifdef CFG_PAUSE_VCPUS_ON_EVENTS
3236  IntResumeVcpus();
3237 #endif
3238 
3240 
3241  return status;
3242 }
3243 
3244 
3245 INTSTATUS
3247  _In_ void *GuestHandle,
3248  _In_ PENG_NOTIFICATION_HEADER EngineNotification
3249  )
3270 {
3271  INTSTATUS status = INT_STATUS_SUCCESS;
3272 
3273  if (NULL == GuestHandle)
3274  {
3276  }
3277 
3278  if (NULL == EngineNotification)
3279  {
3281  }
3282 
3284 
3285  gEventId++;
3286 
3287 #ifdef CFG_PAUSE_VCPUS_ON_EVENTS
3288  IntPauseVcpus(GuestHandle);
3289 #endif
3290 
3292  {
3294  goto done_handling_engine_result;
3295  }
3296 
3297 
3298  if (introEngineNotificationCodeExecution == EngineNotification->Type)
3299  {
3300  PENG_NOTIFICATION_CODE_EXEC codeExecEngineNotification = (PENG_NOTIFICATION_CODE_EXEC)EngineNotification;
3301 
3302  status = IntHandleExecCallback(codeExecEngineNotification);
3303  if (!INT_SUCCESS(status))
3304  {
3305  ERROR("[ERROR] IntHandleExecCallback failed: 0x%08x\n", status);
3306  }
3307  }
3308  else if (introEngineNotificationCmdLine == EngineNotification->Type)
3309  {
3310  PENG_NOTIFICATION_CMD_LINE cmdLineEngineNotification = (PENG_NOTIFICATION_CMD_LINE)EngineNotification;
3311 
3312  status = IntWinHandleCmdLineCallback(cmdLineEngineNotification);
3313  if (!INT_SUCCESS(status))
3314  {
3315  ERROR("[ERROR] IntWinPsHandleCmdLineCallback failed: 0x%08x\n", status);
3316  }
3317  }
3318  else
3319  {
3320  ERROR("[ERROR] Unknown engine notification type, value:%x\n", EngineNotification->Type);
3321  }
3322 
3323 done_handling_engine_result:
3324 
3325 #ifdef CFG_PAUSE_VCPUS_ON_EVENTS
3326  IntResumeVcpus();
3327 #endif
3328 
3330 
3331  return status;
3332 }
3333 
3334 
3335 INTSTATUS
3337  void
3338  )
3349 {
3350  INTSTATUS status;
3351 
3353  if (!INT_SUCCESS(status))
3354  {
3355  ERROR("[ERROR] IntRegisterVmxTimerHandler failed: 0x%08x\n", status);
3356  return status;
3357  }
3358 
3360  if (!INT_SUCCESS(status))
3361  {
3362  ERROR("[ERROR] IntRegisterIntroCallHandler failed: 0x%08x\n", status);
3363  return status;
3364  }
3365 
3367  if (!INT_SUCCESS(status))
3368  {
3369  ERROR("[ERROR] IntRegisterEventInjectionHandler failed: 0x%08x\n", status);
3370  return status;
3371  }
3372 
3374  if (!INT_SUCCESS(status))
3375  {
3376  ERROR("[ERROR] IntRegisterEnginesResultCallback failed: 0x%08x\n", status);
3377  return status;
3378  }
3379 
3380  return INT_STATUS_SUCCESS;
3381 }
3382 
3383 
3384 INTSTATUS
3386  void
3387  )
3393 {
3395 
3397 
3398  // NOTE: This is activated differently for Linux & Windows, but no harm if it's unregistered here.
3400 
3402 
3404 
3405  return INT_STATUS_SUCCESS;
3406 }
BOOLEAN IntPtiIsPtrInAgent(QWORD Ptr, THS_PTR_TYPE Type)
Check if an address points inside the PT filter. Ignore non-executable sections when doing so...
Definition: ptfilter.c:1813
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
#define INT_STATUS_PAGE_NOT_PRESENT
Indicates that a virtual address is not present.
Definition: introstatus.h:438
INTSTATUS IntDecGetAccessedMem(PINSTRUX Instrux, PIG_ARCH_REGS Registers, PIG_XSAVE_AREA XsaveArea, MEMADDR *Gla, DWORD *Count)
Decode each accessed address by an instruction.
Definition: decoder.c:3160
QWORD PhysicalAddress
The physical address to which VirtualAddress translates to.
Definition: introcore.h:107
QWORD RSP
Definition: vecommon.h:73
#define DEC_OPT_NO_CACHE
Flag used to hint the instruction decoder to not use the instruction cache.
Definition: decoder.h:30
#define __unlikely(x)
Definition: common.h:47
#define _Out_
Definition: intro_sal.h:22
_Bool BOOLEAN
Definition: intro_types.h:58
INTSTATUS IntIdtFindBase(DWORD CpuNumber, QWORD *Base, WORD *Limit)
Returns the IDT base and limit for a guest CPU.
Definition: introcpu.c:102
DWORD EptpIndex
The index of the current loaded EPT.
Definition: guests.h:196
#define CONTAINING_RECORD(List, Type, Member)
Definition: introlists.h:36
WORD Limit
Definition: introcpu.h:73
QWORD OldValue
Old page-table entry.
Definition: vecommon.h:125
#define PFEC_P
Definition: processor.h:83
INTSTATUS IntDecEmulateRead(PINSTRUX Instrux, BYTE *SrcValueBuffer)
Emulate a read access.
Definition: decoder.c:1570
#define INTRO_OPT_VE
Enable the Virtualization exception page table access pre-filtering agent (64-bit Windows only)...
Definition: intro_types.h:459
HOOK_HEADER Header
Hook header.
Definition: hook_gpa.h:43
QWORD RDX
Definition: vecommon.h:71
struct _ENG_NOTIFICATION_CMD_LINE * PENG_NOTIFICATION_CMD_LINE
long long INT64
Definition: intro_types.h:45
void * gLock
A lock that ensures that all the events are serialized inside introcore.
Definition: introcore.c:24
void * Context
User-defined data that will be supplied to the callback.
Definition: hook.h:74
LIST_HEAD GpaHooksWrite[GPA_HOOK_TABLE_SIZE]
Hash table of write hooks.
Definition: hook_gpa.h:106
#define INT_STATUS_SKIP_OTHER_CALLBACKS
Definition: introstatus.h:348
Commit all the MSR hooks.
Definition: guests.h:433
#define GPA_HOOK_ID(addr)
Definition: hook_gpa.h:88
Handling MSR violation.
Definition: guests.h:23
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
BYTE Vector
The injected exception number.
Definition: guests.h:133
BOOLEAN PtContext
Set to True if we are in the context of a PT filter VMCALL.
Definition: guests.h:178
INTSTATUS IntHookRemoveChain(PHOOK_GPA HookGpa)
Removes a hook chain, starting with the given GPA hook.
Definition: hook.c:105
QWORD EatReadCount
The number of EAT reads that took place from withing known drivers.
Definition: windriver.h:43
IG_ARCH_REGS Regs
The current state of the guest registers.
Definition: guests.h:95
INTSTATUS IntMtblCheckAccess(void)
Check if the current instruction is like a switch-case table access instruction.
Definition: memtables.c:401
DWORD Index
The VCPU number.
Definition: guests.h:172
Measures CR violation exits.
Definition: stats.h:30
#define _In_
Definition: intro_sal.h:21
QWORD GuestPhysicalAddress
Same as the GPA field provided on EPT Violations.
Definition: vecommon.h:116
#define INT_STATUS_INSTRUCTION_PATCHED
Indicates that an instruction was patched.
Definition: introstatus.h:386
INTSTATUS IntGuestDisableIntro(QWORD Flags)
Disables and unloads the introspection engine.
Definition: guests.c:1201
Handling an event injection.
Definition: guests.h:30
#define CLEAN_PHYS_ADDRESS64(x)
Definition: pgtable.h:119
QWORD SystemCr3
The Cr3 used to map the kernel.
Definition: guests.h:207
#define INT_STATUS_SUCCESS
Definition: introstatus.h:54
Up & running.
Definition: guests.h:21
#define PAGE_REMAINING(addr)
Definition: pgtable.h:163
WIN_KERNEL_DRIVER Win
Valid only for Windows guests.
Definition: drivers.h:70
QWORD Base
Definition: introcpu.h:74
BOOLEAN IsWritable
True if this page is writable.
Definition: introcore.h:132
uint16_t WORD
Definition: intro_types.h:48
INTSTATUS IntHandleMsrViolation(void *GuestHandle, DWORD Msr, IG_MSR_HOOK_TYPE Flags, INTRO_ACTION *Action, QWORD OriginalValue, QWORD *NewValue, DWORD CpuNumber)
Handle a model specific register violation.
Definition: callbacks.c:1381
#define STATS_EXIT(id)
Definition: stats.h:148
INTSTATUS IntHandleExecCallback(PENG_NOTIFICATION_CODE_EXEC ExecNotification)
Handle the code execution scan result provided by the engines.
Definition: scan_engines.c:371
INTSTATUS IntCallbacksInit(void)
Initialize the callbacks.
Definition: callbacks.c:3336
INTSTATUS IntGetGprs(DWORD CpuNumber, PIG_ARCH_REGS Regs)
Get the current guest GPR state.
Definition: introcpu.c:827
void IntVeDumpStats(void)
Dump VE statistics.
Definition: vecore.c:2719
CR_HOOK_STATE * CrHooks
CR hook state.
Definition: guests.h:385
BOOLEAN gInjectVeLoader
Definition: callbacks.c:27
void IntSpinLockRelease(void *SpinLock)
Definition: glue.c:848
struct _LIST_ENTRY * Flink
Definition: introlists.h:20
QWORD RAX
Definition: vecommon.h:69
#define VECTOR_UD
Definition: processor.h:109
static INTSTATUS IntDispatchVeAsEpt(void)
Dispatch a VE as an EPT violation.
Definition: callbacks.c:1761
Measures XCR violation exits.
Definition: stats.h:32
INTSTATUS IntHandleDtrViolation(void *GuestHandle, DWORD Flags, DWORD CpuNumber, INTRO_ACTION *Action)
Handle GDTR, IDTR, LDTR, TR accesses.
Definition: callbacks.c:2936
QWORD Qualification
Same as the exit qualification provided on VM Exits.
Definition: vecommon.h:114
Describes a memory address, as used in an instruction.
Definition: decoder.h:39
QWORD IntEntry
Definition: hook_ptwh.h:21
BOOLEAN RepOptDisabled
The state of the rep optimization feature.
Definition: guests.h:198
BOOLEAN Initialized
True if this structure was initialized and can be used.
Definition: guests.h:285
INTSTATUS IntVeHandleHypercall(DWORD CpuNumber)
Handles hyper calls initiated by the VE agent.
Definition: vecore.c:1985
INTSTATUS IntWinInfHookProtect(void)
This function initializes protection against infinity hook mechanism.
BOOLEAN ShutDown
True if the system process protection is in beta (log-only) mode.
Definition: guests.h:309
#define INT_SUCCESS(Status)
Definition: introstatus.h:42
#define HOOK_FLG_DISABLED
If flag is set, the hook is disabled, therefore ignored on EPT violations.
Definition: hook.h:46
QWORD NewValue
New page-table entry.
Definition: vecommon.h:126
INTSTATUS IntHandleEptViolation(void *GuestHandle, QWORD PhysicalAddress, DWORD Length, QWORD LinearAddress, DWORD CpuNumber, INTRO_ACTION *Action, IG_EPT_ACCESS AccessType)
Handle an EPT violation.
Definition: callbacks.c:705
INTSTATUS IntResumeVcpus(void)
Resumes the VCPUs previously paused with IntPauseVcpus.
Definition: introcore.c:2355
#define PFEC_ID
Definition: processor.h:87
Measures all EPT violations.
Definition: stats.h:18
#define PAGE_OFFSET
Definition: pgtable.h:32
Used by GVA hooks.
Definition: hook.h:18
#define MAX_GLAS
QWORD ExitAccess
The access type for which the EPT violation was generated.
Definition: guests.h:100
DWORD WrittenMask
Bit mask indicating which bytes inside the page-table entry have been written.
Definition: hook_ptwh.h:23
BOOLEAN IntUpdateAreExceptionsLoaded(void)
Checks if the exceptions are loaded.
QWORD Cr3
Virtual address space where the address is monitored.
Definition: hook_pts.h:88
INTSTATUS IntRegisterVmxTimerHandler(PFUNC_IntIntroTimerCallback Callback)
Definition: glue.c:759
BYTE EptHookType
The type of the hook in EPT (see IG_EPT_HOOK_TYPE)
Definition: hook.h:69
#define PML4_A
Definition: pgtable.h:56
QWORD R11
Definition: vecommon.h:80
#define ERROR(fmt,...)
Definition: glue.h:62
REGISTERS Registers
Offset 0x30 - 0x200, general purpose registers.
Definition: vecommon.h:120
INTSTATUS IntDecDecodeInstructionAtRip(DWORD CpuNumber, IG_ARCH_REGS *Registers, IG_SEG_REGS *Segments, INSTRUX *Instrux)
Decode an instruction at current RIP on the provided VCPU.
Definition: decoder.c:384
static BOOLEAN IntHandleCowOnPage(QWORD Gla, DWORD CpuNumber, BYTE AccessType)
Handle copy-on-write on a page.
Definition: callbacks.c:580
Handling a CR load.
Definition: guests.h:25
QWORD Gla
The guest linear address for which the buffer is filled.
Definition: guests.h:40
Measures the DTR violation exits.
Definition: stats.h:35
BOOLEAN IntVeIsCurrentRipInAgent(void)
Check if the current RIP points inside the VE agent.
Definition: vecore.c:2253
INTSTATUS IntCallbacksUnInit(void)
Uninit all the Introcore callbacks.
Definition: callbacks.c:3385
#define PHYS_PAGE_MASK
Definition: pgtable.h:38
void IntSwapMemCancelPendingPF(QWORD VirtualAddress)
Cancel a pending PF.
Definition: swapmem.c:879
static INTSTATUS IntDispatchPtAsEpt(void)
Dispatch a VMCALL issued by the PT filter as an EPT violation.
Definition: callbacks.c:1688
int INTSTATUS
The status data type.
Definition: introstatus.h:24
QWORD GvaPage
Guest virtual page base address, aligned to 4K.
Definition: hook_gva.h:32
BOOLEAN Partial
True if the write is partial and not the entire page table entry is modified.
Definition: guests.h:55
INTSTATUS IntDecComputeLinearAddress(PINSTRUX Instrux, PND_OPERAND Operand, PIG_ARCH_REGS Registers, QWORD *LinearAddress)
Given an instruction and a memory operand, it will compute the guest linear address encoded by that o...
Definition: decoder.c:790
QWORD RIP
Definition: vecommon.h:85
QWORD gEventId
The ID of the current event.
Definition: glue.c:55
INTSTATUS IntWinHandleCmdLineCallback(PENG_NOTIFICATION_CMD_LINE EngineNotification)
Handle a command line scan response.
Definition: wincmdline.c:151
BYTE Access
Access (read, write, or a combination).
Definition: decoder.h:43
INTSTATUS IntUnregisterVmxTimerHandler(void)
Definition: glue.c:768
#define INT_STATUS_NOT_FOUND
Definition: introstatus.h:284
struct _HOOK_GVA * PHOOK_GVA
HOOK_STATE * gHooks
Global hooks state.
Definition: hook.c:8
static INTSTATUS IntHandleEventInjection(void *GuestHandle, DWORD Vector, QWORD ErrorCode, QWORD Cr2, DWORD CpuNumber)
Handle event injections inside the guest.
Definition: callbacks.c:2806
BOOLEAN IntMtblInsRelocated(QWORD Rip)
Check if the instruction at the provided RIP is instrumented.
Definition: memtables.c:677
INTSTATUS IntInjectExceptionInGuest(BYTE Vector, QWORD Cr2, DWORD ErrorCode, DWORD CpuNumber)
Injects an exception inside the guest.
Definition: introcore.c:2264
#define TRFLG_NONE
No special options.
Definition: introcore.h:82
BOOLEAN Valid
Definition: guests.h:68
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:368
Measures the EPT violations for which the instruction does a read and a write.
Definition: stats.h:27
INTSTATUS IntHandleXcrWrite(void *GuestHandle, DWORD CpuNumber, INTRO_ACTION *Action)
Handle extended control registers writes.
Definition: callbacks.c:2400
BOOLEAN Valid
True if Data is valid, False if it is not.
Definition: guests.h:42
INTSTATUS IntPauseVcpus(void)
Pauses all the guest VCPUs.
Definition: introcore.c:2320
static BOOLEAN IntValidateTranslation(PHOOK_GPA Hook)
Checks if the given GPA hook points to a valid GVA hook with a correct translation.
Definition: callbacks.c:34
INTRO_GUEST_TYPE OSType
The type of the guest.
Definition: guests.h:274
INSTRUX Instruction
The current instruction, pointed by the guest RIP.
Definition: guests.h:88
Commit all the memory hooks.
Definition: guests.h:432
BOOLEAN Emulated
True if the access was already emulated; False if it was not emulated.
Definition: guests.h:54
Measures event injections.
Definition: stats.h:36
#define MIN(a, b)
Definition: introdefs.h:146
#define PML4_INDEX(a)
Definition: pgtable.h:95
PVECPU VeInfoPage
Pointer to the VEINFO page used for this VCPU.
Definition: guests.h:190
static BOOLEAN IntHandleFetchRetryOnPageBoundary(DWORD CpuNumber)
Handle instruction fetch at page boundary, if an EPT execute violation has been generated.
Definition: callbacks.c:494
INTSTATUS IntEnginesResultCallback(void *GuestHandle, PENG_NOTIFICATION_HEADER EngineNotification)
Handler called by the integrator as soon as the engines report a scan result for a buffer...
Definition: callbacks.c:3246
INTSTATUS IntWinProcValidateSystemCr3(void)
This function checks if the system CR3 value was modified and if GUEST_STATE::KernelBetaDetections is...
Definition: winprocess.c:3195
#define LOG(fmt,...)
Definition: glue.h:61
Measures the handling of VMCALL exits.
Definition: stats.h:29
MSR_HOOK_STATE * MsrHooks
MSR hook state.
Definition: guests.h:383
DWORD AccessSize
The size of the memory access. Valid only for EPT exits.
Definition: guests.h:103
INTSTATUS IntSetEPTPageProtection(DWORD EptIndex, QWORD Gpa, BYTE Read, BYTE Write, BYTE Execute)
Definition: glue.c:672
Commit all the CR hooks.
Definition: guests.h:434
struct _VCPU_STATE::@77 Exception
The exception to be injected in guest.
#define PML4_P
Definition: pgtable.h:51
Measures the execution of EPT violation handlers.
Definition: stats.h:26
QWORD Cr3
Process PDBR. Includes PCID.
Definition: winprocess.h:96
BYTE HookType
The type of the hook structure (see _HOOK_TYPE)
Definition: hook.h:68
Exposes the functions used to schedule an asynchronous code execution scan and receives its result...
INTSTATUS IntGetCurrentMode(DWORD CpuNumber, DWORD *Mode)
Read the current CS type.
Definition: introcpu.c:977
QWORD Gla
The guest linear address.
Definition: decoder.h:41
Command line notification for scan engines.
Definition: intro_types.h:1917
LIST_HEAD GpaHooksRead[GPA_HOOK_TABLE_SIZE]
Hash table of read hooks.
Definition: hook_gpa.h:107
QWORD R12
Definition: vecommon.h:81
INTSTATUS IntGpaCacheRelease(PGPA_CACHE Cache, QWORD Gpa)
Release a previously used cached entry.
Definition: gpacache.c:678
#define _Inout_opt_
Definition: intro_sal.h:31
#define _Inout_
Definition: intro_sal.h:20
static int8_t _InterlockedCompareExchange8(int8_t volatile *Destination, int8_t Exchange, int8_t Comparand)
Definition: intrinsics.h:603
TIMER_FRIENDLY void IntDumpInstruction(INSTRUX *Instruction, QWORD Rip)
This function dumps a given instruction (textual disassembly).
Definition: dumper.c:513
INTSTATUS IntRtlpVirtualUnwindCheckAccess(void)
Check if a memory read operation was issued by RtlpVirtualUnwind or friends and update the cache...
#define IG_TIMER_FREQUENCY
The timer frequency (1 call per second).
Definition: glueiface.h:371
QWORD Flags
The entry that maps VirtualAddress to PhysicalAddress, together with all the control bits...
Definition: introcore.h:119
QWORD R14
Definition: vecommon.h:83
void IntHookGpaDump(void)
Dump the entire contents of the GPA hook system, listing each hook.
Definition: hook_gpa.c:1152
#define INT_STATUS_NOT_INITIALIZED
Definition: introstatus.h:266
INTSTATUS IntRegisterEnginesResultCallback(PFUNC_IntEventEnginesResultCallback Callback)
Thin wrapper over the optional GLUE_IFACE.RegisterEnginesResultCallback API.
Definition: glue.c:619
QWORD RBX
Definition: vecommon.h:72
INTSTATUS IntGuestPreReturnCallback(DWORD Options)
Handles all the operations that must be done before returning from a VMEXIT event handler...
Definition: guests.c:1278
INTSTATUS IntWinSelfMapValidateSelfMapEntries(void)
Validates the self map entries for every process in the system.
Definition: winselfmap.c:445
#define STATS_ENTER(id)
Definition: stats.h:141
uint8_t * PBYTE
Definition: intro_types.h:47
BOOLEAN PaeEnabled
True if Physical Address Extension is enabled.
Definition: guests.h:291
INTSTATUS IntHookXcrRemoveHook(HOOK_XCR *Hook)
Remove an extended control register hook.
Definition: hook_xcr.c:110
__noreturn void IntBugCheck(void)
Definition: glue.c:917
QWORD New
The new, to be written, value of the page table entry.
Definition: guests.h:52
XCR_HOOK_STATE * XcrHooks
XCR hook state.
Definition: guests.h:384
static BOOLEAN gForceActionOnBeta
Definition: callbacks.c:30
CPU_STATE State
The state of this VCPU. Describes what action is the VCPU currently doing.
Definition: guests.h:173
#define PT_P
Definition: pgtable.h:83
Reinject the #VE or PT filtering agent, based on the active options.
Definition: guests.h:438
BOOLEAN Guest64
True if this is a 64-bit guest, False if it is a 32-bit guest.
Definition: guests.h:286
Measures EPT violations generated while the guest was in kernel mode.
Definition: stats.h:22
unsigned long long QWORD
Definition: intro_types.h:53
QWORD Current
The currently used options.
Definition: guests.h:232
Handling a LIDT or LGDT.
Definition: guests.h:26
QWORD R10
Definition: vecommon.h:79
QWORD Old
The old, original, value of the written page table entry.
Definition: guests.h:51
PTWRITE_CACHE PtWriteCache
The last written PT entry.
Definition: guests.h:170
Handling a breakpoint (int3).
Definition: guests.h:29
struct _HOOK_HEADER * PHOOK_HEADER
void * ParentHook
The parent hook. For a GPA hook, for example, a GVA hook or a PagedHook will be the parent hook...
Definition: hook.h:73
QWORD UserCr3
Process user PDBR. Includes PCID.
Definition: winprocess.h:97
static INTSTATUS IntHandleMemAccess(QWORD LinearAddress, QWORD PhysicalAddress, DWORD Length, INTRO_ACTION *Action, BOOLEAN *CallbackFound, BOOLEAN *PageHooked, BOOLEAN ProbeOnly, IG_EPT_ACCESS AccessType)
Handle a memory access to a guest linear address.
Definition: callbacks.c:192
INTSTATUS IntDecGetAccessedMemCount(PINSTRUX Instrux, DWORD *Count)
Decode the number of memory locations accessed by an instruction.
Definition: decoder.c:3110
WORD Offset
The offset within the page where the hook starts. 0-4095 valid.
Definition: hook_gpa.h:47
INTSTATUS IntTranslateVirtualAddress(QWORD Gva, QWORD Cr3, QWORD *PhysicalAddress)
Translates a guest virtual address to a guest physical address.
Definition: introcore.c:1999
void * GpaCache
The currently used GPA cache.
Definition: guests.h:399
QWORD GpaPage
The page where the hook is set.
Definition: hook_gpa.h:46
static BOOLEAN IntHandlePageBoundaryCow(QWORD Gla, DWORD AccessSize, BYTE AccessType, DWORD CpuNumber)
Check if we have a copy-on-write condition at a page boundary.
Definition: callbacks.c:672
BOOLEAN gInjectVeUnloader
Definition: callbacks.c:27
#define TRUE
Definition: intro_types.h:30
#define INT_STATUS_INVALID_PARAMETER_4
Definition: introstatus.h:71
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
Measures the decoding of instructions that generate EPT violations.
Definition: stats.h:24
INTSTATUS IntSetIntroEmulatorContext(DWORD CpuNumber, QWORD VirtualAddress, DWORD BufferSize, BYTE *Buffer)
Definition: glue.c:1018
Execution notification for scan engines.
Definition: intro_types.h:1908
BOOLEAN GuestInitialized
True if the OS-specific portion has been initialized.
Definition: guests.h:289
QWORD Gpa
The accessed guest physical address. Valid only for EPT exits.
Definition: guests.h:101
#define TRACE(fmt,...)
Definition: glue.h:58
INFO_UD_PENDING * CurrentUD
The currently pending #UD injection on this CPU.
Definition: guests.h:123
PWIN_PROCESS_OBJECT IntWinProcFindObjectByCr3(QWORD Cr3)
Finds a process by its kernel CR3.
Definition: winprocesshp.c:122
INTSTATUS IntHandleCrWrite(void *GuestHandle, DWORD Cr, DWORD CpuNumber, QWORD OldValue, QWORD NewValue, INTRO_ACTION *Action)
Handle a control register violation.
Definition: callbacks.c:1537
#define INT_STATUS_INVALID_PARAMETER_5
Definition: introstatus.h:74
Measures EPT violations generated while the guest was in user mode.
Definition: stats.h:23
#define INT_STATUS_INVALID_INTERNAL_STATE
Definition: introstatus.h:272
INTSTATUS IntAgentHandleInt3(QWORD Rip, DWORD CpuNumber)
Dispatch a breakpoint event to the Windows or Linux agent breakpoint handler.
Definition: agent.c:12
INTSTATUS IntHookCrRemoveHook(HOOK_CR *Hook)
Remove a control register hook.
Definition: hook_cr.c:135
Contains information about the patch buffer.
Definition: guests.h:38
QWORD ExitGpa
The accessed guest physical address, for which the EPT violation was generated.
Definition: guests.h:98
INTSTATUS IntHookPtwEmulateWrite(QWORD Address)
Emulate a write that took place on page table entry at Address.
Definition: hook_ptwh.c:12
INTSTATUS IntAgentHandleVmcall(QWORD Rip)
Dispatch a VMCALL event to the Windows or Linux agent VMCALL handler.
Definition: agent.c:42
Measures the INT3 events.
Definition: stats.h:34
struct _ENG_NOTIFICATION_CODE_EXEC * PENG_NOTIFICATION_CODE_EXEC
#define IG_IA32_LSTAR
Definition: glueiface.h:146
BYTE WordSize
Guest word size. Will be 4 for 32-bit guests and 8 for 64-bit guests.
Definition: guests.h:363
DWORD RepOptsDisableCount
The number of times the rep optimizations have been disabled.
Definition: guests.h:379
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
#define INT_STATUS_NO_DETOUR_EMU
Signals that no emulation is needed for this event.
Definition: introstatus.h:378
QWORD Cr2
The Cr2. Valid only if Vector is 14 (Page Fault)
Definition: guests.h:135
Handling XSETBV.
Definition: guests.h:28
QWORD R8
Definition: vecommon.h:77
INTSTATUS IntHookMsrRemoveHook(HOOK_MSR *Hook)
Remove a model specific register hook.
Definition: hook_msr.c:138
QWORD gEptEvents
Definition: callbacks.c:26
#define INT_STATUS_UNINIT_BUGCHECK
Indicates that the guest crashed and Introcore must be deactivated.
Definition: introstatus.h:430
QWORD Xcr0
The value of XCR0. Updated by IntHandleXcrWrite.
Definition: guests.h:114
#define WARNING(fmt,...)
Definition: glue.h:60
INTSTATUS IntDetCallCallback(void)
Calls the appropriate detour handler for hypercall.
Definition: detours.c:1517
LIST_HEAD MsrHooksList
The list of MSR hooks.
Definition: hook_msr.h:38
QWORD RBP
Definition: vecommon.h:74
Command line scan results.
Definition: intro_types.h:135
INTSTATUS IntUnregisterEventInjectionHandler(void)
Definition: glue.c:610
HOOK_GPA_STATE GpaHooks
GPA hooks state.
Definition: hook.h:92
INTSTATUS IntVeHandleEPTViolationInProtectedView(IG_EPT_ACCESS AccessType, INTRO_ACTION *Action)
Handle an EPT violation inside the protected EPT view.
Definition: vecore.c:234
void IntSpinLockAcquire(void *SpinLock)
Definition: glue.c:833
INTSTATUS IntPtiCacheAdd(QWORD Gpa)
Add a guest-physical address to the PT filter cache of entries for which an exit is not required...
Definition: ptfilter.c:1912
DTR_HOOK_STATE * DtrHooks
DTR hook state.
Definition: guests.h:386
A descriptor table register. Valid for IDTR and GDTR.
Definition: introcpu.h:71
DWORD CpuCount
The number of logical CPUs.
Definition: guests.h:275
#define PAGE_SIZE
Definition: common.h:53
#define UNREFERENCED_PARAMETER(P)
Definition: introdefs.h:29
void IntStatsDumpAll(void)
Prints all the non-zero stats.
Definition: stats.c:213
void * InstructionCache
The currently used instructions cache.
Definition: guests.h:400
INTSTATUS IntRegisterIntroCallHandler(PFUNC_IntIntroCallCallback Callback)
Definition: glue.c:741
Measures the timer events.
Definition: stats.h:33
Measures the VMCALL exists generated by the page table filtering agent.
Definition: stats.h:61
uint32_t DWORD
Definition: intro_types.h:49
BYTE IG_EPT_ACCESS
Definition: glueiface.h:303
DWORD ProtectedEptIndex
The EPTP index of the trusted EPT.
Definition: guests.h:397
IG_MSR_HOOK_TYPE
The type of the MSR access.
Definition: glueiface.h:171
#define INT_STATUS_INVALID_PARAMETER_6
Definition: introstatus.h:77
QWORD R13
Definition: vecommon.h:82
enum _INTRO_ACTION INTRO_ACTION
Event actions.
PHOOK_PTS_ENTRY Parent
The leaf page-table entry hook associated with this address.
Definition: hook_pts.h:96
WORD Length
The length, in bytes, of the hook. 1-4096 valid.
Definition: hook_gpa.h:48
INTSTATUS IntPtiHandleInt3(void)
This function is the main INT3 handler.
Definition: ptfilter.c:859
static void IntValidatePageRights(QWORD LinearAddress, QWORD PhysicalAddress, DWORD Access)
Check if the access rights for the provided PhysicalAddress are up-to-date in the EPT...
Definition: callbacks.c:111
INTSTATUS IntSetGprs(DWORD CpuNumber, PIG_ARCH_REGS Regs)
Sets the values of the guest GPRs.
Definition: introcpu.c:905
INTSTATUS IntGetEPTPageProtection(DWORD EptIndex, QWORD Gpa, BYTE *Read, BYTE *Write, BYTE *Execute)
Definition: glue.c:659
#define IntDbgEnterDebugger()
Definition: introcore.h:381
INTSTATUS IntHookPtsCheckIntegrity(void)
Checks the integrity of the existing page-table hooks. Used for debugging the PT filter.
Definition: hook_pts.c:2236
#define MAX(a, b)
Definition: introdefs.h:151
INTSTATUS IntHandleBreakpoint(void *GuestHandle, QWORD GuestPhysicalAddress, DWORD CpuNumber)
Handle guest breakpoints.
Definition: callbacks.c:2554
DWORD Size
The size.
Definition: decoder.h:42
#define INT_STATUS_FATAL_ERROR
An unrecoverable error was detected. Introcore must be unloaded.
Definition: introstatus.h:465
void IntUDRemoveEntry(INFO_UD_PENDING **InfoUD)
Remove a pending UD entry.
Definition: udlist.c:96
MM Mm
Guest memory information, such as paging mode, system Cr3 value, etc.
Definition: guests.h:370
Measures MSR violation exits.
Definition: stats.h:31
PATCH_BUFFER PatchBuffer
The patch buffer used to emulate reads.
Definition: guests.h:168
GUEST_STATE gGuest
The current guest state.
Definition: guests.c:48
BOOLEAN VeContext
Set to True if we are in the context of the #VE agent.
Definition: guests.h:184
QWORD ExitGla
The accessed guest linear address, for which the EPT violation was generated.
Definition: guests.h:99
Execution attempt result.
Definition: intro_types.h:131
INTSTATUS IntVirtMemRead(QWORD Gva, DWORD Length, QWORD Cr3, void *Buffer, DWORD *RetLength)
Reads data from a guest virtual memory range.
Definition: introcore.c:627
#define PFEC_US
Definition: processor.h:85
Measures write EPT violations.
Definition: stats.h:20
LIST_HEAD GpaHooksExecute[GPA_HOOK_TABLE_SIZE]
Hash table of execute hooks.
Definition: hook_gpa.h:108
static int64_t _InterlockedCompareExchange64(int64_t volatile *Destination, int64_t Exchange, int64_t Comparand)
Definition: intrinsics.h:627
enum _STAT_ID STAT_ID
Stat IDs.
TIMER_FRIENDLY void IntDumpGva(QWORD Gva, DWORD Length, QWORD Cr3)
This function is a wrapper over IntDumpGvaEx (it uses RowLength = 16, ElementLength = 1...
Definition: dumper.c:249
INTSTATUS IntDecDecodeAccessSize(PINSTRUX Instrux, PIG_ARCH_REGS Registers, QWORD Gla, BYTE AccessType, DWORD *AccessSize)
Decode the memory access size of a given instruction.
Definition: decoder.c:731
QWORD TimerCalls
The number of times the timer callback has been invoked.
Definition: guests.h:270
INTSTATUS IntDecDecodeDestinationLinearAddressFromInstruction(PINSTRUX Instrux, PIG_ARCH_REGS Registers, QWORD *LinearAddress)
Decode the destination memory linear address.
Definition: decoder.c:1202
void IntSwapMemReinjectFailedPF(void)
Reinject timed-out PFs.
Definition: swapmem.c:913
INTSTATUS IntHandleTimer(void *GuestHandle)
Periodically called by the integrator, once every second.
Definition: callbacks.c:2194
INTSTATUS IntGpaCacheFindAndAdd(PGPA_CACHE Cache, QWORD Gpa, void **Hva)
Search for an entry in the GPA cache, and add it, if it wasn&#39;t found.
Definition: gpacache.c:451
Commit all the XCR hooks.
Definition: guests.h:435
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 INT_STATUS_NO_MAPPING_STRUCTURES
Indicates that not all mapping structures of a virtual address are present.
Definition: introstatus.h:434
HOOK_PTEWS WriteState
Write state.
Definition: hook_pts.h:70
#define VECTOR_PF
Definition: processor.h:116
#define INT_STATUS_REMOVE_AND_SKIP
Definition: introstatus.h:353
PHOOK_PTS PtsHook
The page tables hook.
Definition: hook_gva.h:31
#define HOOK_PTS_MONITORED_BITS
Definition: hook_pts.h:19
BOOLEAN IntVeIsAgentRemapped(QWORD Gla)
Checks if a given guest linear address belongs to the VE agent.
Definition: vecore.c:2899
#define INT_STATUS_NOT_INITIALIZED_HINT
Definition: introstatus.h:320
DWORD Size
The valid size of the Data buffer.
Definition: guests.h:41
Encapsulates information about a virtual to physical memory translation.
Definition: introcore.h:102
KERNEL_DRIVER * KernelDriver
Points to the driver object that describes the kernel image.
Definition: guests.h:381
#define INT_STATUS_INVALID_PARAMETER_8
Definition: introstatus.h:83
Measures the look-up of EPT violation handlers.
Definition: stats.h:25
BOOLEAN Valid
True if the fields are valid; False if they are not.
Definition: guests.h:132
PFUNC_EptViolationCallback Callback
The callback for this hook.
Definition: hook_gpa.h:50
#define INT_STATUS_INVALID_PARAMETER_1
Definition: introstatus.h:62
LIST_HEAD XcrHooksList
The list of XCR hooks.
Definition: hook_xcr.h:31
#define INT_STATUS_NOT_SUPPORTED
Definition: introstatus.h:287
VCPU_STATE * gVcpu
The state of the current VCPU.
Definition: guests.c:57
BOOLEAN gLoadPtDriver
Definition: callbacks.c:28
#define INT_STATUS_FORCE_ACTION_ON_BETA
Definition: introstatus.h:370
LIST_HEAD CrHooksList
The list of CR hooks.
Definition: hook_cr.h:35
INTSTATUS IntGpaCachePatchAndAdd(PGPA_CACHE Cache, QWORD Gpa, DWORD Size, PBYTE Buffer)
Patch data in a cached entry, or add it to the cache, of not already present.
Definition: gpacache.c:593
QWORD RSI
Definition: vecommon.h:75
64-bit selector.
Definition: glueiface.h:188
TIMER_FRIENDLY INTSTATUS IntWinTokenCheckIntegrity(void)
This function checks the integrity of the security token for all the processes inside gWinProcesses...
Definition: wintoken.c:942
INTSTATUS IntUnregisterIntroCallHandler(void)
Definition: glue.c:750
INTSTATUS IntGdtFindBase(DWORD CpuNumber, QWORD *GdtBase, WORD *GdtLimit)
Returns the GDT base and limit for a guest CPU.
Definition: introcpu.c:206
void IntGuestUpdateCoreOptions(QWORD NewOptions)
Updates Introcore options.
Definition: guests.c:1424
INTSTATUS IntDecDecodeInstructionAtRipWithCache(void *Cache, DWORD CpuNumber, PIG_ARCH_REGS Registers, PINSTRUX Instrux, DWORD Options, BOOLEAN *CacheHit, BOOLEAN *Added)
Decode an instruction using the cache.
Definition: decoder.c:449
Holds register state.
Definition: glueiface.h:30
INTSTATUS IntGetCurrentEptIndex(DWORD CpuNumber, DWORD *EptpIndex)
Get the EPTP index of the currently loaded EPT.
Definition: introcpu.c:1238
BOOLEAN BugCheckInProgress
Definition: guests.h:329
INTSTATUS IntGetCurrentRing(DWORD CpuNumber, DWORD *Ring)
Read the current protection level.
Definition: introcpu.c:959
DWORD Flags
Generic flags. Check out EPT Hook flags.
Definition: hook.h:67
BOOLEAN Valid
True if the information in this structure is valid; False it it is not.
Definition: guests.h:53
Execute-access hook.
Definition: glueiface.h:300
BOOLEAN DisableOnReturn
Set to True if after returning from this event handler, introcore must be unloaded.
Definition: guests.h:324
QWORD RCX
Definition: vecommon.h:70
#define PFEC_RW
Definition: processor.h:84
char CHAR
Definition: intro_types.h:56
INTSTATUS IntRegisterEventInjectionHandler(PFUNC_IntEventInjectionCallback Callback)
Definition: glue.c:601
unsigned long long * PQWORD
Definition: intro_types.h:53
INTSTATUS IntIntegrityCheckAll(void)
The function which is called once every second and checks all the integrity regions.
Definition: integrity.c:377
#define INTRO_OPT_IN_GUEST_PT_FILTER
Enable in-guest page-table filtering (64-bit Windows only).
Definition: intro_types.h:445
#define list_for_each(_head, _struct_type, _var)
Definition: introlists.h:41
Measures execute EPT violations.
Definition: stats.h:21
PTEMU_BUFFER PtEmuBuffer
The page table write emulator buffer.
Definition: guests.h:169
#define INT_STATUS_REMOVE_HOOK_ON_RET
Can be used by hook callbacks in order to signal that the hook should be removed. ...
Definition: introstatus.h:343
#define HOOK_PAGE_TABLE_FLAGS
Any of these flags set indicates that we are dealing with a page table page.
Definition: hook.h:57
Write-access hook.
Definition: glueiface.h:299
BOOLEAN gUnloadPtDriver
Definition: callbacks.c:28
INTSTATUS IntToggleRepOptimization(BOOLEAN Enable)
Definition: glue.c:1098
#define PAGE_MASK
Definition: pgtable.h:35
BYTE Data[ND_MAX_REGISTER_SIZE]
The actual contents of the buffer.
Definition: guests.h:43
#define INT_STATUS_INVALID_PARAMETER_2
Definition: introstatus.h:65
INTRO_PROT_OPTIONS CoreOptions
The activation and protection options for this guest.
Definition: guests.h:267
QWORD Gla
The accessed guest virtual address. Valid only for EPT exits.
Definition: guests.h:102
Measures read EPT violations.
Definition: stats.h:19
INTSTATUS IntUnregisterBreakpointHandler(void)
Definition: glue.c:592
INTSTATUS IntHandleIntroCall(void *GuestHandle, QWORD Rip, DWORD CpuNumber)
Handle a VMCALL issued inside the guest.
Definition: callbacks.c:1985
QWORD R15
Definition: vecommon.h:84
Notification header for scan engines alerts.
Definition: intro_types.h:1889
INTSTATUS IntUnregisterEnginesResultCalback(void)
Thin wrapper over the optional GLUE_IFACE.UnregisterEnginesResultCalback API.
Definition: glue.c:640
QWORD R9
Definition: vecommon.h:78
Inject pending page faults.
Definition: guests.h:437
void IntHookPtsDump(void)
Prints all the page table hooks.
Definition: hook_pts.c:2452
Commit all the DTR hooks.
Definition: guests.h:436
#define HOOK_FLG_REMOVE
If flag is set, the hook has been removed, and waits the next commit to be actually deleted...
Definition: hook.h:44
INTSTATUS IntHookDtrRemoveHook(HOOK_DTR *Hook)
Remove a descriptor register hook.
Definition: hook_dtr.c:106
Exposes the functions used to schedule an asynchronous command line scan and receives its result...
#define INT_STATUS_INVALID_PARAMETER_7
Definition: introstatus.h:80
LIST_HEAD DtrHooksList
The list of DTR hooks.
Definition: hook_dtr.h:34
Handling a VMCALL.
Definition: guests.h:24
#define FALSE
Definition: intro_types.h:34
This structure describes a running process inside the guest.
Definition: winprocess.h:81
#define INT_STATUS_INVALID_PARAMETER_3
Definition: introstatus.h:68
Handling EPT violation.
Definition: guests.h:22