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 "lixcmdline.h"
24 #include "scan_engines.h"
25 #include "wintoken.h"
26 #include "winsecdesc.h"
27 #include "winsud.h"
28 
32 
34 
35 
36 static BOOLEAN
38  _In_ PHOOK_GPA Hook
39  )
52 {
53  INTSTATUS status;
54  PHOOK_HEADER header;
55  PHOOK_GVA hookGva;
56  VA_TRANSLATION tr;
57 
58  header = (PHOOK_HEADER)Hook->Header.ParentHook;
59 
60  if (header == NULL)
61  {
62  // No parent, this is a plain GPA hook (page table, most likely).
63  return TRUE;
64  }
65 
66  if (header->HookType != hookTypeGva)
67  {
68  // Not a GVA hook, there's nothing to translate.
69  return TRUE;
70  }
71 
72  hookGva = (PHOOK_GVA)header;
73 
74  // The CR3 used when the GVA hook was placed should be used for translation. This is needed because we may have
75  // shared memory scenarios:
76  // Process 1: GVA_1 via CR3_1 => GPA
77  // Process 2: GVA_2 via CR3_2 => GPA
78  // 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
79  // it yields the expected GPA.
80  status = IntTranslateVirtualAddressEx(hookGva->GvaPage, hookGva->PtsHook->Cr3, 0, &tr);
81  if (!INT_SUCCESS(status) || 0 == (tr.Flags & PT_P))
82  {
83  // We couldn't translate this page - this may happen if the entry was invalidated due to a partial write
84  // (on PAE x86), and in the meantime, an exit was triggered in that GPA.
85  // In this case, we trigger the error if the PTS entry is NOT in a partial write state.
86  if (hookGva->PtsHook->Parent->WriteState.WrittenMask == 0)
87  {
88  ERROR("[ERROR] Failed translating GVA 0x%016llx (reported GLA 0x%016llx, GPA 0x%016llx). "
89  "Int entry: 0x%016llx, Real entry: 0x%016llx, error: 0x%08x\n",
90  hookGva->GvaPage, gVcpu->Gla, gVcpu->Gpa,
91  hookGva->PtsHook->Parent->WriteState.IntEntry, tr.Flags, status);
92  return FALSE;
93  }
94 
95  // We can bail out now, both entries are invalid, which is good.
96  return TRUE;
97  }
98 
100  {
101  // The translation succeeded, but we obtained a different GPA than the one we just got an exit for: this means
102  // that the known GPA is outdated, and as such, we've probably missed a translation.
103  ERROR("[ERROR] Translation mismatch for GVA 0x%016llx, translated GPA 0x%016llx "
104  "(reported GLA 0x%016llx, GPA 0x%016llx)!\n",
105  hookGva->GvaPage, tr.PhysicalAddress, gVcpu->Gla, gVcpu->Gpa);
106  return FALSE;
107  }
108 
109  return TRUE;
110 }
111 
112 
113 static BOOLEAN
115  _In_ QWORD LinearAddress,
116  _In_ QWORD PhysicalAddress,
117  _In_ DWORD Access
118  )
133 {
134  INTSTATUS status;
135  BYTE r, w, x;
136  CHAR text[ND_MIN_BUF_SIZE];
137  HOOK_EPT_ENTRY *eptEntry = IntHookGpaGetExistingEptEntry(PhysicalAddress);
138 
139  if (NULL != eptEntry)
140  {
141  // Read access, the page has at least one read hook.
142  if (!!(Access & IG_EPT_HOOK_READ) && eptEntry->ReadCount != 0)
143  {
144  return TRUE;
145  }
146 
147  // Write access, the page has at least one write hook.
148  if (!!(Access & IG_EPT_HOOK_WRITE) && eptEntry->WriteCount != 0)
149  {
150  return TRUE;
151  }
152 
153  // Execute access, the page has at least one execute hook.
154  if (!!(Access & IG_EPT_HOOK_EXECUTE) && eptEntry->ExecuteCount != 0)
155  {
156  return TRUE;
157  }
158  }
159 
160  // Either we don't have an EPT entry at all (meaning that there are absolutely no EPT hooks on it), or the access
161  // that generated the EPT violations does not coincide with a hook placed on the page.
162  r = w = x = 0;
163 
164  status = IntGetEPTPageProtection(gVcpu->EptpIndex, PhysicalAddress, &r, &w, &x);
165  if (!INT_SUCCESS(status))
166  {
167  ERROR("[ERROR] IntGetEPTPageProtection failed for 0x%016llx: 0x%08x\n", PhysicalAddress, status);
168  }
169  else
170  {
171  // We need this for logging purposes.
172  NdToText(&gVcpu->Instruction, gVcpu->Regs.Rip, ND_MIN_BUF_SIZE, text);
173 
174  WARNING("[WARNING] GPA 0x%016llx, GLA 0x%016llx, was accessed with type %c%c%c, but no hooks exist on it: %c%c%c! "
175  "CR3 0x%016llx RIP 0x%016llx %s\n",
176  PhysicalAddress, LinearAddress,
177  !!(Access & IG_EPT_HOOK_READ) ? 'R' : '-',
178  !!(Access & IG_EPT_HOOK_WRITE) ? 'W' : '-',
179  !!(Access & IG_EPT_HOOK_EXECUTE) ? 'X' : '-',
180  r ? 'R' : '-', w ? 'W' : '-', x ? 'X' : '-',
181  gVcpu->Regs.Cr3, gVcpu->Regs.Rip, text);
182 
183  if (!!(Access & IG_EPT_HOOK_EXECUTE))
184  {
185  x = 1;
186  }
187 
188  if (!!(Access & IG_EPT_HOOK_WRITE))
189  {
190  w = 1;
191  }
192 
193  if (!!(Access & IG_EPT_HOOK_READ))
194  {
195  r = 1;
196  }
197 
198  TRACE("[INFO] New access rights: %c%c%c\n", r ? 'R' : '-', w ? 'W' : '-', x ? 'X' : '-');
199 
200  // This set has the role of fooling the integrator cache - if we set the exact same rights as last time,
201  // the integrator may optimize it, by silently discarding the request. Therefore, we will set some different
202  // rights before, in order to make sure we really end up calling the HV to modify the rights again.
203  // Note that the 0, 0, 0 rights are correct - since there's at least one access type for which we didn't
204  // find a hook, this means there is at least on access right present for the page, so at least one of the
205  // R, W or X is 1. Therefore, the RWX == 000 request will most certainly be different than the current
206  // actual rights.
207  status = IntSetEPTPageProtection(gVcpu->EptpIndex, PhysicalAddress, 0, 0, 0);
208  if (!INT_SUCCESS(status))
209  {
210  ERROR("[ERROR] IntSetEPTPageProtection failed for 0x%016llx: 0x%08x\n", PhysicalAddress, status);
211  }
212 
213  // Now the actual RWX rights.
214  status = IntSetEPTPageProtection(gVcpu->EptpIndex, PhysicalAddress, r, w, x);
215  if (!INT_SUCCESS(status))
216  {
217  ERROR("[ERROR] IntSetEPTPageProtection failed for 0x%016llx: 0x%08x\n", PhysicalAddress, status);
218  }
219 
221  }
222 
223  return FALSE;
224 }
225 
226 
227 static void
229  _In_ QWORD LinearAddress,
230  _In_ QWORD PhysicalAddress,
231  _In_ DWORD Access
232  )
245 {
246  INTSTATUS status;
247  BYTE r, w, x;
248  CHAR text[ND_MIN_BUF_SIZE];
249 
250  // We need this for logging purposes.
251  NdToText(&gVcpu->Instruction, gVcpu->Regs.Rip, ND_MIN_BUF_SIZE, text);
252 
253  r = w = x = 0;
254 
255  WARNING("[WARNING] GPA 0x%016llx, GLA 0x%016llx, was accessed with type %d, but no hooks exist on it! "
256  "CR3 0x%016llx RIP 0x%016llx %s\n",
257  PhysicalAddress, LinearAddress, Access, gVcpu->Regs.Cr3, gVcpu->Regs.Rip, text);
258 
259  status = IntGetEPTPageProtection(gVcpu->EptpIndex, PhysicalAddress, &r, &w, &x);
260  if (!INT_SUCCESS(status))
261  {
262  ERROR("[ERROR] IntGetEPTPageProtection failed for 0x%016llx: 0x%08x\n", PhysicalAddress, status);
263  }
264  else
265  {
266  TRACE("[INFO] Old access rights: %c%c%c\n", r ? 'R' : '-', w ? 'W' : '-', x ? 'X' : '-');
267 
268  if (!!(Access & IG_EPT_HOOK_EXECUTE))
269  {
270  x = 1;
271  }
272 
273  if (!!(Access & IG_EPT_HOOK_WRITE))
274  {
275  w = 1;
276  }
277 
278  if (!!(Access & IG_EPT_HOOK_READ))
279  {
280  r = 1;
281  }
282 
283  TRACE("[INFO] New access rights: %c%c%c\n", r ? 'R' : '-', w ? 'W' : '-', x ? 'X' : '-');
284 
285  // This set has the role of fooling the integrator cache - if we set the exact same rights as last time,
286  // the integrator may optimize it, by silently discarding the request. Therefore, we will set some different
287  // rights before, in order to make sure we really end up calling the HV to modify the rights again.
288  // Note that the 0, 0, 0 rights are correct - since there's at least one access type for which we didn't
289  // find a hook, this means there is at least on access right present for the page, so at least one of the
290  // R, W or X is 1. Therefore, the RWX == 000 request will most certainly be different than the current
291  // actual rights.
292  status = IntSetEPTPageProtection(gVcpu->EptpIndex, PhysicalAddress, 0, 0, 0);
293  if (!INT_SUCCESS(status))
294  {
295  ERROR("[ERROR] IntSetEPTPageProtection failed for 0x%016llx: 0x%08x\n", PhysicalAddress, status);
296  }
297 
298  // Now the actual RWX rights.
299  status = IntSetEPTPageProtection(gVcpu->EptpIndex, PhysicalAddress, r, w, x);
300  if (!INT_SUCCESS(status))
301  {
302  ERROR("[ERROR] IntSetEPTPageProtection failed for 0x%016llx: 0x%08x\n", PhysicalAddress, status);
303  }
304 
306  }
307 }
308 
309 
310 static INTSTATUS
312  _In_ QWORD LinearAddress,
313  _In_ QWORD PhysicalAddress,
314  _In_ DWORD Length,
315  _Inout_ INTRO_ACTION *Action,
316  _Inout_ BOOLEAN *CallbackFound,
317  _Inout_ BOOLEAN *PageHooked,
318  _In_ BOOLEAN ProbeOnly,
319  _In_ IG_EPT_ACCESS AccessType
320  )
354 {
355  INTSTATUS status;
356  DWORD hid;
357  QWORD physPage;
358  LIST_ENTRY *hooks, *list;
359  INTRO_ACTION action, finalAction;
360  IG_EPT_ACCESS access;
361  STAT_ID stat, statRip;
362 
363  action = finalAction = introGuestAllowed;
364  access = AccessType;
365 
366  if (NULL == gHooks)
367  {
369  }
370 
371  gVcpu->Gpa = PhysicalAddress;
372  gVcpu->Gla = LinearAddress;
373  gVcpu->AccessSize = Length;
374 
375  // Assume no callback handled this access.
376  *CallbackFound = FALSE;
377  *PageHooked = FALSE;
378 
379  hid = GPA_HOOK_ID(PhysicalAddress);
380 
381  physPage = PhysicalAddress & PHYS_PAGE_MASK;
382 
383 handle_next_access:
384  hooks = NULL;
385 
386  if (AccessType & IG_EPT_HOOK_EXECUTE)
387  {
388  hooks = &gHooks->GpaHooks.GpaHooksExecute[hid];
389  AccessType &= ~IG_EPT_HOOK_EXECUTE;
390  stat = statsEptExecute;
391  }
392  else if (AccessType & IG_EPT_HOOK_READ)
393  {
394  hooks = &gHooks->GpaHooks.GpaHooksRead[hid];
395  AccessType &= ~IG_EPT_HOOK_READ;
396  stat = statsEptRead;
397  }
398  else if (AccessType & IG_EPT_HOOK_WRITE)
399  {
400  hooks = &gHooks->GpaHooks.GpaHooksWrite[hid];
401  AccessType &= ~IG_EPT_HOOK_WRITE;
402  stat = statsEptWrite;
403  }
404  else
405  {
407  }
408 
409  if (hooks == NULL)
410  {
412  }
413 
414  if (0 != (gVcpu->Regs.Rip & (gGuest.Guest64 ? 0x8000000000000000 : 0x80000000)))
415  {
416  statRip = statsEptKernel;
417  }
418  else
419  {
420  statRip = statsEptUser;
421  }
422 
423  STATS_ENTER(stat);
424  STATS_ENTER(statRip);
426 
427  // Xen handles events single-threaded. There's no point in complicating the event handler with a shared lock.
428  // This way, we can add new hooks from existing hooks (no problem if we add hook while we iterate the list).
429  // However, no hooks can be removed from the list - it's safe to iterate the list while new hooks may be
430  // added, since we know for sure no entry will become invalid while we have gLock acquired.
431 
432  // Try to find a suitable hook to call
433  list = hooks->Flink;
434  while (list != hooks)
435  {
436  PHOOK_GPA pHook = CONTAINING_RECORD(list, HOOK_GPA, Link);
437 
438  if (pHook->GpaPage == physPage)
439  {
440  *PageHooked = TRUE;
441 
442  // Check if write took place outside this protected region
443  if ((pHook->GpaPage + pHook->Offset >= PhysicalAddress + Length) ||
444  (pHook->GpaPage + pHook->Offset + pHook->Length <= PhysicalAddress))
445  {
446  goto _hook_continue;
447  }
448 
449  if (0 == (pHook->Header.Flags & (HOOK_FLG_REMOVE | HOOK_FLG_DISABLED)))
450  {
451 #ifdef CFG_DEBUG_EPT_VIOLATIONS
452  TRACE("[DEBUG] Calling EPT handler for GPA 0x%016llx, hook address: 0x%016llx, callback 0x%016llx\n",
453  PhysicalAddress, pHook, pHook->Callback);
454 #endif
455 
456 #ifdef CHECK_PAGE_RIGHTS
457  if (!IntValidateTranslation(pHook))
458  {
459  ERROR("[ERROR] IntValidateTranslation failed: GLA 0x%016llx, GPA 0x%016llx!\n",
460  LinearAddress, PhysicalAddress);
461  IntHookPtsDump();
462  }
463 #endif
464 
465  // Indicate that we've found a callback.
466  *CallbackFound = TRUE;
467 
468  // If we don't want to actually handle the access, bail out now and don't invoke any handlers.
469  if (ProbeOnly)
470  {
471  AccessType = 0; // Stop access handling.
472  status = INT_STATUS_SUCCESS;
473  break;
474  }
475 
476  // Pre-process all the PT writes here, before calling any callback. We will then cache the old & new
477  // values.
478  // Note: we do this only if we find a callback; otherwise, we let the hypervisor emulate the access.
479  // If we don't have at least a callback registered for a page, there's no way to know if that page is
480  // a page table or not.
481  if ((pHook->Header.EptHookType == IG_EPT_HOOK_WRITE) &&
482  !!(pHook->Header.Flags & HOOK_PAGE_TABLE_FLAGS) &&
484  {
485  QWORD oldValue, newValue;
486 
487  status = IntHookPtwEmulateWrite(PhysicalAddress);
488  if (!INT_SUCCESS(status))
489  {
490  ERROR("[ERROR] IntHookPtwEmulateWrite failed: 0x%08x\n", status);
491  IntBugCheck();
492  }
493 
494  oldValue = gVcpu->PtEmuBuffer.Old;
495  newValue = gVcpu->PtEmuBuffer.New;
496 
497  // If the write didn't touch any relevant bits, we can bail out right now. There's no point in
498  // calling the callback, since it will bail out itself.
500  (((oldValue & HOOK_PTS_MONITORED_BITS) == (newValue & HOOK_PTS_MONITORED_BITS)) ||
501  (0 == ((oldValue & PT_P) + (newValue & PT_P)))))
502  {
503  status = INT_STATUS_SUCCESS;
504  break;
505  }
506  }
507 
509 
510  status = (pHook->Callback)(pHook->Header.Context, pHook, PhysicalAddress, &action);
511 
513 
514  if (!INT_SUCCESS(status))
515  {
516  ERROR("[ERROR] EPT callback failed: 0x%08x\n", status);
517 
518  goto _hook_continue;
519  }
520 
521  // Check if the callback requested to be removed.
522  if ((INT_STATUS_REMOVE_HOOK_ON_RET == status) || (INT_STATUS_REMOVE_AND_SKIP == status))
523  {
524  INTSTATUS status2;
525 
526  status2 = IntHookRemoveChain(pHook);
527  if (!INT_SUCCESS(status2))
528  {
529  ERROR("[ERROR] IntHookRemoveChain failed: 0x%08x\n", status2);
530  }
531  }
532 
533  // Check if the returned action should be forced on beta.
534  if (INT_STATUS_FORCE_ACTION_ON_BETA == status)
535  {
537  }
538 
539  // The verdicts priorities is the following:
540  // - allow - smallest priority; default for all callbacks that don't have security logic.
541  // - block - the action has been blocked by the introspection logic
542  // - allow virtual - the action has been emulated by the introspection logic
543  // - allow patched - the actual accessed data has been patched by the introspection logic
544  // - ignore - ignore and allow the action
545  // - retry - highest priority; the instruction will be literally re-executed (without modifying EPT)
546  finalAction = MAX(action, finalAction);
547 
548  // Check if the callback requested skipping all other callbacks.
549  if ((INT_STATUS_SKIP_OTHER_CALLBACKS == status) || (INT_STATUS_REMOVE_AND_SKIP == status))
550  {
551  break;
552  }
553  }
554  }
555 
556 _hook_continue:
557  // We must update the list entry after the callback is called, because new hooks may be added from existing
558  // callbacks. This way, we ensure a deterministic behavior, where new hooks added for the currently-faulted
559  // page will all be called.
560  list = list->Flink;
561  }
562 
564  STATS_EXIT(statRip);
565  STATS_EXIT(stat);
566 
567  if (0 != AccessType)
568  {
569  goto handle_next_access;
570  }
571 
572  // Only read accesses
573  if (!(*CallbackFound) && *PageHooked && (access == IG_EPT_HOOK_READ))
574  {
575  status = IntMtblCheckAccess();
576  if (INT_STATUS_INSTRUCTION_PATCHED == status)
577  {
578  // If we successfully patched the instruction, retry it right now.
579  finalAction = introGuestRetry;
580  }
581 
583  }
584 
585 //#ifdef CHECK_PAGE_RIGHTS
586  // Handle special cases where permission set may have failed silently, leaving a page with no registered hooks,
587  // but with altered EPT permissions. Note that we are interested only in the exit GPA; in reality, the
588  // mem access handler may be called for more addresses - practically, for each individual address accessed
589  // by the instruction. Most of the times, those additional addresses point inside pages that are not hooked,
590  // so there's no need to restore the rights for them. Also, no need to do this if PT filtering is enabled, and we
591  // are in the context of a PT write raised from the agent, as in that case, the page-tables will be RWX by default.
592  // Furthermore, avoid calling the validation routine if the accessed linear address is the same as the page
593  // containing the RIP: if an execution exit was previously generated, we may be in a single-step/re-execution
594  // context inside the HV, so calling SetEPTPageProtection now may break whatever access rights the HV placed in
595  // order to properly re-execute the instruction.
596  if (!*PageHooked && // The page must not be hooked.
597  (PhysicalAddress == gVcpu->ExitGpa) && // The accessed GPA must be the one the exit was triggered on.
598  (LinearAddress == gVcpu->ExitGla) && // The accessed GLA must be the one the exit was triggered on.
599  (access == gVcpu->ExitAccess) && // The access type must be the one generated at exit.
600  ((gVcpu->Regs.Rip & PAGE_MASK) != (LinearAddress & PAGE_MASK)) && // Not the same page with the RIP
601  !gVcpu->PtContext) // Not in PT context, as page tables are writable
602  {
603  IntValidatePageRights(LinearAddress, PhysicalAddress, access);
604  }
605 //#endif
606 
607  *Action = MAX(*Action, finalAction);
608 
609  return INT_STATUS_SUCCESS;
610 }
611 
612 
613 static BOOLEAN
615  _In_ DWORD CpuNumber
616  )
631 {
632  INSTRUX instrux;
633  BYTE code[16];
634  DWORD cbread = 0, csType, ring;
635  QWORD rip = gVcpu->Regs.Rip;
636  QWORD cr3 = gVcpu->Regs.Cr3;
637 
638  // Optimistically read up to 16 bytes starting with the current RIP. This will be more than enough, and it may
639  // span the next page (even if the instruction doesn't), but in this case, the decode will simply succeed.
640  IntVirtMemRead(rip, sizeof(code), cr3, code, &cbread);
641  if (cbread > 0 && cbread < 16)
642  {
643  INTSTATUS status;
644  NDSTATUS ndstatus;
645 
646  // Get the current mode (again), as it's needed for the disassembler.
647  status = IntGetCurrentMode(CpuNumber, &csType);
648  if (!INT_SUCCESS(status))
649  {
650  ERROR("[ERROR] IntGetCurrentMode failed: 0x%08x\n", status);
651  return FALSE;
652  }
653 
654  // Get the current ring. It's needed for the US flag inside the #PF error code.
655  status = IntGetCurrentRing(CpuNumber, &ring);
656  if (!INT_SUCCESS(status))
657  {
658  ERROR("[ERROR] IntGetCurrentRing failed: 0x%08x\n", status);
659  return FALSE;
660  }
661 
662  // Attempt to decode whatever instruction lies at RIP. There are two main possibilities:
663  // 1. The decode succeeds. This means that one or two pages that contain the instruction have already been
664  // swapped in, and there's nothing more we need to do. This is less likely if we get here.
665  // 2. The decode fails because the buffer is too small. This means the instruction spills in the second page,
666  // which is still swapped out. We can inject a #PF on it, and retry. Not injecting a #PF on the second page
667  // would trigger an infinite loop, because the first page would continue generating EPT violations BEFORE the
668  // second page is accessed and gets to generate a #PF.
669  ndstatus = NdDecodeEx(&instrux, code, cbread,
670  (csType == IG_CS_TYPE_64B) ? ND_CODE_64 : ND_CODE_32,
671  (csType == IG_CS_TYPE_64B) ? ND_DATA_64 : ND_DATA_32);
672  if (ND_STATUS_BUFFER_TOO_SMALL == ndstatus)
673  {
674  // We read at least one byte from the page end, but the decode still failed with ND_STATUS_BUFFER_TOO_SMALL.
675  // This means only one thing: the instruction spills inside the next page, and the next page is not present.
676  // Page fault error code: instruction fetch from user/supervisor mode.
677  DWORD pfec = PFEC_ID | (ring == IG_CS_RING_3 ? PFEC_US : 0);
678 
679  status = IntInjectExceptionInGuest(VECTOR_PF, (rip + PAGE_SIZE) & PAGE_MASK, pfec, CpuNumber);
680  if (!INT_SUCCESS(status))
681  {
682  ERROR("[ERROR] IntInjectExceptionInGuest failed: 0x%08x\n", status);
683  return FALSE;
684  }
685 
686  TRACE("[INFO] Fetch retry at GLA 0x%016llx, CR3 0x%016llx\n", rip, cr3);
687 
688  return TRUE;
689  }
690 
691  // All other statuses can fall through, as we cannot (and shouldn't) inject a #PF for them.
692  }
693 
694  // Nothing read, or everything is successful - nothing we should do.
695  return FALSE;
696 }
697 
698 
699 static BOOLEAN
701  _In_ QWORD Gla,
702  _In_ DWORD CpuNumber,
703  _In_ BYTE AccessType
704  )
722 {
723  INTSTATUS status;
724  QWORD cr3 = gVcpu->Regs.Cr3;
725  VA_TRANSLATION tr;
726  DWORD ring;
727 
728  if (0 == (ND_ACCESS_ANY_WRITE & AccessType))
729  {
730  return FALSE;
731  }
732 
733  status = IntGetCurrentRing(CpuNumber, &ring);
734  if (!INT_SUCCESS(status))
735  {
736  ERROR("[ERROR] IntGetCurrentRing failed: 0x%08x\n", status);
737  return FALSE;
738  }
739 
740  if (IG_CS_RING_3 != ring)
741  {
742  return FALSE;
743  }
744 
745  status = IntTranslateVirtualAddressEx(Gla, cr3, TRFLG_NONE, &tr);
746  if (!INT_SUCCESS(status) &&
747  (INT_STATUS_NO_MAPPING_STRUCTURES != status) &&
748  (INT_STATUS_PAGE_NOT_PRESENT != status))
749  {
750  ERROR("[ERROR] IntTranslateVirtualAddressEx failed: 0x%08x\n", status);
751  return FALSE;
752  }
753 
754  if ((INT_STATUS_NO_MAPPING_STRUCTURES == status) ||
755  (INT_STATUS_PAGE_NOT_PRESENT == status) ||
756  (0 == (tr.Flags & PT_P)) ||
757  !tr.IsWritable)
758  {
759  DWORD pfec;
760 
761  // User-mode fault, write access, page may or may not be present.
762  if ((INT_STATUS_NO_MAPPING_STRUCTURES == status) ||
763  (INT_STATUS_PAGE_NOT_PRESENT == status) ||
764  (0 == (tr.Flags & PT_P)))
765  {
766  pfec = 0;
767  }
768  else
769  {
770  pfec = PFEC_P;
771  }
772 
773  pfec |= PFEC_US | PFEC_RW;
774 
775  status = IntInjectExceptionInGuest(VECTOR_PF, Gla, pfec, CpuNumber);
776  if (!INT_SUCCESS(status))
777  {
778  ERROR("[ERROR] IntInjectExceptionInGuest failed: 0x%08x\n", status);
779  return FALSE;
780  }
781 
782  TRACE("[INFO] Xen workaround at GLA 0x%016llx/0x%016llx, CR3 0x%016llx\n", Gla, Gla, cr3);
783 
784  return TRUE;
785  }
786 
787  return FALSE;
788 }
789 
790 
791 static BOOLEAN
793  _In_ QWORD Gla,
794  _In_ DWORD AccessSize,
795  _In_ BYTE AccessType,
796  _In_ DWORD CpuNumber
797  )
810 {
811  QWORD secpg;
812 
813  if ((Gla & 0xFFF) <= ((Gla + AccessSize - 1) & 0xFFF))
814  {
815  return FALSE;
816  }
817 
818  secpg = (Gla + AccessSize - 1) & PAGE_MASK;
819 
820  return IntHandleCowOnPage(secpg, CpuNumber, AccessType);
821 }
822 
823 
824 INTSTATUS
826  _In_ void *GuestHandle,
827  _In_ QWORD PhysicalAddress,
828  _In_ DWORD Length,
829  _In_ QWORD LinearAddress,
830  _In_ DWORD CpuNumber,
831  _Out_ INTRO_ACTION *Action,
832  _In_ IG_EPT_ACCESS AccessType
833  )
888 {
889  INTSTATUS status;
890  INTRO_ACTION action;
891  DWORD glacount, glaidx, pgcnt, pgidx, tsize, asize;
892  QWORD tgla;
893  BOOLEAN cbkFound, probe, pageHooked, cacheuse, cachehit, cacheadd, fetchfail;
894  IG_EPT_ACCESS access;
895 #define MAX_GLAS 32
896  MEMADDR glas[MAX_GLAS] = {0};
897  struct
898  {
899  QWORD gla, gpa;
900  DWORD size;
901  } pages[2];
902 
903  if (GuestHandle == NULL)
904  {
906  }
907 
908  if (Action == NULL)
909  {
911  }
912 
913  action = introGuestAllowed;
914  glacount = glaidx = pgcnt = pgidx = tsize = 0;
915  probe = FALSE;
916  cbkFound = FALSE;
917  pageHooked = FALSE;
918  cachehit = FALSE;
919  cacheadd = FALSE;
920  fetchfail = FALSE;
921 
922  *Action = introGuestAllowed;
923 
925 
926  gEptEvents++;
927 
928  gEventId++;
929 
930 #ifdef CFG_PAUSE_VCPUS_ON_EVENTS
931  IntPauseVcpus();
932 #endif
933 
934 #ifdef CFG_DEBUG_EPT_VIOLATIONS
935  TRACE("[DEBUG] EPT violation for GPA 0x%016llx, GLA 0x%016llx, on CPU %d, type %d\n",
936  PhysicalAddress, LinearAddress, CpuNumber, AccessType);
937 #endif
938 
940  {
941  // We need to exit with success, since most likely the introspection was disabled in the meantime.
943  goto _exit_release;
944  }
945 
946  if (__unlikely(CpuNumber >= gGuest.CpuCount))
947  {
948  ERROR("[ERROR] An EPT exit came for cpu %d, but we have only %d\n", CpuNumber, gGuest.CpuCount);
950  goto _exit_release;
951  }
952 
953  gVcpu = &gGuest.VcpuArray[CpuNumber];
955 
957 
958  status = IntGetGprs(CpuNumber, &gVcpu->Regs);
959  if (!INT_SUCCESS(status))
960  {
961  ERROR("[ERROR] IntGetGprs failed: 0x%08x\n", status);
962  goto _exit_stop_count;
963  }
964 
965  // Get the EPT index in order to determine whether we are in protected view or not.
966  status = IntGetCurrentEptIndex(CpuNumber, &gVcpu->EptpIndex);
967  if (!INT_SUCCESS(status))
968  {
969  ERROR("[ERROR] IntGetCurrentEptIndex failed: 0x%08x\n", status);
970  goto _exit_stop_count;
971  }
972 
974 
975  cacheuse = ((gGuest.Mm.SystemCr3 != 0) && (AccessType != IG_EPT_HOOK_EXECUTE));
976 
978  CpuNumber,
979  &gVcpu->Regs,
980  &gVcpu->Instruction,
981  cacheuse ? 0 : DEC_OPT_NO_CACHE,
982  &cachehit, &cacheadd);
983  if ((INT_STATUS_PAGE_NOT_PRESENT == status) || (INT_STATUS_NO_MAPPING_STRUCTURES == status))
984  {
985  TRACE("[INFO] The page containing the RIP %llx has been swapped out; will retry the instruction.\n",
986  gVcpu->Regs.Cr3);
987 
988  action = introGuestRetry;
989  fetchfail = TRUE;
990  status = INT_STATUS_SUCCESS;
991  goto _exit_pre_ret;
992  }
993  else if (!INT_SUCCESS(status))
994  {
995  ERROR("[ERROR] IntDecDecodeInstructionAtRipWithCache failed: 0x%08x\n", status);
996  goto _exit_stop_count;
997  }
998 
1000 
1001  // We get an exit from the RIP that was allowed to execute, bu this time it's not an exec violation - this means
1002  // that instruction accessed another hooked page, so right now we're in single-step/re-execute context in the HV.
1003  if (gVcpu->AllowOnExec && (gVcpu->AllowOnExecRip == gVcpu->Regs.Rip) &&
1004  (gVcpu->AllowOnExecGpa != PhysicalAddress || (IG_EPT_HOOK_EXECUTE != AccessType)))
1005  {
1006  LOG("[WARNING] We are in reexecute context: RIP = 0x%016llx, GLA = 0x%016llx, GPA = 0x%016llx, ACC = %d\n",
1007  gVcpu->Regs.Rip, LinearAddress, PhysicalAddress, AccessType);
1008  gVcpu->SingleStep = TRUE;
1009  }
1010  else
1011  {
1012  gVcpu->SingleStep = FALSE;
1013  gVcpu->AllowOnExec = FALSE;
1014  gVcpu->AllowOnExecRip = 0;
1015  gVcpu->AllowOnExecGpa = 0;
1016  }
1017 
1018 #ifdef CHECK_PAGE_RIGHTS
1019  if (!gVcpu->SingleStep && !IntValidatePageRightsEx(LinearAddress, PhysicalAddress, AccessType))
1020  {
1021  action = introGuestRetry;
1022  goto _exit_pre_ret;
1023  }
1024 #endif
1025 
1026  // If we use the instruction cache, and we could add the instruction on the cache but it wasn't previously cached
1027  // then retry the instruction, in order to mitigate ToCvsToS attacks.
1028  if (cacheuse && cacheadd && !cachehit)
1029  {
1030  action = introGuestRetry;
1031  goto _exit_pre_ret;
1032  }
1033 
1034 _process_again:
1035  // Fill in the original access information, as generated at VM exit.
1036  gVcpu->ExitGpa = PhysicalAddress;
1037  gVcpu->ExitGla = LinearAddress;
1038  gVcpu->ExitAccess = AccessType;
1039 
1040  // Make sure we reset this on each EPT violation.
1046 
1047 
1048  if ((ND_ACCESS_READ | ND_ACCESS_WRITE) == (gVcpu->Instruction.MemoryAccess & (ND_ACCESS_READ | ND_ACCESS_WRITE)))
1049  {
1052  }
1053 
1054 
1055  // Sometimes, we end up modifying instructions inside the guest memory:
1056  // 1. Instructions that access a switch - case branch table (memtable instructions)
1057  // 2. Instructions that access a PT, when we deploy the PT filtering agent
1058  // In these cases, we may get an exit before the instruction is modified, but by the time we get here, another
1059  // CPU got to modify that instruction. Therefore, we'll have an EPT violation with a peculiar instruction, such
1060  // 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
1061  // take place. However, we must ensure that we avoid hangs, in case one of these instructions legitimately
1062  // causes a read/write EPT violation.
1063  // For JMP and INT 20 on memtables, the treatment is simple, since there will be no match inside the hooks.
1064  if (((gVcpu->Instruction.Instruction == ND_INS_INT3) ||
1065  ((gVcpu->Instruction.Instruction == ND_INS_INT) && (gVcpu->Instruction.Immediate1 == 20))) &&
1067  {
1068  TRACE("[INFO] The instruction at RIP seems to have been modified, will retry the instruction.\n");
1069  action = introGuestRetry;
1070  status = INT_STATUS_SUCCESS;
1071  goto _exit_pre_ret;
1072  }
1073 
1074  // If this instruction is a JMP or a INT 20, check if it was a replace memtable instruction, in which case we
1075  // can safely retry it.
1076  if (((gVcpu->Instruction.Instruction == ND_INS_JMPNR) ||
1077  ((gVcpu->Instruction.Instruction == ND_INS_INT) && (gVcpu->Instruction.Immediate1 == 20))) &&
1078  (IG_EPT_HOOK_EXECUTE != AccessType) && (IntMtblInsRelocated(gVcpu->Regs.Rip)))
1079  {
1080  TRACE("[INFO] The instruction at RIP seems to have been relocated, will retry the instruction.\n");
1081  action = introGuestRetry;
1082  status = INT_STATUS_SUCCESS;
1083  goto _exit_pre_ret;
1084  }
1085 
1086  // Xen always sets the R flag, for every W violation. See if the instruction indeed does read access to the
1087  // memory, and clear the R flag if it doesn't.
1088  if (0 == (gVcpu->Instruction.MemoryAccess & ND_ACCESS_ANY_READ))
1089  {
1090  AccessType &= ~IG_EPT_HOOK_READ;
1091  }
1092 
1093  // Usually, only one GLA will be accessed with a simple Write instruction. In rare cases, however, any combination
1094  // of the following may take place:
1095  // - multiple GLAs accessed (such as PUSH [mem], POP [mem], MOVSD, etc.).
1096  // - RMW or RW access (XOR [mem], r, ADD [mem], r, MOVSD, etc.)
1097  // - page boundary accesses
1098 
1099  if (gVcpu->Instruction.IsRepeated)
1100  {
1101  // If the REP optimizations are disabled, we can handle the access. Otherwise, we will probe the entire access.
1102  probe = (0 == gGuest.RepOptsDisableCount);
1103  }
1104  else
1105  {
1106  // No REP instruction, check if we can re-enable the REP optimizations.
1107  if (gVcpu->RepOptDisabled)
1108  {
1110 
1111  if (0 == --gGuest.RepOptsDisableCount)
1112  {
1114  }
1115  }
1116  }
1117 
1118  // Check if we are in protected EPT, if so, send an alert.
1120  {
1121  // Make sure we have the right thing in CurrVcpu
1122  gVcpu->Gla = LinearAddress;
1123  gVcpu->Gpa = PhysicalAddress;
1124 
1125  status = IntVeHandleEPTViolationInProtectedView(AccessType, &action);
1126  if (!INT_SUCCESS(status))
1127  {
1128  ERROR("[ERROR] IntVeHandleEPTViolationInProtectedView failed: 0x%08x\n", status);
1129  }
1130 
1131  goto done_handling_instruction;
1132  }
1133 
1134  // Handle execution violations. These are special, since an execution fault will be signaled during the fetch phase,
1135  // so no other read or write fault will be reported.
1136  if (AccessType & IG_EPT_HOOK_EXECUTE)
1137  {
1138  // Handle the execute fault, if any. Note that there's no need to handle split-page access for execution faults.
1139  status = IntHandleMemAccess(LinearAddress, PhysicalAddress, gVcpu->Instruction.Length,
1140  &action, &cbkFound, &pageHooked, FALSE, IG_EPT_HOOK_EXECUTE);
1141  if (!INT_SUCCESS(status))
1142  {
1143  ERROR("[ERROR] IntHandleMemAccess failed for 0x%016llx/0x%016llx with size 0x%x for type %d: %08x\n",
1144  PhysicalAddress, LinearAddress, Length, AccessType, status);
1145  }
1146 
1147  // If the restriction has been removed, we can bail out, No need to check other accesses, as they will cause
1148  // fault upon retrying the instruction.
1149  if (introGuestRetry == action)
1150  {
1151  goto done_handling_instruction;
1152  }
1153  }
1154 
1155  // Decode the number of memory locations accessed.
1156  status = IntDecGetAccessedMemCount(&gVcpu->Instruction, &glacount);
1157  if (!INT_SUCCESS(status))
1158  {
1159  ERROR("[ERROR] IntDecGetAccessedMemCount failed: 0x%x\n", status);
1160  status = INT_STATUS_NOT_SUPPORTED;
1161  goto _exit_stop_count;
1162  }
1163 
1164  // Handle memory accesses.
1165  if (glacount == 0)
1166  {
1167  // No access - we can leave now.
1168  goto done_handling_instruction;
1169  }
1170  else if ((glacount == 1) && !(AccessType & IG_EPT_HOOK_EXECUTE))
1171  {
1174 
1175  // A single address is accessed, this is easy to handle. The accessed GLA must be the provided LinearAddress.
1176  glas[0].Gla = LinearAddress;
1177  // Use the access as indicated by the Instrux - Xen always send RW access for every W fault.
1178  glas[0].Access = AccessType;
1179 
1181  &gVcpu->Regs, LinearAddress,
1182  gVcpu->Instruction.MemoryAccess, &glas[0].Size);
1183  if (!INT_SUCCESS(status))
1184  {
1185  char text[ND_MIN_BUF_SIZE];
1186  NdToText(&gVcpu->Instruction, 0, ND_MIN_BUF_SIZE, text);
1187  ERROR("[ERROR] IntDecDecodeAccessSize failed: 0x%08x for instruction '%s' "
1188  "with access %d GLA = 0x%016llx, GPA = 0x%016llx\n",
1189  status,
1190  text,
1191  AccessType,
1192  LinearAddress,
1193  PhysicalAddress);
1194 
1198  IntHookGpaDump();
1199  status = INT_STATUS_NOT_SUPPORTED;
1200  goto _exit_stop_count;
1201  }
1202 
1203  if (0 == glas[0].Size)
1204  {
1205  char text[ND_MIN_BUF_SIZE];
1206  NdToText(&gVcpu->Instruction, 0, ND_MIN_BUF_SIZE, text);
1207  WARNING("[WARNING] Access size 0 returned for instruction '%s' "
1208  "with access %d GLA = 0x%016llx, GPA = 0x%016llx\n",
1209  text,
1210  AccessType,
1211  LinearAddress,
1212  PhysicalAddress);
1213 
1217  goto done_handling_instruction;
1218  }
1219  }
1220  else
1221  {
1222  // Multiple addresses accessed, decode each and every one of them.
1223  glacount = MAX_GLAS;
1224 
1226  &gVcpu->Regs, NULL, glas, &glacount);
1227  if (!INT_SUCCESS(status))
1228  {
1229  ERROR("[ERROR] IntDecGetAccessedMem failed: 0x%x\n", status);
1230  status = INT_STATUS_NOT_SUPPORTED;
1231  goto _exit_stop_count;
1232  }
1233  }
1234 
1235  // Handle each accessed address.
1236  for (glaidx = 0; glaidx < glacount; glaidx++)
1237  {
1238  // This is REP instruction and the REP optimization is enabled. Handle up until the end of page.
1239  if (probe)
1240  {
1241  glas[glaidx].Size = (DWORD)MIN(glas[glaidx].Size * gVcpu->Regs.Rcx,
1242  PAGE_REMAINING(glas[glaidx].Gla));
1243  }
1244 
1245  tgla = glas[glaidx].Gla;
1246  tsize = glas[glaidx].Size;
1247  asize = 0;
1248  pgidx = pgcnt = 0;
1249 
1250  // Workaround for the RMW at page boundary emulation issue:
1251  // - The access must be write (specifically it must be RMW, but we handle any write)
1252  // - The access must be at a page boundary
1253  // - The second page must be read-only or COW
1254  // - We must be in user mode (there is no COW in kernel)
1255 
1256  if (IntHandlePageBoundaryCow(glas[glaidx].Gla, glas[glaidx].Size, glas[glaidx].Access, CpuNumber))
1257  {
1258  action = introGuestRetry;
1259 
1260  // we can skip processing the rest of the instruction, since we will retry it & we injected a #PF.
1261  goto done_handling_instruction;
1262  }
1263 
1264  // Translate each accessed page for this memory access.
1265  while (tsize != 0)
1266  {
1267  pages[pgidx].gla = tgla;
1268  pages[pgidx].size = tsize;
1269 
1270  if ((tgla & PAGE_MASK) == (LinearAddress & PAGE_MASK))
1271  {
1272  // We don't have to do the page walk if the linear address is the same page as the one passed to the
1273  // callback. We can use the provided PhysicalAddress page instead.
1274  pages[pgidx].gpa = (PhysicalAddress & PHYS_PAGE_MASK) + (tgla & PAGE_OFFSET);
1275  }
1276  else
1277  {
1278  status = IntTranslateVirtualAddress(tgla, gVcpu->Regs.Cr3, &pages[pgidx].gpa);
1279  if (!INT_SUCCESS(status))
1280  {
1281  // We do not care about translation failures; if there would be a #PF generated for this access,
1282  // it would not bother us, no matter what action we return:
1283  // 1. allow -> emulation (and injection of #PF if needed) or single-step (#PF would be generated
1284  // naturally)
1285  // 2. allowed virtual -> we handled the instruction entirely, integrator/HV does nothing
1286  // 3. allowed patched -> emulation with context setting (and injection of #PF if needed)
1287  // 4. not allowed -> we will skip the instruction, no matter what
1288  // 5. ignored -> we shouldn't do anything anyway
1289  // 6. retry -> the instruction will be retried normally; eventually, some different action will
1290  // be returned; this only happens if the current faulted page is not present or if the
1291  // page restrictions have been removed. Note that retry will be returned only when explicitly
1292  // removing protection from the current page after an execution attempt.
1293  goto done_handling_instruction;
1294  }
1295  }
1296 
1297  asize = MIN(tsize, PAGE_REMAINING(tgla));
1298  tgla += asize;
1299  tsize -= asize;
1300 
1301  pgidx++, pgcnt++;
1302  }
1303 
1304  // If the instruction does a RMW access on the address, we need to call both the read and the write handlers.
1305  access = 0;
1306 
1307  if (glas[glaidx].Access & ND_ACCESS_ANY_READ)
1308  {
1309  access |= IG_EPT_HOOK_READ;
1310  }
1311 
1312  if (glas[glaidx].Access & ND_ACCESS_ANY_WRITE)
1313  {
1314  access |= IG_EPT_HOOK_WRITE;
1315  }
1316 
1317  // Handle the access.
1318  for (pgidx = 0; pgidx < pgcnt; pgidx++)
1319  {
1320  status = IntHandleMemAccess(pages[pgidx].gla, pages[pgidx].gpa, glas[glaidx].Size,
1321  &action, &cbkFound, &pageHooked, probe, access);
1322  if (!INT_SUCCESS(status))
1323  {
1324  ERROR("[ERROR] IntHandleMemAccess failed for 0x%016llx/0x%016llx with size 0x%x for type %d: %08x\n",
1325  pages[pgidx].gpa, glas[glaidx].Gla, glas[glaidx].Size, access, status);
1326  }
1327 
1328  // We asked for probe only (for REPed instruction) and a callback was found. Disable the REP optimization
1329  // and retry the REPed instruction step by step.
1330  if (probe && cbkFound)
1331  {
1333 
1334  if (0 == gGuest.RepOptsDisableCount++)
1335  {
1337 
1338  action = introGuestRetry;
1339 
1340  goto done_handling_instruction;
1341  }
1342  }
1343  }
1344  }
1345 
1346 done_handling_instruction:
1347 
1348  // Handle patched accesses.
1349  if (introGuestAllowedPatched == action)
1350  {
1352 
1353  // The patch buffer is reset on each EPT violation callback invocation.
1354  if (pb->Valid)
1355  {
1356  pb->Valid = FALSE;
1357 
1358 #ifndef USER_MODE
1359  // Try to emulate the read
1360  status = IntDecEmulateRead(&gVcpu->Instruction, pb->Data);
1361  if (!INT_SUCCESS(status))
1362  {
1363  ERROR("[ERROR] IntDecEmulateRead failed: 0x%08x\n", status);
1364  }
1365  else
1366  {
1367  action = introGuestAllowedVirtual;
1368  goto _skip_emu_ctx;
1369  }
1370 #endif // !USER_MODE
1371 
1372  status = IntSetIntroEmulatorContext(CpuNumber, pb->Gla, pb->Size, pb->Data);
1373  if (!INT_SUCCESS(status))
1374  {
1375  ERROR("[ERROR] IntSetIntroEmulatorContext failed: 0x%08x\n", status);
1377  goto _exit_stop_count;
1378  }
1379 
1380 #ifndef USER_MODE
1381 _skip_emu_ctx:
1382  ;
1383 #endif // !USER_MODE
1384  }
1385  else
1386  {
1387  ERROR("[ERROR] IntroGuestAllowedPatched is requested, but the patch buffer is not valid!\n");
1390  goto _exit_stop_count;
1391  }
1392  }
1393 
1394  // Handle PT accesses. These will be emulated in-place.
1395  if (gVcpu->PtEmuBuffer.Emulated)
1396  {
1399 
1400  // On PAE, PT writes are done in two steps, by two instructions (one for the low DWORD, one for the high DWORD)
1401  // In this case we will have two exits; avoid that by checking that the next instruction completes the PT
1402  // write started by the current one
1404  {
1405  QWORD nextGla = 0;
1406 
1407  // At this point we already emulated the first instruction so the RIP is pointing to the next one
1409  CpuNumber,
1410  &gVcpu->Regs,
1411  &gVcpu->Instruction,
1412  gGuest.Mm.SystemCr3 != 0 ? 0 : DEC_OPT_NO_CACHE,
1413  NULL,
1414  NULL);
1415  if (!INT_SUCCESS(status))
1416  {
1417  goto _bail_out_of_next_emu;
1418  }
1419 
1420  if ((gVcpu->Instruction.Instruction != ND_INS_MOV) &&
1421  (gVcpu->Instruction.Instruction != ND_INS_XCHG) &&
1422  (gVcpu->Instruction.Instruction != ND_INS_CMPXCHG))
1423  {
1424  goto _bail_out_of_next_emu;
1425  }
1426 
1427  // Make sure that the second instruction is indeed one that modifies the
1428  // memory and that the access size is 4
1429  if ((gVcpu->Instruction.Operands[0].Type != ND_OP_MEM) ||
1430  (gVcpu->Instruction.Operands[0].Size != 4))
1431  {
1432  goto _bail_out_of_next_emu;
1433  }
1434 
1436  &gVcpu->Regs, &nextGla);
1437  if (!INT_SUCCESS(status))
1438  {
1439  goto _bail_out_of_next_emu;
1440  }
1441 
1442  // There are two cases that interest us:
1443  // MOV dword ptr [edi], ecx
1444  // MOV dword ptr [edi + 4], ecx
1445  // Or:
1446  // MOV dword ptr [edi + 4], ecx
1447  // MOV dword ptr [edi], ecx
1448  // Check that we are in one of these, bail out if not
1449  if ((nextGla & ~7ull) != (LinearAddress & ~7ull))
1450  {
1451  goto _bail_out_of_next_emu;
1452  }
1453 
1454  // The gla was already computed by IntDecDecodeDestinationLinearAddressFromInstruction
1455  // Depending on the two cases from above, the pair of instructions already updated the low DWORD or the
1456  // high DWORD, update the PhysicalAddress to point to the right part of the PT entry
1457  LinearAddress = nextGla;
1458  PhysicalAddress = (PhysicalAddress & PHYS_PAGE_MASK) + (nextGla & PAGE_OFFSET);
1459 
1460  goto _process_again;
1461  }
1462 
1463 _bail_out_of_next_emu:
1464 
1465  action = introGuestAllowedVirtual;
1466  }
1467 
1468 _exit_pre_ret:
1469  // Special handling for introGuestRetry on EPT exec violations. We may request a retry when we cannot fetch the RIP,
1470  // for example. Sometimes, the instruction may be situated at a page boundary and be contained inside both pages.
1471  // In this case, if at least the first page is exec hooked and the second one is not present, we will induce an
1472  // infinite loop by requesting introGuestRetry, because the #PF on the second page will never be triggered since the
1473  // EPT violation on the first one will always take place first. In this case, we will manually inject the #PF on
1474  // the second page.
1475  if (action == introGuestRetry && // Retry requested.
1476  AccessType == IG_EPT_HOOK_EXECUTE && // Execute fault.
1477  fetchfail && // Instruction fetch failed due to page miss.
1478  (gVcpu->Regs.Rip & 0xFFF) + 15 > 0x1000) // There are chances the instruction spills.
1479  {
1480  // NOTE: This does not conflict with IntHandlePageBoundaryCow, as that one is triggered on write faults.
1482  }
1483 
1484  // Handle introGuestAllowed on execution violations. These will end up being re-executed by Xen, so we must be
1485  // careful not to modify their access rights while they're re-executed.
1486  if (action == introGuestAllowed && AccessType == IG_EPT_HOOK_EXECUTE)
1487  {
1488  gVcpu->AllowOnExec = TRUE;
1491  }
1492 
1493 
1494  // Handle pre-return events. Don't inject #PF if we already injected one for the Xen workaround.
1496  if (!INT_SUCCESS(status))
1497  {
1498  ERROR("[ERROR] IntGuestPreReturnCallback failed: 0x%08x\n", status);
1499  }
1500 
1501  status = INT_STATUS_SUCCESS;
1502 
1504  {
1506  }
1507 
1508 _exit_stop_count:
1510 
1512 
1513 _exit_release:
1515  {
1516  ERROR("[ERROR] EPT callback set DisableOnReturn... We will try to disable introcore...\n");
1517 
1519 
1520  status = INT_STATUS_FATAL_ERROR;
1521  }
1522 
1523 #ifdef CFG_PAUSE_VCPUS_ON_EVENTS
1524  IntResumeVcpus();
1525 #endif
1526 
1528 
1529  *Action = action;
1530 
1531  return status;
1532 }
1533 
1534 
1535 INTSTATUS
1537  _In_ void *GuestHandle,
1538  _In_ DWORD Msr,
1539  _In_ IG_MSR_HOOK_TYPE Flags,
1540  _Out_ INTRO_ACTION *Action,
1541  _In_opt_ QWORD OriginalValue,
1542  _Inout_opt_ QWORD *NewValue,
1543  _In_ DWORD CpuNumber
1544  )
1568 {
1569  INTSTATUS status;
1570  BOOLEAN found;
1571  BOOLEAN reinjectPerfAgent;
1572 
1573  if (GuestHandle == NULL)
1574  {
1576  }
1577 
1578  if (Action == NULL)
1579  {
1581  }
1582 
1583  found = FALSE;
1584 
1585  *Action = introGuestAllowed;
1586 
1588 
1589  gEventId++;
1590 
1591 #ifdef CFG_PAUSE_VCPUS_ON_EVENTS
1592  IntPauseVcpus();
1593 #endif
1594 
1596  {
1597  // We need to exit with success, since most likely the introspection was disabled in the meantime.
1599  goto _exit_release;
1600  }
1601 
1602  if (__unlikely(CpuNumber >= gGuest.CpuCount))
1603  {
1604  ERROR("[ERROR] A MSR exit came for cpu %d, but we have only %d\n", CpuNumber, gGuest.CpuCount);
1606  goto _exit_release;
1607  }
1608 
1609  gVcpu = &gGuest.VcpuArray[CpuNumber];
1611 
1612  status = IntGetGprs(CpuNumber, &gVcpu->Regs);
1613  if (!INT_SUCCESS(status))
1614  {
1615  ERROR("[ERROR] IntGetGprs failed: 0x%08x\n", status);
1616  goto _exit_release;
1617  }
1618 
1620 
1622  {
1623  if (Msr == pHook->Msr)
1624  {
1625  found = TRUE;
1626 
1627  if (pHook->Disabled)
1628  {
1629  continue;
1630  }
1631 
1632  status = pHook->Callback(Msr, Flags, Action, pHook->Context, OriginalValue, NewValue);
1633 
1634  if (INT_STATUS_REMOVE_HOOK_ON_RET == status)
1635  {
1636  status = IntHookMsrRemoveHook(pHook);
1637  if (!INT_SUCCESS(status))
1638  {
1639  ERROR("[ERROR] IntHookMsrRemoveHook failed: 0x%08x\n", status);
1640  }
1641  }
1642  }
1643  }
1644 
1645  // Only try to re-inject the PT Filter if the LSTAR was initialized
1646  reinjectPerfAgent = (IG_IA32_LSTAR == Msr) && (0 == OriginalValue) && (NULL != NewValue) && (0 != *NewValue);
1647 
1649 
1650  // Handle pre-return events.
1652  POST_COMMIT_MSR |
1653  POST_INJECT_PF |
1654  (reinjectPerfAgent ? POST_RETRY_PERFAGENT : 0));
1655  if (!INT_SUCCESS(status))
1656  {
1657  ERROR("[ERROR] IntGuestPreReturnCallback failed: 0x%08x\n", status);
1658  }
1659 
1660  if (found)
1661  {
1662  status = INT_STATUS_SUCCESS;
1663  }
1664  else
1665  {
1666  status = INT_STATUS_NOT_FOUND;
1667  }
1668 
1670 
1671 _exit_release:
1673  {
1674  ERROR("[ERROR] MSR callback set DisableOnReturn... We will try to disable introcore...\n");
1675 
1677 
1678  status = INT_STATUS_FATAL_ERROR;
1679  }
1680 
1681 #ifdef CFG_PAUSE_VCPUS_ON_EVENTS
1682  IntResumeVcpus();
1683 #endif
1684 
1686 
1687  return status;
1688 }
1689 
1690 
1691 INTSTATUS
1693  _In_ void *GuestHandle,
1694  _In_ DWORD Cr,
1695  _In_ DWORD CpuNumber,
1696  _In_ QWORD OldValue,
1697  _In_ QWORD NewValue,
1698  _Out_ INTRO_ACTION *Action
1699  )
1720 {
1721  INTSTATUS status;
1722  BOOLEAN found;
1723  INTRO_ACTION action;
1724 
1725  if (GuestHandle == NULL)
1726  {
1728  }
1729 
1730  if (Action == NULL)
1731  {
1733  }
1734 
1735  found = FALSE;
1736  action = introGuestAllowed;
1737 
1738  *Action = introGuestAllowed;
1739 
1741 
1742  gEventId++;
1743 
1744 #ifdef CFG_PAUSE_VCPUS_ON_EVENTS
1745  IntPauseVcpus();
1746 #endif
1747 
1749  {
1750  // We need to exit with success, since most likely the introspection was disabled in the meantime.
1752  goto _exit_release;
1753  }
1754 
1755  if (__unlikely(CpuNumber >= gGuest.CpuCount))
1756  {
1757  ERROR("[ERROR] A CR exit came for cpu %d, but we have only %d\n", CpuNumber, gGuest.CpuCount);
1759  goto _exit_release;
1760  }
1761 
1762  gVcpu = &gGuest.VcpuArray[CpuNumber];
1764 
1765  status = IntGetGprs(CpuNumber, &gVcpu->Regs);
1766  if (!INT_SUCCESS(status))
1767  {
1768  ERROR("[ERROR] IntGetGprs failed: 0x%08x\n", status);
1769  goto _exit_release;
1770  }
1771 
1773 
1775  {
1776  if (Cr == pHook->Cr)
1777  {
1778  found = TRUE;
1779 
1780  if (pHook->Disabled)
1781  {
1782  continue;
1783  }
1784 
1785  status = pHook->Callback(pHook->Context, Cr, OldValue, NewValue, &action);
1786 
1787  if (INT_STATUS_REMOVE_HOOK_ON_RET == status)
1788  {
1789  status = IntHookCrRemoveHook(pHook);
1790  if (!INT_SUCCESS(status))
1791  {
1792  ERROR("[ERROR] IntHookCrRemoveHook failed: 0x%08x\n", status);
1793  }
1794  }
1795 
1796  if (action > *Action)
1797  {
1798  *Action = action;
1799  }
1800  }
1801  }
1802 
1804 
1806  if (!INT_SUCCESS(status))
1807  {
1808  ERROR("[ERROR] IntGuestPreReturnCallback failed: 0x%08x\n", status);
1809  }
1810 
1811  if (found)
1812  {
1813  status = INT_STATUS_SUCCESS;
1814  }
1815  else
1816  {
1817  status = INT_STATUS_NOT_FOUND;
1818  }
1819 
1821 
1822 _exit_release:
1824  {
1825  ERROR("[ERROR] CR%d callback set DisableOnReturn... We will try to disable introcore...\n", Cr);
1826 
1828 
1829  status = INT_STATUS_FATAL_ERROR;
1830  }
1831 
1832 #ifdef CFG_PAUSE_VCPUS_ON_EVENTS
1833  IntResumeVcpus();
1834 #endif
1835 
1837 
1838  return status;
1839 }
1840 
1841 
1842 static INTSTATUS
1844  void
1845  )
1860 {
1861  INTSTATUS status;
1862  BOOLEAN found, hooked;
1863  INTRO_ACTION action;
1864 
1866 
1867  action = introGuestAllowed;
1868  found = hooked = FALSE;
1869 
1870  gVcpu->PtContext = TRUE;
1871 
1874 
1879  gVcpu->Gpa = gVcpu->Regs.R9;
1880  gVcpu->Gla = gVcpu->Regs.R8;
1881  gVcpu->AccessSize = 8;
1882 
1883  // Handle EPT violations that came as #VE at our in-guest agent. We treat them as direct EPT violations inside the
1884  // mem access handler. Note that we don't handle multi-accesses here, since a #VE will only be triggered on single
1885  // known accesses - page tables, for now.
1886  status = IntHandleMemAccess(gVcpu->Gla, gVcpu->Gpa, 8, &action, &found, &hooked, FALSE, IG_EPT_HOOK_WRITE);
1887  if (!INT_SUCCESS(status))
1888  {
1889  ERROR("[ERROR] IntHandleEptViolation failed: 0x%08x\n", status);
1891  return status;
1892  }
1893 
1894  if (!hooked)
1895  {
1896  status = IntPtiCacheAdd(gVcpu->Gpa);
1897  if (!INT_SUCCESS(status))
1898  {
1899  ERROR("[ERROR] IntPtsInt3CacheAdd failed for 0x%016llx: 0x%08x\n", gVcpu->Gpa, status);
1900  }
1901  }
1902 
1906 
1907  gVcpu->PtContext = FALSE;
1908 
1910 
1911  return INT_STATUS_SUCCESS;
1912 }
1913 
1914 
1915 static INTSTATUS
1917  void
1918  )
1938 {
1939  INTSTATUS status;
1940  BOOLEAN found, hooked, paused;
1941  QWORD eptvGpa, eptvGla;
1942  DWORD violType, mode;
1943  BYTE accessType;
1944  INTRO_ACTION action;
1945 
1946  action = introGuestAllowed;
1947  found = hooked = paused = FALSE;
1948 
1949  if (0 == (gVcpu->VeInfoPage->Qualification & (1ULL << 8)))
1950  {
1951  // This is a page-walk. The only reason the #VE agent asks for a page walk is to set the A bit inside the
1952  // user-mode CR3 of the current process. This happens because #VE agent makes the page-walk in the context
1953  // of the current CR3, but since it runs in kernel, the current CR3 will be the kernel CR3 (if KPTI is on).
1954  // Although normally all non-leaf entries are marked A/D, it seems that sometimes, an entry inside the user
1955  // CR3 PML4 is NOT accessed, causing an infinite loop due to the page-walker.
1956  PWIN_PROCESS_OBJECT pProc;
1957  PQWORD pPml4e;
1958 
1960  {
1961  ERROR("[ERROR] #VE is supported only on Windows, how did we end up here?\n");
1962  return INT_STATUS_NOT_SUPPORTED;
1963  }
1964 
1965  TRACE("[#VE] Handling special user-mode page-walk, CR3 0x%016llx, GLA 0x%016llx\n",
1967 
1969  if (NULL == pProc)
1970  {
1971  ERROR("[ERROR] No process found for CR3 0x%016llx!\n", gVcpu->Regs.Cr3);
1972  return INT_STATUS_NOT_FOUND;
1973  }
1974 
1975  if ((pProc->UserCr3 != pProc->Cr3) && (pProc->UserCr3 >= 0x1000))
1976  {
1977  QWORD oldVal;
1978 
1980  if (!INT_SUCCESS(status))
1981  {
1982  ERROR("[ERROR] IntGpaCacheFindAndAdd failed for GPA 0x%016llx: 0x%08x\n",
1983  CLEAN_PHYS_ADDRESS64(pProc->UserCr3), status);
1984  return status;
1985  }
1986 
1988 
1989  oldVal = *pPml4e;
1990 
1991  if (0 != (oldVal & PML4_P))
1992  {
1993  QWORD newVal;
1994 
1995  newVal = oldVal | PML4_A;
1996 
1997  _InterlockedCompareExchange64((INT64 *)pPml4e, (INT64)newVal, (INT64)oldVal);
1998  }
1999 
2001  }
2002 
2003  return INT_STATUS_SUCCESS;
2004  }
2005 
2006  // From here on, we're inside #VE context.
2007  gVcpu->VeContext = TRUE;
2008 
2009  // Fake the current EPTP index to be the default EPT view - we are normally in the protected view now, but the #VE
2010  // took place in the default view.
2011  gVcpu->EptpIndex = 0;
2012 
2013  // Fetch the GPA and GLA from the guest.
2015  eptvGla = gVcpu->VeInfoPage->GuestLinearAddress;
2016  violType = (DWORD)gVcpu->VeInfoPage->Qualification;
2017 
2018  // Copy the #VE registers from the #VE info page; these are the real registers that were loaded when the violation
2019  // was triggered. Note that we do not save the old registers, because while we're inside #VE context, we inhibit
2020  // the modification of the real, global registers. Any SetGprs made from within the #VE context will only affect
2021  // the local cache (gVcpu->Regs) which will be used to propagate the new values in the #VE info page.
2040 
2041  violType &= 0x7;
2042 
2043  if (violType & 4)
2044  {
2045  accessType = IG_EPT_HOOK_EXECUTE;
2046  }
2047  else if (violType & 2)
2048  {
2049  accessType = IG_EPT_HOOK_READ | IG_EPT_HOOK_WRITE;
2050  }
2051  else
2052  {
2053  accessType = IG_EPT_HOOK_READ;
2054  }
2055 
2056  // Get the current operating mode. This should always be 64 bit, since we support #VE on 64 bit only.
2057  status = IntGetCurrentMode(gVcpu->Index, &mode);
2058  if (!INT_SUCCESS(status))
2059  {
2060  ERROR("[ERROR] IntGetCurrentMode failed: 0x%08x\n", status);
2061  goto cleanup_and_exit;
2062  }
2063 
2064  // Decode the already fetched instruction from the #VE info page.
2066  if (!INT_SUCCESS(status))
2067  {
2068  ERROR("[ERROR] IntDecDecodeInstructionFromBuffer failed: 0x%08x\n", status);
2069  goto cleanup_and_exit;
2070  }
2071 
2072  if (IntVeIsAgentRemapped(eptvGla))
2073  {
2074  IntPauseVcpus();
2075  paused = TRUE;
2076  }
2077 
2079 
2084 
2086  if (!INT_SUCCESS(status))
2087  {
2088  ERROR("[ERROR] IntGpaCachePatchAndAdd failed: 0x%08x\n", status);
2089  goto cleanup_and_exit;
2090  }
2091 
2092  // Handle EPT violations that came as #VE at our in-guest agent. We treat them as direct EPT violations inside the
2093  // mem access handler. Note that we don't handle multi-accesses here, since a #VE will only be triggered on single
2094  // known accesses - page tables, for now.
2095  status = IntHandleMemAccess(eptvGla, eptvGpa, 8, &action, &found, &hooked, FALSE, accessType);
2096  if (!INT_SUCCESS(status))
2097  {
2098  ERROR("[ERROR] IntHandleEptViolation failed: 0x%08x\n", status);
2099  goto cleanup_and_exit;
2100  }
2101 
2102 cleanup_and_exit:
2103  if (paused)
2104  {
2105  IntResumeVcpus();
2106  }
2107 
2108  // Copy the (possibly) modified VE regs back to the #VE info page.
2127 
2128  // Done with the #VE context.
2132 
2133  gVcpu->VeContext = FALSE;
2134 
2135  return INT_STATUS_SUCCESS;
2136 }
2137 
2138 
2139 INTSTATUS
2141  _In_ void *GuestHandle,
2142  _In_ QWORD Rip,
2143  _In_ DWORD CpuNumber
2144  )
2172 {
2173  INTSTATUS status;
2174  BOOLEAN bFound, bRaiseEptPt, bRaiseEptVe;
2175 
2176  bFound = bRaiseEptPt = bRaiseEptVe = FALSE;
2177 
2178  if (NULL == GuestHandle)
2179  {
2181  }
2182 
2184 
2185  gEventId++;
2186 
2187 #ifdef CFG_PAUSE_VCPUS_ON_EVENTS
2188  IntPauseVcpus();
2189 #endif
2190 
2192  {
2193  // We need to exit with success, since most likely the introspection was disabled in the meantime.
2195  goto _exit_release;
2196  }
2197 
2198  if (__unlikely(CpuNumber >= gGuest.CpuCount))
2199  {
2200  ERROR("[ERROR] A VMCALL exit came for cpu %d, but we have only %d\n", CpuNumber, gGuest.CpuCount);
2202  goto _exit_release;
2203  }
2204 
2205  gVcpu = &gGuest.VcpuArray[CpuNumber];
2207 
2208  status = IntGetGprs(CpuNumber, &gVcpu->Regs);
2209  if (!INT_SUCCESS(status))
2210  {
2211  ERROR("[ERROR] IntGetGprs failed: 0x%08x\n", status);
2212  goto _exit_release;
2213  }
2214 
2216 
2217  // Check the #VE agent.
2218  if (!bFound)
2219  {
2221  {
2222  bFound = TRUE;
2223 
2224  status = IntVeHandleHypercall(CpuNumber);
2225  if (!INT_SUCCESS(status))
2226  {
2227  ERROR("[ERROR] IntVeHandleHypercall failed: 0x%08x\n", status);
2228  }
2229 
2230  if (INT_STATUS_RAISE_EPT == status)
2231  {
2232  bRaiseEptVe = TRUE;
2233  }
2234  }
2235  }
2236 
2237  // Check the PT cache agent.
2238  if (!bFound)
2239  {
2241  {
2242  if (gVcpu->Regs.R8 == 0)
2243  {
2244  // Request to remove the instruction, as it's faulty.
2246  }
2247  else
2248  {
2249  // We assume only the raise-EPT VMCALL can be issued.
2250  bRaiseEptPt = TRUE;
2251  }
2252 
2253  bFound = TRUE;
2254  }
2255  }
2256 
2257  if (!bFound)
2258  {
2259  // Call the guest detours.
2260  status = IntDetCallCallback();
2261  if (!INT_SUCCESS(status))
2262  {
2263  if (INT_STATUS_NOT_FOUND != status)
2264  {
2265  ERROR("[ERROR] IntDetourCallCallback failed: 0x%08x\n", status);
2266  }
2267  }
2268  else
2269  {
2270  bFound = TRUE;
2271  }
2272  }
2273 
2274  if (!bFound)
2275  {
2276  // Call the generic agent handler
2277  status = IntAgentHandleVmcall(Rip);
2278  if (!INT_SUCCESS(status))
2279  {
2280  ERROR("[ERROR] IntAgentHandleVmcall failed: 0x%08x\n", status);
2281  }
2282  else
2283  {
2284  bFound = TRUE;
2285  }
2286  }
2287 
2288  if (bRaiseEptPt)
2289  {
2291 
2292  status = IntDispatchPtAsEpt();
2293  if (!INT_SUCCESS(status))
2294  {
2295  ERROR("[ERROR] IntDispatchPtAsEpt failed: 0x%08x\n", status);
2296  }
2297  }
2298  else if (bRaiseEptVe)
2299  {
2301 
2302  status = IntDispatchVeAsEpt();
2303  if (!INT_SUCCESS(status))
2304  {
2305  ERROR("[ERROR] IntDispatchVeAsEpt failed: 0x%08x\n", status);
2306  }
2307  }
2308 
2310 
2311  // Handle pre-return events.
2313  if (!INT_SUCCESS(status))
2314  {
2315  ERROR("[ERROR] IntGuestPreReturnCallback failed: 0x%08x\n", status);
2316  }
2317 
2319 
2320  if (bFound)
2321  {
2322  status = INT_STATUS_SUCCESS;
2323  }
2324  else
2325  {
2326  status = INT_STATUS_NOT_FOUND;
2327  }
2328 
2329 _exit_release:
2331  {
2332  ERROR("[ERROR] VMCALL callback set DisableOnReturn... We will try to disable introcore...\n");
2333 
2335 
2336  status = INT_STATUS_FATAL_ERROR;
2337  }
2338 
2339  if (INT_SUCCESS(status) && gGuest.BugCheckInProgress)
2340  {
2341  LOG("[INFO] VMCALL callback set BugCheckInProgress... We will try to disable introcore...\n");
2342 
2344 
2345  status = INT_STATUS_UNINIT_BUGCHECK;
2346  }
2347 
2348 #ifdef CFG_PAUSE_VCPUS_ON_EVENTS
2349  IntResumeVcpus();
2350 #endif
2351 
2353 
2354  return status;
2355 }
2356 
2357 
2358 INTSTATUS
2360  _In_ void *GuestHandle
2361  )
2393 {
2394  INTSTATUS status;
2395 
2396  if (NULL == GuestHandle)
2397  {
2399  }
2400 
2402 
2403  gEventId++;
2404 
2405 #if defined(CFG_PAUSE_VCPUS_ON_EVENTS)
2406  IntPauseVcpus(); // Must pause on Xen, as the VCPUs run when the timer events comes.
2407 #endif
2408 
2409  if (!gGuest.Initialized)
2410  {
2411  // We need to exit with success, since most likely the introspection was disabled in the meantime.
2413  goto release_and_exit;
2414  }
2415 
2416  gVcpu = &gGuest.VcpuArray[0];
2417 
2418  if (gGuest.ShutDown)
2419  {
2420  status = INT_STATUS_SUCCESS;
2421  goto release_and_exit;
2422  }
2423 
2424  gGuest.TimerCalls++;
2425 
2427 
2429  {
2434 
2435  // Don't check anything until the exceptions are not loaded, we may end up excepting unwanted writes
2437  {
2438  // Once every timer tick, on static init, try to initialize Infinity Hook protection
2440  {
2441  status = IntWinInfHookProtect();
2442  if (!INT_SUCCESS(status))
2443  {
2444  ERROR("[ERROR] IntWinInfHookProtect failed: 0x%08x\n", status);
2445  }
2446  }
2447 
2448  // Once every timer tick, do the integrity checks.
2449  status = IntIntegrityCheckAll();
2450  if (!INT_SUCCESS(status))
2451  {
2452  ERROR("[ERROR] IntIntegrityCheckAll failed: 0x%08x\n", status);
2453  }
2454 
2455  // Once every timer tick, make sure no process tokens have been stolen by another processes,
2456  // and no token privileges have been modified in a malicious way, indicating a LPE.
2457  status = IntWinTokenCheckIntegrity();
2458  if (!INT_SUCCESS(status))
2459  {
2460  ERROR("[ERROR] IntWinTokenCheckIntegrity failed: 0x%x\n", status);
2461  }
2462 
2463  // Once every timer tick, make sure no process security descriptor pointers or ACLs (SACL/DACL) have
2464  // been modified.
2465  status = IntWinSDCheckIntegrity();
2466  if (!INT_SUCCESS(status))
2467  {
2468  ERROR("[ERROR] IntWinSDCheckIntegrity failed: 0x%x\n", status);
2469  }
2470 
2471  // Once every timer tick, make sure that the System CR3 remained the same.
2472  status = IntWinProcValidateSystemCr3();
2473  if (!INT_SUCCESS(status))
2474  {
2475  ERROR("[ERROR] IntWinProcValidateSystemCr3 failed: 0x%08x\n", status);
2476  }
2477 
2478  // Once every timer tick, validate self map entries
2480  if (!INT_SUCCESS(status))
2481  {
2482  ERROR("[ERROR] IntWinProcValidateSelfMapEntries failed: 0x%08x\n", status);
2483  }
2484 
2485  // Once every timer tick, verify SharedUserData integrity.
2486  status = IntWinSudCheckIntegrity();
2487  if (!INT_SUCCESS(status))
2488  {
2489  ERROR("[ERROR] IntWinSudCheckIntegrity failed: 0x%08x\n", status);
2490  }
2491 
2492  }
2493 
2494  // Re-schedule timed-out page-faults.
2496  }
2497 
2498 #ifdef DEBUG
2499  // Dump the #VE statistics every one minute on debug.
2500  if (0 == gGuest.TimerCalls % (IG_TIMER_FREQUENCY * 60))
2501  {
2502  IntVeDumpStats();
2503  }
2504 #endif
2505 
2506  // Check PTS integrity once every 5 seconds
2507 #ifdef USER_MODE
2508  if (0 == gGuest.TimerCalls % (IG_TIMER_FREQUENCY * 5))
2509  {
2510  status = IntHookPtsCheckIntegrity();
2511  if (!INT_SUCCESS(status))
2512  {
2513  ERROR("[ERROR] IntHookPtsCheckIntegrity failed: 0x%08x\n", status);
2514  }
2515 
2516  }
2517 #endif // USER_MODE
2518 
2519  // Handle pre-return events.
2521  if (!INT_SUCCESS(status))
2522  {
2523  ERROR("[ERROR] IntGuestPreReturnCallback failed: 0x%08x\n", status);
2524  }
2525 
2526  // Unless we made an explicit goto release_and_exit, we assume we don't want to propagate an error.
2527  status = INT_STATUS_SUCCESS;
2528 
2530 
2531  if (0 == gGuest.TimerCalls % (IG_TIMER_FREQUENCY * 3600))
2532  {
2533  // Dump performance stats every 60 minutes.
2534  IntStatsDumpAll();
2535  IntVeDumpStats();
2536 
2537  // Reset the number of NT EAT reads every 60 minutes.
2539  {
2541  }
2542  }
2543 
2544  if (gInjectVeLoader)
2545  {
2548  }
2549  if (gInjectVeUnloader)
2550  {
2553  }
2554 
2555  if (gLoadPtDriver)
2556  {
2558  gLoadPtDriver = FALSE;
2559  }
2560  if (gUnloadPtDriver)
2561  {
2564  }
2565 
2566 
2567 release_and_exit:
2568 
2569 #if defined(CFG_PAUSE_VCPUS_ON_EVENTS)
2570  IntResumeVcpus();
2571 #endif
2572 
2574 
2575  return status;
2576 }
2577 
2578 
2579 INTSTATUS
2581  _In_ void *GuestHandle,
2582  _In_ DWORD CpuNumber,
2583  _Out_ INTRO_ACTION *Action
2584  )
2602 {
2603  INTSTATUS status;
2604  BOOLEAN found;
2605  QWORD newValue;
2606  DWORD xcr;
2607  INTRO_ACTION action;
2608 
2609  if (GuestHandle == NULL)
2610  {
2612  }
2613 
2614  if (Action == NULL)
2615  {
2617  }
2618 
2619  found = FALSE;
2620  *Action = introGuestAllowed;
2621  action = introGuestAllowed;
2622 
2624 
2625  gEventId++;
2626 
2627 #ifdef CFG_PAUSE_VCPUS_ON_EVENTS
2628  IntPauseVcpus();
2629 #endif
2630 
2632  {
2633  // We need to exit with success, since most likely the introspection was disabled in the meantime.
2635  goto _exit_release;
2636  }
2637 
2638  if (__unlikely(CpuNumber >= gGuest.CpuCount))
2639  {
2640  ERROR("[ERROR] A XCR exit came for cpu %d, but we have only %d\n", CpuNumber, gGuest.CpuCount);
2642  goto _exit_release;
2643  }
2644 
2645  gVcpu = &gGuest.VcpuArray[CpuNumber];
2647 
2648  status = IntGetGprs(CpuNumber, &gVcpu->Regs);
2649  if (!INT_SUCCESS(status))
2650  {
2651  ERROR("[ERROR] IntGetGprs failed: 0x%08x\n", status);
2652  goto _exit_release;
2653  }
2654 
2655  xcr = (DWORD)gVcpu->Regs.Rcx;
2656 
2657  newValue = ((QWORD)(gVcpu->Regs.Rdx & 0xFFFFFFFF) << 32) |
2658  (gVcpu->Regs.Rax & 0xFFFFFFFF);
2659 
2661 
2662  gVcpu->Xcr0 = newValue;
2663 
2665  {
2666  if (xcr == pHook->Xcr)
2667  {
2668  found = TRUE;
2669 
2670  if (pHook->Disabled)
2671  {
2672  continue;
2673  }
2674 
2675  status = pHook->Callback(pHook->Context, xcr, &action);
2676 
2677  if (INT_STATUS_REMOVE_HOOK_ON_RET == status)
2678  {
2679  status = IntHookXcrRemoveHook(pHook);
2680  if (!INT_SUCCESS(status))
2681  {
2682  ERROR("[ERROR] IntHookXcrRemoveHook failed: 0x%08x\n", status);
2683  }
2684  }
2685 
2686  if (action > *Action)
2687  {
2688  *Action = action;
2689  }
2690  }
2691  }
2692 
2694 
2695  // Handle pre-return events.
2697  if (!INT_SUCCESS(status))
2698  {
2699  ERROR("[ERROR] IntGuestPreReturnCallback failed: 0x%08x\n", status);
2700  }
2701 
2702  if (found)
2703  {
2704  status = INT_STATUS_SUCCESS;
2705  }
2706  else
2707  {
2708  status = INT_STATUS_NOT_FOUND;
2709  }
2710 
2712 
2713 _exit_release:
2715  {
2716  ERROR("[ERROR] XCR callback set DisableOnReturn... We will try to disable introcore...\n");
2717 
2719 
2720  status = INT_STATUS_FATAL_ERROR;
2721  }
2722 
2723 #ifdef CFG_PAUSE_VCPUS_ON_EVENTS
2724  IntResumeVcpus();
2725 #endif
2726 
2728 
2729  return status;
2730 }
2731 
2732 
2733 INTSTATUS
2735  _In_ void *GuestHandle,
2736  _In_ QWORD GuestPhysicalAddress,
2737  _In_ DWORD CpuNumber
2738  )
2768 {
2769  INTSTATUS status;
2770  PIG_ARCH_REGS regs;
2771  BOOLEAN found, emulated, noemu;
2772 
2773  UNREFERENCED_PARAMETER(GuestPhysicalAddress);
2774 
2775  if (GuestHandle == NULL)
2776  {
2778  }
2779 
2780  found = emulated = noemu = FALSE;
2781 
2783 
2784  gEventId++;
2785 
2786 #ifdef CFG_PAUSE_VCPUS_ON_EVENTS
2787  IntPauseVcpus();
2788 #endif
2789 
2791  {
2792  // We need to exit with success, since most likely the introspection was disabled in the meantime.
2794  goto _exit_release;
2795  }
2796 
2798  {
2799  WARNING("[WARNING] A BP exit came for cpu %d while the guest was not initialized. Will ignore.\n", CpuNumber);
2800  // Here we have to return an error to signal the fact that this #BP was not set by introcore
2801  status = INT_STATUS_NOT_INITIALIZED;
2802  goto _exit_release;
2803  }
2804 
2805  if (__unlikely(CpuNumber >= gGuest.CpuCount))
2806  {
2807  ERROR("[ERROR] A BP exit came for cpu %d, but we have only %d\n", CpuNumber, gGuest.CpuCount);
2809  goto _exit_release;
2810  }
2811 
2812  gVcpu = &gGuest.VcpuArray[CpuNumber];
2814 
2815  status = IntGetGprs(CpuNumber, &gVcpu->Regs);
2816  if (!INT_SUCCESS(status))
2817  {
2818  ERROR("[ERROR] IntGetGprs failed: 0x%08x\n", status);
2819  goto _exit_release;
2820  }
2821 
2822  regs = &gVcpu->Regs;
2823 
2825 
2826  // Handle guest detours.
2827  if (!found)
2828  {
2829  status = IntDetCallCallback();
2830  if (INT_SUCCESS(status))
2831  {
2832  found = TRUE;
2833 
2834  if (INT_STATUS_NO_DETOUR_EMU == status)
2835  {
2836  noemu = TRUE;
2837  }
2838  }
2839  else
2840  {
2841  if (INT_STATUS_NOT_FOUND != status)
2842  {
2843  ERROR("[ERROR] IntDetCallCallback failed: 0x%08x\n", status);
2844  }
2845  }
2846  }
2847 
2848  // If no INT3 handler found, call the generic agents handler.
2849  if (!found)
2850  {
2851  status = IntAgentHandleInt3(regs->Rip, CpuNumber);
2852  if (INT_SUCCESS(status))
2853  {
2854  // An agent handled this, we're good.
2855  found = TRUE;
2856 
2857 
2858  if (INT_STATUS_NO_DETOUR_EMU == status)
2859  {
2860  noemu = TRUE;
2861  }
2862  }
2863  else
2864  {
2865  if (INT_STATUS_NOT_FOUND != status)
2866  {
2867  ERROR("[ERROR] IntAgentHandleInt3 failed: 0x%08x\n", status);
2868  }
2869  }
2870  }
2871 
2872  // If no detour or agent handler found, check the PT write candidates.
2873  if (!found)
2874  {
2875  status = IntPtiHandleInt3();
2876  if (INT_SUCCESS(status))
2877  {
2878  // An agent handled this, we're good.
2879  found = TRUE;
2880 
2881 
2882  if (INT_STATUS_NO_DETOUR_EMU == status)
2883  {
2884  noemu = TRUE;
2885  }
2886  }
2887  else
2888  {
2889  if (INT_STATUS_NOT_FOUND != status)
2890  {
2891  ERROR("[ERROR] IntAgentHandleInt3 failed: 0x%08x\n", status);
2892  }
2893  }
2894  }
2895 
2896  if (!found)
2897  {
2898  // So we don't have agents, detours or pt filter; however, this may happen after we remove candidate
2899  // PT instructions, since an INT3 may remain pending while we remove all the breakpoints from memory.
2900  // We can easily handle this may reading the byte at RIP and checking if it is indeed an INT3. If it is,
2901  // we will reinject it, otherwise, we'll ignore it.
2902  // Note that we have to really decode the instruction, because someone may use encodings such CD03 or 48CC.
2903  INSTRUX instrux;
2904 
2905  status = IntDecDecodeInstructionAtRip(CpuNumber, regs, NULL, &instrux);
2906  if (INT_SUCCESS(status))
2907  {
2908  if (instrux.Instruction == ND_INS_INT3 || (instrux.Instruction == ND_INS_INT && instrux.Immediate1 == 3))
2909  {
2910  // This is a legit int3.
2911  TRACE("[INFO] We have a breakpoint exit with instruction %s at RIP %llx, will reinject\n",
2912  instrux.Mnemonic, regs->Rip);
2913  }
2914  else
2915  {
2916  // Not really INT3 there in memory, we can ignore it.
2917  TRACE("[INFO] We have a breakpoint exit with instruction %s at RIP %llx, will ignore\n",
2918  instrux.Mnemonic, regs->Rip);
2919  found = noemu = TRUE;
2920  }
2921  }
2922  }
2923 
2924  // Skip the INT3 instruction if we haven't already emulated it and we do require emulation.
2925  if (found && !emulated && !noemu)
2926  {
2927  regs->Rip++;
2928 
2929  status = IntSetGprs(CpuNumber, regs);
2930  if (!INT_SUCCESS(status))
2931  {
2932  ERROR("[ERROR] IntSetGprs failed: 0x%08x\n", status);
2933  }
2934  }
2935 
2937 
2938  // Handle pre-return events.
2940  if (!INT_SUCCESS(status))
2941  {
2942  ERROR("[ERROR] IntGuestPreReturnCallback failed: 0x%08x\n", status);
2943  }
2944 
2945  if (found)
2946  {
2947  status = INT_STATUS_SUCCESS;
2948  }
2949  else
2950  {
2951  status = INT_STATUS_NOT_FOUND;
2952  }
2953 
2955 
2956 _exit_release:
2958  {
2959  ERROR("[ERROR] BP callback set DisableOnReturn... We will try to disable introcore...\n");
2960 
2962 
2963  status = INT_STATUS_FATAL_ERROR;
2964  }
2965 
2966  if (INT_SUCCESS(status) && gGuest.BugCheckInProgress)
2967  {
2968  ERROR("[ERROR] BP callback set BugCheckInProgress... We will try to disable introcore...\n");
2969 
2971 
2972  status = INT_STATUS_UNINIT_BUGCHECK;
2973  }
2974 
2975 #ifdef CFG_PAUSE_VCPUS_ON_EVENTS
2976  IntResumeVcpus();
2977 #endif
2978 
2980 
2981  return status;
2982 }
2983 
2984 
2985 static INTSTATUS
2987  _In_ void *GuestHandle,
2988  _In_ DWORD Vector,
2989  _In_ QWORD ErrorCode,
2990  _In_ QWORD Cr2,
2991  _In_ DWORD CpuNumber
2992  )
3012 {
3013  INTSTATUS status;
3014 
3015  UNREFERENCED_PARAMETER(ErrorCode);
3016 
3017  if (GuestHandle == NULL)
3018  {
3020  }
3021 
3023 
3024  gEventId++;
3025 
3026 #ifdef CFG_PAUSE_VCPUS_ON_EVENTS
3027  IntPauseVcpus();
3028 #endif
3029 
3031  {
3032  // We need to exit with success, since most likely the introspection was disabled in the meantime.
3034  goto _exit_release;
3035  }
3036 
3037  if (__unlikely(CpuNumber >= gGuest.CpuCount))
3038  {
3039  ERROR("[ERROR] A VMCALL exit came for cpu %d, but we have only %d\n", CpuNumber, gGuest.CpuCount);
3041  goto _exit_release;
3042  }
3043 
3044  gVcpu = &gGuest.VcpuArray[CpuNumber];
3046 
3047  status = IntGetGprs(CpuNumber, &gVcpu->Regs);
3048  if (!INT_SUCCESS(status))
3049  {
3050  ERROR("[ERROR] IntGetGprs failed: 0x%08x\n", status);
3051  goto _exit_release;
3052  }
3053 
3054  TRACE("[INFO] Injected vector 0x%02x, CR2 0x%016llx, ErrorCode %llx, CPU %d\n", Vector, Cr2, ErrorCode, CpuNumber);
3055 
3057 
3058  // We don't expect this callback to be called with an invalid exception state.
3059  if (!gVcpu->Exception.Valid)
3060  {
3061  WARNING("[WARNING] IntHandleEventInjection was called, but no injection was done!\n");
3062  }
3063 
3064  if (((gVcpu->Exception.Vector != Vector) && (gVcpu->Exception.Vector == VECTOR_PF)) ||
3065  (gVcpu->Exception.Cr2 & PAGE_MASK) != (Cr2 & PAGE_MASK))
3066  {
3067  // Something was injected, but it either wasn't a #PF, or the CR2 did not match.
3069  }
3070 
3071  if (gVcpu->Exception.Vector == Vector && Vector == VECTOR_UD)
3072  {
3073  if (NULL == gVcpu->CurrentUD)
3074  {
3075  ERROR("[ERROR] UD INFO is NULL\n");
3076  }
3077  else
3078  {
3080  }
3081  }
3082 
3083 
3084  // Something got injected, reset this exception.
3086 
3087  // We always set this to NULL, as it is set with the proper value on every #UD request
3088  gVcpu->CurrentUD = NULL;
3089 
3091 
3092  // Handle pre-return events.
3094  if (!INT_SUCCESS(status))
3095  {
3096  ERROR("[ERROR] IntGuestPreReturnCallback failed: 0x%08x\n", status);
3097  }
3098 
3099  status = INT_STATUS_SUCCESS;
3100 
3102 
3103 _exit_release:
3104 
3105 #ifdef CFG_PAUSE_VCPUS_ON_EVENTS
3106  IntResumeVcpus();
3107 #endif
3108 
3110 
3111  return status;
3112 }
3113 
3114 
3115 INTSTATUS
3117  _In_ void *GuestHandle,
3118  _In_ DWORD Flags,
3119  _In_ DWORD CpuNumber,
3120  _Out_ INTRO_ACTION *Action
3121  )
3148 {
3149  INTSTATUS status;
3150  PIG_ARCH_REGS regs;
3151  PINSTRUX instruction;
3152  QWORD gla, gpa, gla2, base;
3153  DTR newDtr = {0}, oldDtr = {0};
3154  BOOLEAN cacheuse, cbkfound, pagefound;
3155  WORD limit;
3156 
3157  if (NULL == GuestHandle)
3158  {
3160  }
3161 
3162  if (NULL == Action)
3163  {
3165  }
3166 
3167  *Action = introGuestAllowed;
3168  gla = base = 0;
3169  limit = 0;
3170  cbkfound = pagefound = FALSE;
3171 
3173 
3174  gEventId++;
3175 
3176 #ifdef CFG_PAUSE_VCPUS_ON_EVENTS
3177  IntPauseVcpus(GuestHandle);
3178 #endif
3179 
3181  {
3183  goto _exit_release;
3184  }
3185 
3186  if (__unlikely(CpuNumber >= gGuest.CpuCount))
3187  {
3188  ERROR("[ERROR] A dtr violation came for cpu %d, but we have only %d\n", CpuNumber, gGuest.CpuCount);
3190  goto _exit_release;
3191  }
3192 
3193  gVcpu = &gGuest.VcpuArray[CpuNumber];
3194  regs = &gVcpu->Regs;
3195  instruction = &gVcpu->Instruction;
3197 
3198  status = IntGetGprs(CpuNumber, regs);
3199  if (!INT_SUCCESS(status))
3200  {
3201  ERROR("[ERROR] IntGetGprs failed: 0x%08x\n", status);
3202  goto done_handling_dtr_violation;
3203  }
3204 
3205  cacheuse = (gGuest.Mm.SystemCr3 != 0);
3207  CpuNumber,
3208  regs,
3209  instruction,
3210  cacheuse ? 0 : DEC_OPT_NO_CACHE,
3211  NULL,
3212  NULL);
3213  if ((INT_STATUS_PAGE_NOT_PRESENT == status) || (INT_STATUS_NO_MAPPING_STRUCTURES == status))
3214  {
3215  TRACE("[INFO] The page containing the RIP has been swapped out; will retry the instruction.\n");
3216  *Action = introGuestRetry;
3217  status = INT_STATUS_SUCCESS;
3218  goto done_handling_dtr_violation;
3219  }
3220  else if (!INT_SUCCESS(status))
3221  {
3222  ERROR("[ERROR] IntDecDecodeInstructionAtRipWithCache failed: 0x%08x\n", status);
3223  goto done_handling_dtr_violation;
3224  }
3225 
3226  // Exits triggered by other instructions are fishy - only LIDT, LGDT, SIDT, SGDT can trigger such exits.
3227  // If, however, we get other instructions, we will retry them, as they were probably been mangled inside the guest.
3228  if (instruction->Instruction != ND_INS_LIDT && instruction->Instruction != ND_INS_SIDT &&
3229  instruction->Instruction != ND_INS_LGDT && instruction->Instruction != ND_INS_SGDT &&
3230  instruction->Instruction != ND_INS_LLDT && instruction->Instruction != ND_INS_SLDT &&
3231  instruction->Instruction != ND_INS_LTR && instruction->Instruction != ND_INS_STR)
3232  {
3233  ERROR("[ERROR] We have a DTR exit, but the instruction is not appropriate: %s\n", instruction->Mnemonic);
3234  *Action = introGuestRetry;
3235  goto done_handling_dtr_violation;
3236  }
3237 
3238  // Not operating on memory, we can bail out. Note that LIDT, LGDT, SIDT, SGDT (the instructions that interest us)
3239  // can only operate on memory, so this condition basically covers register access for LLDT, LTR, SLDT, STR.
3240  if (instruction->Operands[0].Type != ND_OP_MEM)
3241  {
3242  goto done_handling_dtr_violation;
3243  }
3244 
3245  // Get the new value from the instruction by getting the address which stores the value and then reading it. We also
3246  // need it in order to handle the memory access caused by this instruction.
3247  status = IntDecComputeLinearAddress(instruction, &instruction->Operands[0], regs, &gla);
3248  if (!INT_SUCCESS(status))
3249  {
3250  ERROR("[ERROR] IntDecComputeLinearAddress failed: 0x%08x\n", status);
3251  goto done_handling_dtr_violation;
3252  }
3253 
3254  if (IntHandleCowOnPage(gla & PAGE_MASK, CpuNumber, instruction->Operands[0].Access.Access))
3255  {
3256  *Action = introGuestRetry;
3257 
3258  // we can skip processing the rest of the violation, since we will retry it & we injected a #PF.
3259  goto done_handling_dtr_violation;
3260  }
3261 
3262  if (IntHandlePageBoundaryCow(gla, instruction->Operands[0].Size, instruction->Operands[0].Access.Access, CpuNumber))
3263  {
3264  *Action = introGuestRetry;
3265 
3266  // we can skip processing the rest of the violation, since we will retry it & we injected a #PF.
3267  goto done_handling_dtr_violation;
3268  }
3269 
3270  status = IntTranslateVirtualAddress(gla, regs->Cr3, &gpa);
3271  if (!INT_SUCCESS(status))
3272  {
3273  ERROR("[ERROR] IntTranslateVirtualAddress failed: 0x%08x\n", status);
3274  goto done_handling_dtr_violation;
3275  }
3276 
3277  status = IntHandleMemAccess(gla, gpa, instruction->Operands[0].Size, Action, &cbkfound, &pagefound, FALSE,
3278  (instruction->Operands[0].Access.Write ? IG_EPT_HOOK_WRITE : IG_EPT_HOOK_READ));
3279  if (!INT_SUCCESS(status))
3280  {
3281  ERROR("[ERROR] IntHandleMemAccess failed: 0x%08x\n", status);
3282  goto done_handling_dtr_violation;
3283  }
3284 
3285  if (((gla + instruction->Operands[0].Size) & PAGE_MASK) != (gla & PAGE_MASK))
3286  {
3287  // Page boundary access.
3288  gla2 = (gla + instruction->Operands[0].Size) & PAGE_MASK;
3289 
3290  status = IntTranslateVirtualAddress(gla2, regs->Cr3, &gpa);
3291  if (!INT_SUCCESS(status))
3292  {
3293  ERROR("[ERROR] IntTranslateVirtualAddress failed: 0x%08x\n", status);
3294  goto done_handling_dtr_violation;
3295  }
3296 
3297  status = IntHandleMemAccess(gla2, gpa, instruction->Operands[0].Size, Action, &cbkfound, &pagefound, FALSE,
3298  (instruction->Operands[0].Access.Write ? IG_EPT_HOOK_WRITE : IG_EPT_HOOK_READ));
3299  if (!INT_SUCCESS(status))
3300  {
3301  ERROR("[ERROR] IntHandleMemAccess failed: 0x%08x\n", status);
3302  goto done_handling_dtr_violation;
3303  }
3304  }
3305 
3306  // If the memory handler returns anything other than allowed, we will obey that status;
3307  if (*Action != introGuestAllowed)
3308  {
3309  LOG("[INFO] The memory handling callback returned action %d for instruction %s!\n",
3310  *Action,
3311  instruction->Mnemonic);
3312 
3313  goto done_handling_dtr_violation;
3314  }
3315 
3316  // We can bail out on SIDT, SGDT, STR, SLDT, LTR, LLDT - they do not interest us.
3317  if (ND_INS_LIDT != instruction->Instruction && ND_INS_LGDT != instruction->Instruction)
3318  {
3319  //TRACE("[INFO] SIDT/SGDT (instruction code: %04d) came. Will allow.\n", instruction->Instruction);
3320  goto done_handling_dtr_violation;
3321  }
3322 
3323  // Get the original value from DTR (also, here we do the specific DTR stuff)
3324  if (ND_INS_LIDT == instruction->Instruction)
3325  {
3326  status = IntIdtFindBase(CpuNumber, &base, &limit);
3327  if (!INT_SUCCESS(status))
3328  {
3329  ERROR("[ERROR] IntIdtFindBase failed: 0x%08x\n", status);
3330  goto done_handling_dtr_violation;
3331  }
3332  }
3333  else if (ND_INS_LGDT == instruction->Instruction)
3334  {
3335  status = IntGdtFindBase(CpuNumber, &base, &limit);
3336  if (!INT_SUCCESS(status))
3337  {
3338  ERROR("[ERROR] IntGdtFindBase failed: 0x%08x\n", status);
3339  goto done_handling_dtr_violation;
3340  }
3341  }
3342  else
3343  {
3344  WARNING("[WARNING] Unknown instruction on DTR violation callback. Instruction code: %04d. Rip: 0x%016llx\n",
3345  instruction->Instruction, regs->Rip);
3346  goto done_handling_dtr_violation;
3347  }
3348 
3349  oldDtr.Base = base;
3350  oldDtr.Limit = limit;
3351 
3352  status = IntVirtMemRead(gla, gGuest.WordSize + sizeof(WORD), regs->Cr3, &newDtr, NULL);
3353  if (!INT_SUCCESS(status))
3354  {
3355  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x\n", status);
3356  goto done_handling_dtr_violation;
3357  }
3358 
3359  // If the old value is the same as the new one, then we will allow it.
3360  // 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.
3361  if ((0 == oldDtr.Base && 0 != newDtr.Base) ||
3362  (oldDtr.Base == newDtr.Base && oldDtr.Limit == newDtr.Limit))
3363  {
3364  goto done_handling_dtr_violation;
3365  }
3366 
3368 
3370  {
3371  if (pHook->Disabled)
3372  {
3373  continue;
3374  }
3375 
3376  // We compare the flags for which the hook was set vs. the flags reported to us. They have to be identical
3378  if (pHook->Flags == Flags)
3379  {
3380  status = pHook->Callback(&oldDtr, &newDtr, Flags, Action);
3381 
3382  if (INT_STATUS_REMOVE_HOOK_ON_RET == status)
3383  {
3384  status = IntHookDtrRemoveHook(pHook);
3385  if (!INT_SUCCESS(status))
3386  {
3387  ERROR("[ERROR] IntHookIdtrRemoveHook failed: 0x%08x\n", status);
3388  }
3389  }
3390  }
3391  }
3392 
3394 
3395 done_handling_dtr_violation:
3397 
3398  // Handle pre-return events.
3400  if (!INT_SUCCESS(status))
3401  {
3402  ERROR("[ERROR] IntGuestPreReturnCallback failed: 0x%08x\n", status);
3403  }
3404 
3405 _exit_release:
3407  {
3408  ERROR("[ERROR] DTR callback set DisableOnReturn... We will try to disable introcore...\n");
3409 
3411 
3412  status = INT_STATUS_FATAL_ERROR;
3413  }
3414 
3415 #ifdef CFG_PAUSE_VCPUS_ON_EVENTS
3416  IntResumeVcpus();
3417 #endif
3418 
3420 
3421  return status;
3422 }
3423 
3424 
3425 INTSTATUS
3427  _In_ void *GuestHandle,
3428  _In_ PENG_NOTIFICATION_HEADER EngineNotification
3429  )
3450 {
3451  INTSTATUS status = INT_STATUS_SUCCESS;
3452 
3453  if (NULL == GuestHandle)
3454  {
3456  }
3457 
3458  if (NULL == EngineNotification)
3459  {
3461  }
3462 
3464 
3465  gEventId++;
3466 
3467 #ifdef CFG_PAUSE_VCPUS_ON_EVENTS
3468  IntPauseVcpus(GuestHandle);
3469 #endif
3470 
3472  {
3474  goto done_handling_engine_result;
3475  }
3476 
3477 
3478  if (introEngineNotificationCodeExecution == EngineNotification->Type)
3479  {
3480  PENG_NOTIFICATION_CODE_EXEC codeExecEngineNotification = (PENG_NOTIFICATION_CODE_EXEC)EngineNotification;
3481 
3482  status = IntHandleExecCallback(codeExecEngineNotification);
3483  if (!INT_SUCCESS(status))
3484  {
3485  ERROR("[ERROR] IntHandleExecCallback failed: 0x%08x\n", status);
3486  }
3487  }
3488  else if (introEngineNotificationCmdLine == EngineNotification->Type)
3489  {
3490  PENG_NOTIFICATION_CMD_LINE cmdLineEngineNotification = (PENG_NOTIFICATION_CMD_LINE)EngineNotification;
3491 
3493  {
3494  status = IntWinHandleCmdLineCallback(cmdLineEngineNotification);
3495  if (!INT_SUCCESS(status))
3496  {
3497  ERROR("[ERROR] IntWinHandleCmdLineCallback failed: 0x%08x\n", status);
3498  }
3499  }
3500  else if (gGuest.OSType == introGuestLinux)
3501  {
3502  status = IntLixHandleCmdLineCallback(cmdLineEngineNotification);
3503  if (!INT_SUCCESS(status))
3504  {
3505  ERROR("[ERROR] IntLixCmdLineHandleCallback failed: 0x%08x\n", status);
3506  }
3507  }
3508  }
3509  else
3510  {
3511  ERROR("[ERROR] Unknown engine notification type, value:%x\n", EngineNotification->Type);
3512  }
3513 
3514 done_handling_engine_result:
3515 
3516 #ifdef CFG_PAUSE_VCPUS_ON_EVENTS
3517  IntResumeVcpus();
3518 #endif
3519 
3521 
3522  return status;
3523 }
3524 
3525 
3526 INTSTATUS
3528  void
3529  )
3540 {
3541  INTSTATUS status;
3542 
3544  if (!INT_SUCCESS(status))
3545  {
3546  ERROR("[ERROR] IntRegisterVmxTimerHandler failed: 0x%08x\n", status);
3547  return status;
3548  }
3549 
3551  if (!INT_SUCCESS(status))
3552  {
3553  ERROR("[ERROR] IntRegisterIntroCallHandler failed: 0x%08x\n", status);
3554  return status;
3555  }
3556 
3558  if (!INT_SUCCESS(status))
3559  {
3560  ERROR("[ERROR] IntRegisterEventInjectionHandler failed: 0x%08x\n", status);
3561  return status;
3562  }
3563 
3565  if (!INT_SUCCESS(status))
3566  {
3567  ERROR("[ERROR] IntRegisterEnginesResultCallback failed: 0x%08x\n", status);
3568  return status;
3569  }
3570 
3571  return INT_STATUS_SUCCESS;
3572 }
3573 
3574 
3575 INTSTATUS
3577  void
3578  )
3584 {
3586 
3588 
3589  // NOTE: This is activated differently for Linux & Windows, but no harm if it's unregistered here.
3591 
3593 
3595 
3596  return INT_STATUS_SUCCESS;
3597 }
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:64
#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:475
HOOK_HEADER Header
Hook header.
Definition: hook_gpa.h:43
DWORD ReadCount
Number of read EPT hooks.
Definition: hook_gpa.h:77
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:437
#define GPA_HOOK_ID(addr)
Definition: hook_gpa.h:88
INTSTATUS IntPtiRemoveInstruction(QWORD Rip)
Remove the hook on a monitored instruction.
Definition: ptfilter.c:792
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
BOOLEAN SingleStep
True if th VCPU is currently single-stepping the current instruction.
Definition: guests.h:200
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:1203
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:211
#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:1536
#define STATS_EXIT(id)
Definition: stats.h:160
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:3527
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:389
BOOLEAN gInjectVeLoader
Definition: callbacks.c:30
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:1916
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:3116
QWORD AllowOnExecRip
The RIP which was allowed to execute on an exec violation.
Definition: guests.h:202
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:289
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:313
#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:825
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.
DWORD WriteCount
Number of write EPT hooks.
Definition: hook_gpa.h:78
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:700
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
INTSTATUS IntLixHandleCmdLineCallback(ENG_NOTIFICATION_CMD_LINE *EngineNotification)
Send a command line violation event.
Definition: lixcmdline.c:140
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:3576
#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:1843
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
BOOLEAN AllowOnExec
True if we returned introGuestAllowed on an execution alert.
Definition: guests.h:201
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:2986
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:372
Measures the EPT violations for which the instruction does a read and a write.
Definition: stats.h:27
INTSTATUS IntFlushEPTPermissions(void)
Definition: glue.c:1242
INTSTATUS IntHandleXcrWrite(void *GuestHandle, DWORD CpuNumber, INTRO_ACTION *Action)
Handle extended control registers writes.
Definition: callbacks.c:2580
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:37
INTRO_GUEST_TYPE OSType
The type of the guest.
Definition: guests.h:278
INSTRUX Instruction
The current instruction, pointed by the guest RIP.
Definition: guests.h:88
Commit all the memory hooks.
Definition: guests.h:436
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:614
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:3426
INTSTATUS IntWinProcValidateSystemCr3(void)
This function checks if the system CR3 value was modified and if GUEST_STATE::KernelBetaDetections is...
Definition: winprocess.c:3371
#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:387
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:438
#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:98
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:2080
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
PHOOK_EPT_ENTRY IntHookGpaGetExistingEptEntry(QWORD GpaPage)
Get the EPT entry associated with the provided guest physical page.
Definition: hook_gpa.c:161
TIMER_FRIENDLY void IntDumpInstruction(INSTRUX *Instruction, QWORD Rip)
This function dumps a given instruction (textual disassembly).
Definition: dumper.c:583
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:1156
QWORD AllowOnExecGpa
The GPA which was allowed to execute on an exec violation.
Definition: guests.h:203
#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:1280
INTSTATUS IntWinSelfMapValidateSelfMapEntries(void)
Validates the self map entries for every process in the system.
Definition: winselfmap.c:453
#define STATS_ENTER(id)
Definition: stats.h:153
uint8_t * PBYTE
Definition: intro_types.h:47
BOOLEAN PaeEnabled
True if Physical Address Extension is enabled.
Definition: guests.h:295
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:388
static BOOLEAN gForceActionOnBeta
Definition: callbacks.c:33
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:442
BOOLEAN Guest64
True if this is a 64-bit guest, False if it is a 32-bit guest.
Definition: guests.h:290
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:236
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:99
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:311
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:403
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:792
BOOLEAN gInjectVeUnloader
Definition: callbacks.c:30
#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:2071
BOOLEAN GuestInitialized
True if the OS-specific portion has been initialized.
Definition: guests.h:293
QWORD Gpa
The accessed guest physical address. Valid only for EPT exits.
Definition: guests.h:101
#define TRACE(fmt,...)
Definition: glue.h:58
static BOOLEAN IntValidatePageRightsEx(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:114
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:195
INTSTATUS IntHandleCrWrite(void *GuestHandle, DWORD Cr, DWORD CpuNumber, QWORD OldValue, QWORD NewValue, INTRO_ACTION *Action)
Handle a control register violation.
Definition: callbacks.c:1692
#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:367
DWORD RepOptsDisableCount
The number of times the rep optimizations have been disabled.
Definition: guests.h:383
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:29
#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:1596
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
INTSTATUS IntWinSDCheckIntegrity(void)
This function checks the integrity of the security descriptor for all the processes inside gWinProces...
Definition: winsecdesc.c:1656
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:390
A descriptor table register. Valid for IDTR and GDTR.
Definition: introcpu.h:71
DWORD CpuCount
The number of logical CPUs.
Definition: guests.h:279
#define PAGE_SIZE
Definition: common.h:70
#define UNREFERENCED_PARAMETER(P)
Definition: introdefs.h:29
void IntStatsDumpAll(void)
Prints all the non-zero stats.
Definition: stats.c:220
Exposes the functions used to schedule an asynchronous command line scan and receives its result...
void * InstructionCache
The currently used instructions cache.
Definition: guests.h:404
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:401
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:228
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:2734
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:469
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:374
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:50
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:273
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:274
struct _VCPU_STATE::@80 Exception
The exception to be injected in guest.
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:2359
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:439
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
DWORD ExecuteCount
Number of execute EPT hooks.
Definition: hook_gpa.h:79
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:385
#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:59
BOOLEAN gLoadPtDriver
Definition: callbacks.c:31
#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:1097
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:1426
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:333
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:328
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:461
TIMER_FRIENDLY INTSTATUS IntWinSudCheckIntegrity(void)
This function checks the integrity of protected fields from SharedUserData, described in gProtFields...
Definition: winsud.c:980
#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:31
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:271
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:2140
QWORD R15
Definition: vecommon.h:84
Notification header for scan engines alerts.
Definition: intro_types.h:2052
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:441
void IntHookPtsDump(void)
Prints all the page table hooks.
Definition: hook_pts.c:2452
Commit all the DTR hooks.
Definition: guests.h:440
#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:83
#define INT_STATUS_INVALID_PARAMETER_3
Definition: introstatus.h:68
Handling EPT violation.
Definition: guests.h:22