Bitdefender Hypervisor Memory Introspection
winguest.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 Bitdefender
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 #include "winguest.h"
6 #include "callbacks.h"
7 #include "cr_protection.h"
8 #include "decoder.h"
9 #include "drivers.h"
10 #include "dtr_protection.h"
11 #include "guests.h"
12 #include "introcpu.h"
13 #include "msr_protection.h"
14 #include "swapgs.h"
15 #include "swapmem.h"
16 #include "vecore.h"
17 #include "winagent.h"
18 #include "winapi.h"
19 #include "winguest_supported.h"
20 #include "winhal.h"
21 #include "winidt.h"
22 #include "winpe.h"
23 #include "winpfn.h"
24 #include "winprocesshp.h"
25 #include "hook.h"
26 #include "exceptions.h"
27 #include "alerts.h"
28 #include "winthread.h"
29 
36 
41 #define KERNEL_SEARCH_LIMIT (16 * ONE_MEGABYTE)
42 
43 
44 //
45 // These are mostly constant on supported os versions
46 // If any windows update would fail the init because we can't find the idle process'
47 // cr3, simply change those and it SHOULD work.
48 //
49 
53 #define IDLE_THREAD_OFFSET_PCR ((gGuest.Guest64) ? 0x198 : 0x12C)
54 
56 #define PROCESS_SEARCH_LIMIT_THREAD_UPPER (DWORD)((gGuest.Guest64) ? 0x220 : 0x150)
57 
59 #define PROCESS_SEARCH_LIMIT_THREAD_LOWER (DWORD)((gGuest.Guest64) ? 0x210 : 0x150)
60 
67 #define CR3_OFFSET_IN_KPROCESS (DWORD)((gGuest.Guest64) ? 0x28 : 0x18)
68 
70 #define WIN_SHARED_USER_DATA_PTR (gGuest.Guest64 ? 0xFFFFF78000000000 : 0xFFDF0000)
71 
73 static HOOK_GVA *gSudExecHook = NULL;
74 
75 
76 static INTSTATUS
78  void
79  )
93 {
94  INTSTATUS status;
95  DWORD sectionCount;
96 
97  // hopefully, we won't have a total of more than 10 sections with names .data or ALMOSTRO
98  const DWORD maxSecCount = 10;
99  const DWORD objCount = 3;
100 
101  DWORD objectsFound = 0;
102 
103  QWORD kernelBase = gGuest.KernelVa;
104  BYTE *pKernel = gWinGuest->KernelBuffer;
105 
106  IMAGE_SECTION_HEADER *pSections = HpAllocWithTag(sizeof(*pSections) * maxSecCount, IC_TAG_IMGE);
107  if (NULL == pSections)
108  {
110  }
111 
112  status = IntPeGetSectionHeadersByName(kernelBase, pKernel, "ALMOSTRO", maxSecCount - 1, gGuest.Mm.SystemCr3,
113  pSections, &sectionCount);
114  if (!INT_SUCCESS(status) || (0 == sectionCount))
115  {
116  ERROR("[ERROR] IntPeGetSectionHeadersByName failed for `ALMOSTRO`: 0x%08x\n", status);
117  goto cleanup_and_exit;
118  }
119 
120  status = IntPeGetSectionHeadersByName(kernelBase, pKernel, ".data", 1, gGuest.Mm.SystemCr3,
121  &pSections[sectionCount], NULL);
122  if (!INT_SUCCESS(status))
123  {
124  ERROR("[ERROR] IntPeGetSectionHeadersByName failed for `.data`: 0x%08x\n", status);
125  goto cleanup_and_exit;
126  }
127 
128  sectionCount++;
129 
130  // Iterate the kernel sections in order to get the internal variables.
131  for (DWORD i = 0; i < sectionCount; i++)
132  {
133  PIMAGE_SECTION_HEADER pSec = &pSections[i];
134  DWORD pageCount = ROUND_UP(pSec->Misc.VirtualSize, PAGE_SIZE) / PAGE_SIZE;
135 
136  for (DWORD j = 0; j < pageCount; j++)
137  {
138  DWORD offset = pSec->VirtualAddress + j * PAGE_SIZE;
139  DWORD sizeToParse;
140  union
141  {
142  void *ptrValue;
143  DWORD *pPage32;
144  QWORD *pPage64;
145  } pPage;
146 
147  QWORD target = kernelBase + offset;
148 
149  pPage.ptrValue = NULL;
150 
151  if (offset > gWinGuest->KernelBufferSize)
152  {
153  status = IntVirtMemMap(target, PAGE_SIZE, gGuest.Mm.SystemCr3, 0, &pPage.ptrValue);
154  if (!INT_SUCCESS(status))
155  {
156  ERROR("[ERROR] Failed mapping page %d of section %d: 0x%08x\n", j, i, status);
157  continue;
158  }
159  }
160  else
161  {
162  pPage.ptrValue = pKernel + offset;
163  }
164 
165  // If this is the last page, parse only what's left
166  if (j == pageCount - 1)
167  {
168  sizeToParse = pSec->Misc.VirtualSize & PAGE_OFFSET;
169  }
170  else
171  {
172  sizeToParse = PAGE_SIZE;
173  }
174 
175  // Analyze this page - we will parse DWORD entities, since we're running on x86 here.
176  for (DWORD parsed = 0; parsed < sizeToParse / gGuest.WordSize;)
177  {
178  QWORD p = gGuest.Guest64 ? pPage.pPage64[parsed] : pPage.pPage32[parsed];
179  void *hostPtr = gGuest.Guest64 ? (void *)&pPage.pPage64[parsed] : (void *)&pPage.pPage32[parsed];
180 
181  if (!gGuest.Guest64 &&
183  (p == 0xffffffff) ||
184  (p % 4 != 0)))
185  {
186  goto _continue;
187  }
188 
189  if (gGuest.Guest64 &&
191  (p % 4 != 0) ||
192  ((p & 0xFFFFFFFF00000000) == 0xFFFFFFFF00000000)))
193  {
194  goto _continue;
195  }
196 
197  if (pSec->Name[0] != '.')
198  {
199  goto _almostro;
200  }
201 
202  if (0 == gWinGuest->PsLoadedModuleList)
203  {
204  status = IntWinDrvIsListHead(target, hostPtr, p);
205  if (INT_SUCCESS(status))
206  {
207  gWinGuest->PsLoadedModuleList = target;
208 
209  TRACE("[INTRO-INIT] Found loaded module list: 0x%llx\n", target);
210 
211  objectsFound++;
212 
213  goto _continue;
214  }
215  }
216 
217  if (0 == gWinGuest->PsActiveProcessHead)
218  {
219  // p now points to what we believe is the EPROCESS of the System process.
221  if (INT_SUCCESS(status))
222  {
223  gWinGuest->PsActiveProcessHead = target;
224 
225  TRACE("[INTRO-INIT] Found process list head: 0x%llx\n", target);
226 
227  objectsFound++;
228 
229  goto _continue;
230  }
231  }
232 
233 _almostro:
234  if (pSec->Name[0] == '.')
235  {
236  goto _continue;
237  }
238 
239  if (gWinGuest->MmPfnDatabase == 0)
240  {
241  status = IntWinPfnIsMmPfnDatabase(p);
242  if (INT_SUCCESS(status))
243  {
244  // Save the actual location, not the kernel pointer to it
245  gWinGuest->MmPfnDatabase = p;
246 
247  TRACE("[INTRO-INIT] Found PFN database: 0x%llx\n", p);
248 
249  // There are cases where pfn is before non-paged pool size
250  objectsFound++;
251 
252  goto _continue;
253  }
254  }
255 
256 _continue:
257  parsed++;
258 
259  target += gGuest.WordSize;
260 
261  if (objectsFound == objCount)
262  {
263  break;
264  }
265  }
266 
267  if (offset > gWinGuest->KernelBufferSize)
268  {
269  IntVirtMemUnmap(&pPage.ptrValue);
270  }
271 
272  if (objectsFound == objCount)
273  {
274  break;
275  }
276  }
277  }
278 
279  if (objectsFound < objCount)
280  {
281  status = INT_STATUS_NOT_FOUND;
282 
283  // Be more explicit
284  if (0 == gWinGuest->MmPfnDatabase)
285  {
286  ERROR("[ERROR] MmPfnDatabase not found!\n");
287  }
288 
289  if (0 == gWinGuest->PsLoadedModuleList)
290  {
291  ERROR("[ERROR] PsLoadedModuleList not found!\n");
292  }
293 
294  if (0 == gWinGuest->PsActiveProcessHead)
295  {
296  ERROR("[ERROR] PsActiveProcessHead not found!\n");
297  }
298  }
299  else
300  {
301  status = INT_STATUS_SUCCESS;
302  }
303 
304 cleanup_and_exit:
305  if (NULL != pSections)
306  {
307  HpFreeAndNullWithTag(&pSections, IC_TAG_IMGE);
308  }
309 
310  return status;
311 }
312 
313 
314 static INTSTATUS
316  void
317  )
329 {
330  INTSTATUS status;
331  QWORD systemProcess = 0;
332 
334  if (!INT_SUCCESS(status))
335  {
336  TRACE("[INTRO-INIT] IntWinGuestFindKernelObjects%d: 0x%08x\n", gGuest.Guest64 ? 64 : 32, status);
337  return status;
338  }
339 
340  status = IntKernVirtMemRead(gWinGuest->PsActiveProcessHead,
342  &systemProcess,
343  NULL);
344  if (!INT_SUCCESS(status))
345  {
346  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x\n", status);
347  return status;
348  }
349 
350  status = IntKernVirtMemFetchQword(systemProcess -
351  WIN_KM_FIELD(Process, ListEntry) +
352  WIN_KM_FIELD(Process, Cr3),
353  &gGuest.Mm.SystemCr3);
354  if (!INT_SUCCESS(status))
355  {
356  ERROR("[ERROR] IntKernVirtMemFetchQword failed: 0x%08x\n", status);
357  return status;
358  }
359 
360  // The CR3 (DirectoryTableBase field in KPROCESS) is 8 bytes only in long mode.
361  if (!gGuest.Guest64)
362  {
363  gGuest.Mm.SystemCr3 &= 0xFFFFFFFF;
364  }
365 
366  TRACE("[INTRO-INIT] System CR3 is 0x%016llx!\n", gGuest.Mm.SystemCr3);
367 
368  return INT_STATUS_SUCCESS;
369 }
370 
371 
372 static INTSTATUS
374  void
375  )
393 {
394  INTSTATUS status;
395  QWORD *p;
396 
399  {
401  }
402 
404  if (!INT_SUCCESS(status))
405  {
406  ERROR("[ERROR] IntPhysMemMap failed for 0x%016llx: 0x%08x\n", gGuest.Mm.SystemCr3 & PHYS_PAGE_MASK, status);
407  return status;
408  }
409 
410  for (DWORD i = 256; i < 512; i++)
411  {
412  if (((p[i] & PHYS_PAGE_MASK) == (gGuest.Mm.SystemCr3 & PHYS_PAGE_MASK)) && (0 == (p[i] & 0x800)))
413  {
414  LOG("[SELFMAP] Found index = %d (0x%x)\n", i, i);
415  gGuest.Mm.SelfMapIndex = i;
416  status = INT_STATUS_SUCCESS;
417  break;
418  }
419  }
420 
421  if (0 == gGuest.Mm.SelfMapIndex)
422  {
423  ERROR("[ERROR] Self map index not found!\n");
425  status = INT_STATUS_NOT_FOUND;
426  }
427 
428  IntPhysMemUnmap(&p);
429 
430  return status;
431 }
432 
433 
434 static BOOLEAN
436  _In_ QWORD KernelAddress,
437  _In_ const VA_TRANSLATION *GvaTranslation,
438  _In_ QWORD Cr3
439  )
452 {
453  INTSTATUS status;
454  VA_TRANSLATION nkt;
455  BOOLEAN valid = FALSE;
456  QWORD *p;
457 
458  // Try to translate the kernel virtual address and see if it matches
459  // Also, don't add the GPAs to the cache since they will be mostly wrong
460 
461  status = IntTranslateVirtualAddressEx(KernelAddress, Cr3, TRFLG_NONE, &nkt);
462  if (!INT_SUCCESS(status))
463  {
464  return FALSE;
465  }
466 
467  if (0 == (nkt.Flags & PT_P))
468  {
469  return FALSE;
470  }
471 
472  if ((nkt.PhysicalAddress != GvaTranslation->PhysicalAddress) ||
473  (nkt.MappingsCount != GvaTranslation->MappingsCount))
474  {
475  return FALSE;
476  }
477 
478  status = IntPhysMemMap(Cr3, PAGE_SIZE, 0, &p);
479  if (!INT_SUCCESS(status))
480  {
481  return FALSE;
482  }
483 
484  if (GvaTranslation->PagingMode == PAGING_4_LEVEL_MODE ||
485  GvaTranslation->PagingMode == PAGING_5_LEVEL_MODE)
486  {
487  // On 4-level paging, make sure the self map entry doesn't have bit 11 set.
488  BOOLEAN selfMapOk = FALSE;
489 
490  for (DWORD i = 256; i < 512; i++)
491  {
492  if (((p[i] & PHYS_PAGE_MASK) == Cr3) && (0 == (p[i] & 0x800)))
493  {
494  selfMapOk = TRUE;
495  break;
496  }
497  }
498 
499  if (!selfMapOk)
500  {
501  valid = FALSE;
502  goto _cleanup_and_leave;
503  }
504  }
505  else if (GvaTranslation->PagingMode == PAGING_PAE_MODE)
506  {
507  // On PAE paging, make sure the rest of the page is zero.
508  for (DWORD i = 4; i < 512; i++)
509  {
510  if (p[i] != 0)
511  {
512  valid = FALSE;
513  goto _cleanup_and_leave;
514  }
515  }
516  }
517  else
518  {
519  // Ignore normal paging mode.
520  }
521 
522  if (GvaTranslation->PhysicalAddress != nkt.PhysicalAddress)
523  {
524  WARNING("[WARNING] We have a translation, but different physical addresses for 0x%016llx: "
525  "0x%016llx != 0x%016llx\n", KernelAddress, GvaTranslation->PhysicalAddress, nkt.PhysicalAddress);
526 
527  valid = FALSE;
528  goto _cleanup_and_leave;
529  }
530 
531  valid = TRUE;
532 
533 _cleanup_and_leave:
534  IntPhysMemUnmap(&p);
535 
536  return valid;
537 }
538 
539 
540 static INTSTATUS
542  _In_ QWORD KernelAddress,
543  _Out_ QWORD *SystemCr3,
544  _In_ QWORD StartPhysical,
545  _In_ QWORD EndPhysical
546  )
561 {
562  INTSTATUS status;
563  const QWORD startPhys = StartPhysical;
564  const QWORD endPhys = EndPhysical;
565  QWORD currentCr3 = 0;
566  VA_TRANSLATION kt;
567 
568  if (NULL == SystemCr3)
569  {
571  }
572 
573  status = IntCr3Read(IG_CURRENT_VCPU, &currentCr3);
574  if (!INT_SUCCESS(status))
575  {
576  ERROR("[ERROR] IntCr3Read failed: 0x%08x\n", status);
577  return status;
578  }
579 
580  if (currentCr3 >= startPhys && currentCr3 < endPhys)
581  {
582  LOG("[WINGUEST STATIC] The current CR3 0x%016llx is already the system cr3!\n", currentCr3);
583  *SystemCr3 = currentCr3;
584 
585  return INT_STATUS_SUCCESS;
586  }
587 
588  status = IntTranslateVirtualAddressEx(KernelAddress, currentCr3, TRFLG_NONE, &kt);
589  if (!INT_SUCCESS(status))
590  {
591  ERROR("[ERROR] IntTranslateVirtualAddressEx failed for 0x%016llx with cr3 0x%016llx: 0x%08x\n",
592  KernelAddress, currentCr3, status);
593  return status;
594  }
595 
596  for (QWORD gpa = startPhys; gpa < endPhys; gpa += PAGE_SIZE)
597  {
598  if (IntWinGuestIsSystemCr3(KernelAddress, &kt, gpa))
599  {
600  *SystemCr3 = gpa;
601 
602  return INT_STATUS_SUCCESS;
603  }
604  }
605 
606  return INT_STATUS_NOT_FOUND;
607 }
608 
609 
610 void
612  void
613  )
620 {
621  INTSTATUS status;
622  LIST_ENTRY *initEntry;
623 
624  initEntry = gWinGuest->InitSwapHandles.Flink;
625  while (initEntry != &gWinGuest->InitSwapHandles)
626  {
627  PWIN_INIT_SWAP pInitSwap = CONTAINING_RECORD(initEntry, WIN_INIT_SWAP, Link);
628  initEntry = initEntry->Flink;
629 
630  status = IntSwapMemRemoveTransaction(pInitSwap->SwapHandle);
631  if (!INT_SUCCESS(status))
632  {
633  ERROR("[ERROR] IntSwapMemRemoveTransaction failed for %llx:%x: 0x%08x\n",
634  pInitSwap->VirtualAddress, pInitSwap->Size, status);
635  }
636 
637  RemoveEntryList(&pInitSwap->Link);
638 
639  HpFreeAndNullWithTag(&pInitSwap, IC_TAG_WSWP);
640  }
641 }
642 
643 
644 INTSTATUS
646  void
647  )
658 {
659  INTSTATUS status;
660 
662  if (!INT_SUCCESS(status))
663  {
664  ERROR("[ERROR] IntRegisterBreakpointHandler failed: 0x%08x\n", status);
665  return status;
666  }
667 
669 
670  return INT_STATUS_SUCCESS;
671 }
672 
673 
674 void
676  void
677  )
684 {
685  INTSTATUS status;
686 
687  if (!gGuest.GuestInitialized || NULL == gWinGuest)
688  {
689  return;
690  }
691 
692  if (gWinGuest->KernelBuffer)
693  {
695  }
696 
697  IntDriverUninit();
698 
699  status = IntWinDrvObjUninit();
700  if (!INT_SUCCESS(status))
701  {
702  ERROR("[ERROR] IntWinDrvObjUninit failed: 0x%08x\n", status);
703  }
704 
705  IntWinHalUninit();
706 
708 
709  // Do this after the process subsystem uninits, so we can free there whatever
710  // caches that weren't fully formed (only allocated)
712 
713  if (NULL != gWinGuest->NtBuildLabString)
714  {
716  }
717 
718  if (NULL != gWinGuest->VersionString)
719  {
721  }
722 
723  if (NULL != gWinGuest->ServerVersionString)
724  {
726  }
727 
728  status = IntCr4Unprotect();
729  if (!INT_SUCCESS(status))
730  {
731  ERROR("[ERROR] IntCr4Unprotect failed: 0x%08x\n", status);
732  }
733 
735 
737 }
738 
739 
740 static void
742  _In_ void *Originator,
743  _In_ EXCEPTION_VICTIM_ZONE *Victim,
744  _In_ BOOLEAN IsKernel,
745  _In_ INTRO_ACTION Action,
747  )
759 {
760  EVENT_EPT_VIOLATION *pEpt = &gAlert.Ept;
761  INTSTATUS status;
762 
763  memzero(pEpt, sizeof(*pEpt));
764 
765  pEpt->Header.Action = Action;
766  pEpt->Header.Reason = Reason;
768 
769  IntAlertEptFillFromVictimZone(Victim, pEpt);
770 
772 
774 
776 
777  pEpt->ExecInfo.Rsp = gVcpu->Regs.Rsp;
778  pEpt->ExecInfo.Length = gVcpu->Instruction.Length;
779 
780  if (IsKernel)
781  {
782  EXCEPTION_KM_ORIGINATOR *originator = Originator;
783 
784  IntAlertEptFillFromKmOriginator(originator, pEpt);
785  }
786  else
787  {
788  EXCEPTION_UM_ORIGINATOR *originator = Originator;
789 
791  pEpt->ReturnRip = originator->Return.Rip;
792  }
793 
796 
798 
799  status = IntNotifyIntroEvent(introEventEptViolation, pEpt, sizeof(*pEpt));
800  if (!INT_SUCCESS(status))
801  {
802  WARNING("[WARNING] IntNotifyIntroEvent failed: 0x%08x\n", status);
803  }
804 }
805 
806 
807 static INTSTATUS
809  _In_ QWORD Address,
810  _Inout_ INTRO_ACTION *Action
811  )
824 {
825  EXCEPTION_VICTIM_ZONE victim = { 0 };
826  EXCEPTION_KM_ORIGINATOR originator = { 0 };
827  BOOLEAN exitAfterInformation = FALSE;
829  INTSTATUS status;
830 
832 
833  status = IntExceptKernelGetOriginator(&originator, 0);
834  if (status == INT_STATUS_EXCEPTION_BLOCK)
835  {
836  reason = introReasonNoException;
837  exitAfterInformation = TRUE;
838  }
839  else if (!INT_SUCCESS(status))
840  {
841  ERROR("[ERROR] IntExceptKernelGetOriginator failed: %08x\n", status);
842  reason = introReasonInternalError;
843  exitAfterInformation = TRUE;
844  }
845 
846  status = IntExceptGetVictimEpt(NULL,
847  Address,
848  gVcpu->Regs.Rip,
850  ZONE_EXECUTE,
851  &victim);
852  if (!INT_SUCCESS(status))
853  {
854  ERROR("[ERROR] IntExceptGetVictimEpt failed: %08x\n", status);
855  reason = introReasonInternalError;
856  exitAfterInformation = TRUE;
857  }
858 
859  if (exitAfterInformation)
860  {
861  IntExceptKernelLogInformation(&victim, &originator, *Action, reason);
862  }
863  else
864  {
865  IntExcept(&victim, &originator, exceptionTypeKm, Action, &reason, introEventEptViolation);
866  }
867 
869 
871  {
872  LOG("[SUD-EXEC] An execution on shared user data occured in kernel-mode!\n");
873 
874  IntDumpCodeAndRegs(gVcpu->Regs.Rip, Address, &gVcpu->Regs);
875 
876  IntWinGuestSendSudAlert(&originator, &victim, TRUE, *Action, reason);
877  }
878 
880 
881  return status;
882 }
883 
884 
885 static INTSTATUS
887  _In_ QWORD Address,
888  _Inout_ INTRO_ACTION *Action
889  )
903 {
904  EXCEPTION_UM_ORIGINATOR originator = { 0 };
905  EXCEPTION_VICTIM_ZONE victim = { 0 };
906  WIN_PROCESS_OBJECT *pProc;
908  BOOLEAN exitAfterInformation = FALSE;
909  INTSTATUS status;
910 
912  if (NULL == pProc)
913  {
914  ERROR("[ERROR] No process found with cr3: 0x%016llx, but ring is 3! Will inject #UD!",
915  gVcpu->Regs.Cr3);
916  return INT_STATUS_NOT_FOUND;
917  }
918 
920 
921  status = IntExceptUserGetExecOriginator(pProc, &originator);
922  if (!INT_SUCCESS(status))
923  {
924  ERROR("[ERROR] Failed getting originator: 0x%08x\n", status);
925  exitAfterInformation = TRUE;
926  }
927 
928  status = IntExceptGetVictimEpt(pProc,
929  Address,
930  gVcpu->Regs.Rip,
932  ZONE_EXECUTE,
933  &victim);
934  if (!INT_SUCCESS(status))
935  {
936  ERROR("[ERROR] Failed getting modified zone: 0x%08x\n", status);
937  exitAfterInformation = TRUE;
938  }
939  if (exitAfterInformation)
940  {
941  IntExceptUserLogInformation(&victim, &originator, *Action, reason);
942  }
943  else
944  {
945  IntExcept(&victim, &originator, exceptionTypeUm, Action, &reason, introEventEptViolation);
946  }
947 
949 
951  {
952  LOG("[SUD-EXEC] An execution on shared user data occured in user-mode!\n");
953 
954  IntDumpCodeAndRegs(gVcpu->Regs.Rip, Address, &gVcpu->Regs);
955 
956  IntWinGuestSendSudAlert(&originator, &victim, FALSE, *Action, reason);
957  }
958 
960 
961  return status;
962 }
963 
964 
965 static INTSTATUS
967  _In_ void *Context,
968  _In_ void *Hook,
969  _In_ QWORD Address,
970  _Out_ INTRO_ACTION *Action
971  )
991 {
992  INTSTATUS status;
993  DWORD ring;
994  INFO_UD_PENDING *pending = NULL;
995  QWORD currentThread = 0;
996 
997  UNREFERENCED_PARAMETER(Context);
999 
1000  *Action = introGuestNotAllowed;
1001 
1002  status = IntGetCurrentRing(gVcpu->Index, &ring);
1003  if (!INT_SUCCESS(status))
1004  {
1005  ERROR("[ERROR] IntGetCurrentRing failed: 0x%08x\n", status);
1006  IntBugCheck();
1007  }
1008 
1009  if (ring == IG_CS_RING_0)
1010  {
1011  status = IntWinGuestHandleKernelSudExec(Address, Action);
1012  if (!INT_SUCCESS(status))
1013  {
1014  ERROR("[ERROR] IntWinGuestHandleKernelSudExec failed: 0x%08x\n", status);
1015  }
1016  }
1017  else
1018  {
1019  status = IntWinThrGetCurrentThread(gVcpu->Index, &currentThread);
1020  if (!INT_SUCCESS(status))
1021  {
1022  ERROR("[ERROR] IntWinThrGetCurrentThread failed: 0x%08x\n", status);
1023  return status;
1024  }
1025 
1026  pending = IntUDGetEntry(gVcpu->Regs.Cr3, gVcpu->Regs.Rip, currentThread);
1027  if (NULL != pending)
1028  {
1029  goto cleanup_and_take_action;
1030  }
1031 
1032  status = IntWinGuestHandleUserSudExec(Address, Action);
1033  if (!INT_SUCCESS(status))
1034  {
1035  ERROR("[ERROR] IntWinGuestHandleKernelSudExec failed: 0x%08x\n", status);
1036  }
1037  }
1038 
1039 cleanup_and_take_action:
1040  if (*Action == introGuestNotAllowed)
1041  {
1042  if (ring == IG_CS_RING_0)
1043  {
1044  BYTE ret = 0xc3;
1045 
1046  status = IntPhysicalMemWrite(Address, sizeof(ret), &ret);
1047  if (!INT_SUCCESS(status))
1048  {
1049  ERROR("[ERROR] IntVirtMemWrite failed: 0x%08x\n", status);
1050  }
1051 
1052  // Force action to introGuestAllowed, in order to force emulation, otherwise we'll end up in an infinite
1053  // cycle, depending on the hypervisor by forcing it to introGuestRetry. For example, this happens on xen.
1054  *Action = introGuestAllowed;
1055  }
1056  else
1057  {
1058  // We are already waiting for the current #UD to get injected.
1059  if (NULL != pending && gVcpu->CurrentUD == pending)
1060  {
1061  goto _skip_inject;
1062  }
1063 
1065  if (!INT_SUCCESS(status))
1066  {
1067  ERROR("[ERROR] IntInjectExceptionInGuest failed: 0x%08x\n", status);
1068  }
1069  else
1070  {
1071  if (NULL == pending)
1072  {
1073  status = IntUDAddToPendingList(gVcpu->Regs.Cr3,
1074  gVcpu->Regs.Rip,
1075  currentThread,
1076  &pending);
1077  if (!INT_SUCCESS(status))
1078  {
1079  ERROR("[ERROR] IntUDAddToPendingList failed: 0x%08x\n", status);
1080  return status;
1081  }
1082  }
1083 
1084  gVcpu->CurrentUD = pending;
1085  }
1086 
1087 _skip_inject:
1088  *Action = introGuestRetry;
1089  }
1090  }
1091 
1092  // Even if the action has been set as allowed by the exceptions mechanism, we'll not remove the hook,
1093  // as the hook needs to remain established.
1094 
1095  return INT_STATUS_SUCCESS;
1096 }
1097 
1098 
1099 INTSTATUS
1101  void
1102  )
1109 {
1110  INTSTATUS status;
1111 
1112  if (NULL != gSudExecHook)
1113  {
1115  }
1116 
1119  PAGE_SIZE,
1122  NULL,
1123  NULL,
1124  0,
1125  &gSudExecHook);
1126  if (!INT_SUCCESS(status))
1127  {
1128  ERROR("[ERROR] IntHookGvaSetHook failed: 0x%08x\n", status);
1129  }
1130 
1131  return status;
1132 }
1133 
1134 INTSTATUS
1136  void
1137  )
1144 {
1145  INTSTATUS status;
1146 
1147  if (NULL == gSudExecHook)
1148  {
1150  }
1151 
1152  status = IntHookGvaRemoveHook(&gSudExecHook, 0);
1153  if (!INT_SUCCESS(status))
1154  {
1155  ERROR("[ERROR] IntHookGvaRemoveHook failed: 0x%08x\n", status);
1156  }
1157 
1158  return status;
1159 }
1160 
1161 
1162 INTSTATUS
1164  void
1165  )
1175 {
1176  INTSTATUS status;
1177 
1179  {
1180  status = IntWinIdtProtectAll();
1181  if (!INT_SUCCESS(status))
1182  {
1183  ERROR("[ERROR] IntWinIdtProtectAll failed: 0x%08x\n", status);
1184  return status;
1185  }
1186  }
1187 
1189  {
1190  status = IntMsrSyscallProtect();
1191  if (!INT_SUCCESS(status))
1192  {
1193  ERROR("[ERROR] IntMsrSyscallProtect failed: 0x%08x\n", status);
1194  return status;
1195  }
1196  }
1197 
1199  {
1200  status = IntCr4Protect();
1201  if (!INT_SUCCESS(status))
1202  {
1203  ERROR("[ERROR] IntCr4Protect failed: 0x%08x\n", status);
1204  return status;
1205  }
1206  }
1207 
1209  {
1210  status = IntIdtrProtect();
1211  if (!INT_SUCCESS(status))
1212  {
1213  ERROR("[ERROR] IntIdtrProtect failed: 0x%08x\n", status);
1214  return status;
1215  }
1216  }
1217 
1219  {
1220  status = IntGdtrProtect();
1221  if (!INT_SUCCESS(status))
1222  {
1223  ERROR("[ERROR] IntGdtrProtect failed: 0x%08x\n", status);
1224  return status;
1225  }
1226  }
1227 
1229  {
1230  status = IntWinGuestProtectSudExec();
1231  if (!INT_SUCCESS(status))
1232  {
1233  ERROR("[ERROR] IntWinGuestProtectSudExec failed: 0x%08x\n", status);
1234  return status;
1235  }
1236  }
1237 
1238  // Flag the protection
1240 
1241  return INT_STATUS_SUCCESS;
1242 }
1243 
1244 
1245 static INTSTATUS
1247  void
1248  )
1258 {
1259  INTSTATUS status;
1260  QWORD expGva;
1261 
1262  status = IntPeFindKernelExport("PsCreateSystemThread", &gWinGuest->PsCreateSystemThread);
1263  if (!INT_SUCCESS(status))
1264  {
1265  ERROR("[ERROR] %s not found: 0x%08x\n", "PsCreateSystemThread", status);
1266  return status;
1267  }
1268 
1269  TRACE("[INTRO-INIT] Found API function PsCreateSystemThread @ 0x%016llx...\n", gWinGuest->PsCreateSystemThread);
1270 
1271  status = IntPeFindKernelExport("ExAllocatePoolWithTag", &gWinGuest->ExAllocatePoolWithTag);
1272  if (!INT_SUCCESS(status))
1273  {
1274  ERROR("[ERROR] %s not found: 0x%08x\n", "ExAllocatePoolWithTag", status);
1275  return status;
1276  }
1277 
1278  TRACE("[INTRO-INIT] Found API function ExAllocatePoolWithTag @ 0x%016llx...\n", gWinGuest->ExAllocatePoolWithTag);
1279 
1280  status = IntPeFindKernelExport("ExFreePoolWithTag", &gWinGuest->ExFreePoolWithTag);
1281  if (!INT_SUCCESS(status))
1282  {
1283  ERROR("[ERROR] %s not found: 0x%08x\n", "ExFreePoolWithTag", status);
1284  return status;
1285  }
1286 
1287  TRACE("[INTRO-INIT] Found API function ExFreePoolWithTag @ 0x%016llx...\n", gWinGuest->ExFreePoolWithTag);
1288 
1289  status = IntPeFindKernelExport("NtBuildNumber", &expGva);
1290  if (!INT_SUCCESS(status))
1291  {
1292  ERROR("[ERROR] %s not found: 0x%08x\n", "NtBuildNumber", status);
1293  return status;
1294  }
1295 
1296  status = IntKernVirtMemFetchDword(expGva, &gWinGuest->NtBuildNumberValue);
1297  if (!INT_SUCCESS(status))
1298  {
1299  ERROR("[ERROR] Failed getting NtBuildNumber value: %08x\n", status);
1300  return status;
1301  }
1302 
1303  TRACE("[INTRO-INIT] Found NtBuildNumber @ 0x%016llx with value 0x%08x...\n",
1304  expGva, gWinGuest->NtBuildNumberValue);
1305 
1306  status = IntPeFindKernelExport("NtBuildLab", &expGva);
1307  if (!INT_SUCCESS(status))
1308  {
1309  ERROR("[ERROR] %s not found: 0x%08x\n", "NtBuildLab", status);
1310  return status;
1311  }
1312 
1313  status = IntReadString(expGva, 16, FALSE, &gWinGuest->NtBuildLabString, NULL);
1314  if (!INT_SUCCESS(status))
1315  {
1316  ERROR("[ERROR] Failed getting NtBuildLab value for kernel base 0x%016llx:0x%08x\n", gGuest.KernelVa, status);
1317  return status;
1318  }
1319 
1320  TRACE("[INTRO-INIT] Found NtBuildLab @ 0x%016llx with value: `%s`\n",
1321  expGva, gWinGuest->NtBuildLabString);
1322 
1323  if (!gGuest.Guest64)
1324  {
1325  status = IntPeFindKernelExport("KeServiceDescriptorTable", &gWinGuest->KeServiceDescriptorTable);
1326  if (!INT_SUCCESS(status))
1327  {
1328  ERROR("[ERROR] %s not found: 0x%08x\n", "KeServiceDescriptorTable RVA", status);
1329  return status;
1330  }
1331 
1332  status = IntKernVirtMemFetchDword(gWinGuest->KeServiceDescriptorTable, (DWORD *)&gWinGuest->Ssdt);
1333  if (!INT_SUCCESS(status))
1334  {
1335  ERROR("[ERROR] Failed getting KeServiceDescriptorTable value: %08x\n", status);
1336  return status;
1337  }
1338 
1339  // Now get the number of services. The KeServiceDescriptorTable is a struct, and the number of services
1340  // is the third DWORD inside this structure.
1341  status = IntKernVirtMemFetchDword(gWinGuest->KeServiceDescriptorTable + 8, &gWinGuest->NumberOfServices);
1342  if (!INT_SUCCESS(status))
1343  {
1344  ERROR("[ERROR] Failed getting KeServiceDescriptorTable number of services: %08x\n", status);
1345  return status;
1346  }
1347 
1348  if (gWinGuest->NumberOfServices > 1024)
1349  {
1350  ERROR("[ERROR] The number of services in the SSDT is higher than expected: %u\n",
1351  gWinGuest->NumberOfServices);
1353  }
1354 
1355  TRACE("[INTRO-INIT] Found KeServiceDescriptorTable @ 0x%016llx with table at 0x%016llx and %d functions...\n",
1356  gWinGuest->KeServiceDescriptorTable, gWinGuest->Ssdt, gWinGuest->NumberOfServices);
1357  }
1358 
1359  return INT_STATUS_SUCCESS;
1360 }
1361 
1362 
1363 static INTSTATUS
1365  _Out_ WIN_PRODUCT_TYPE *ProductType
1366  )
1380 {
1381 // It seems they are hardcoded on every os but we might think about moving them to cami.
1382 #define WIN_SHARED_USER_DATA_OFFSET_PRODUCT 0x264
1383 #define WIN_SHARED_USER_DATA_OFFSET_PROD_VALID 0x268
1384 
1385  INTSTATUS status;
1386  DWORD valid, productType;
1387 
1388  if (!gGuest.Guest64)
1389  {
1390  *ProductType = winProductTypeWinNt;
1392  }
1393 
1395  if (!INT_SUCCESS(status))
1396  {
1397  return status;
1398  }
1399 
1400  if (!valid)
1401  {
1403  }
1404 
1406  if (!INT_SUCCESS(status))
1407  {
1408  return status;
1409  }
1410 
1411  if (productType != winProductTypeWinNt &&
1412  productType != winProductTypeLanManNt &&
1413  productType != winProductTypeServer)
1414  {
1416  }
1417 
1418  *ProductType = productType;
1419 
1420  return INT_STATUS_SUCCESS;
1421 
1422 #undef WIN_SHARED_USER_DATA_OFFSET_PRODUCT
1423 #undef WIN_SHARED_USER_DATA_OFFSET_PROD_VALID
1424 }
1425 
1426 
1427 static INTSTATUS
1429  void
1430  )
1446 {
1447  INTSTATUS status;
1448 
1449  status = IntWinGuestResolveImports();
1450  if (!INT_SUCCESS(status))
1451  {
1452  ERROR("[ERROR] IntWinGuestResolveImports failed: 0x%08x\n", status);
1453 
1455 
1456  goto leave_and_unload;
1457  }
1458 
1460  {
1461  ERROR("[ERROR] Guest %d booted with /3GB is not supported!\n", gGuest.OSVersion);
1462 
1464 
1466 
1467  goto leave_and_unload;
1468  }
1469 
1470  // Set the OS specific fields in the guest-state structure
1471  gGuest.OSVersion = gWinGuest->NtBuildNumberValue & 0xFFFF;
1473 
1474  status = IntWinGuestFetchProductType(&gWinGuest->ProductType);
1475  if (!INT_SUCCESS(status))
1476  {
1477  WARNING("[WARNING] IntWinGuestFetchProductType failed: 0x%08x, will not be able to determine whether it is a "
1478  "server or not\n", status);
1479  gWinGuest->ProductType = winProductTypeUnknown;
1480  }
1481 
1482  status = IntWinGuestIsSupported();
1483  if (!INT_SUCCESS(status))
1484  {
1485  ERROR("[ERROR] NtBuildNumber %04x is not supported!\n", gGuest.OSVersion);
1486 
1488 
1490 
1491  goto leave_and_unload;
1492  }
1493 
1494  LOG("[INFO] Identified OS type Windows, version %d\n", gGuest.OSVersion);
1495  LOG("[INFO] Guest has KPTI %s\n", gGuest.KptiInstalled ? "Installed" : "Not installed");
1496 
1498 
1499  // Fill in the auxiliary data. These fields may not be persistent and may change during the
1500  // normal execution of the OS, so these APIs may be called as many times as necessarily
1501  // (example, one could register a callback for a given event and call these functions again
1502  // anytime that event is triggered). However, all the fields are lists of some sort, and the
1503  // head will remain valid during the normal execution of the guest.
1504 
1505  status = IntWinGuestFindKernelObjects();
1506  if (!INT_SUCCESS(status))
1507  {
1508  if (INT_STATUS_LOAD_ABORTED != status)
1509  {
1510  ERROR("[ERROR] IntWinGuestFindKernelObjects failed: 0x%08x\n", status);
1511 
1513  }
1514  else
1515  {
1516  WARNING("[WARNING] Introcore load was aborted!\n");
1517  }
1518 
1519  goto leave_and_unload;
1520  }
1521 
1522  TRACE("[INTRO-INIT] Kernel objects successfully identified!\n");
1523 
1524  status = IntWinAgentInjectTrampoline();
1525  if (!INT_SUCCESS(status))
1526  {
1527  ERROR("[ERROR] IntWinAgentInjectTrampoline failed: 0x%08x\n", status);
1528  goto leave_and_unload;
1529  }
1530 
1531  status = IntWinApiHookAll();
1532  if (!INT_SUCCESS(status))
1533  {
1534  ERROR("[ERROR] IntWinApiHookAll failed: 0x%08x\n", status);
1535  goto leave_and_unload;
1536  }
1537 
1538  status = IntSwapgsStartMitigation();
1539  if (!INT_SUCCESS(status))
1540  {
1541  ERROR("[ERROR] IntSwapgsStartMitigation failed: 0x%08x\n", status);
1542  goto leave_and_unload;
1543  }
1544 
1546  if (!INT_SUCCESS(status))
1547  {
1548  ERROR("[ERROR] IntWinDrvIterateLoadedModules failed: 0x%08x\n", status);
1549  goto leave_and_unload;
1550  }
1551 
1553  if (NULL == gGuest.KernelDriver)
1554  {
1555  ERROR("[ERROR] Failed finding kernel module!\n");
1556  goto leave_and_unload;
1557  }
1558 
1559  LOG("[INTRO-INIT] Kernel loaded @ 0x%016llx size of image = 0x%llx timedate stamp = 0x%08x\n",
1561 
1562  status = IntWinHalCreateHalData();
1563  if (!INT_SUCCESS(status))
1564  {
1565  ERROR("[ERROR] IntWinHalCreateHalData failed: 0x%08x\n", status);
1566  goto leave_and_unload;
1567  }
1568 
1570  if (!INT_SUCCESS(status))
1571  {
1572  ERROR("[ERROR] IntWinProcIterateGuestProcesses failed: 0x%08x\n", status);
1573  goto leave_and_unload;
1574  }
1575 
1576  status = IntWinGuestActivateProtection();
1577  if (!INT_SUCCESS(status))
1578  {
1579  ERROR("[ERROR] IntWinGuestActivateProtection failed: 0x%08x\n", status);
1580  goto leave_and_unload;
1581  }
1582 
1584  {
1585  status = IntVeDeployAgent();
1586  if (!INT_SUCCESS(status))
1587  {
1588  ERROR("[ERROR] IntVeDeployAgent failed: 0x%08x\n", status);
1589  }
1590  }
1591 
1592  // Make sure no threads have a RIP pointing in a modified code region.
1593  TRACE("[WINGUEST] Ensuring no thread will return into our hooks!\n");
1594 
1596  if (!INT_SUCCESS(status))
1597  {
1598  ERROR("[ERROR] IntThrSafeCheckThreads failed: 0x%08x\n", status);
1599  goto leave_and_unload;
1600  }
1601 
1603 
1605 
1606  TRACE("[WINGUEST] Introspection successfully initialized!\n");
1607 
1608  return INT_STATUS_SUCCESS;
1609 
1610 leave_and_unload:
1612  return status;
1613 }
1614 
1615 
1616 static INTSTATUS
1618  _Inout_ WIN_INIT_SWAP *Context,
1619  _In_ QWORD Cr3,
1620  _In_ QWORD VirtualAddress,
1621  _In_ QWORD PhysicalAddress,
1622  _In_reads_bytes_(DataSize) void *Data,
1623  _In_ DWORD DataSize,
1624  _In_ DWORD Flags
1625  )
1650 {
1651  PWIN_INIT_SWAP pSwp;
1652  QWORD va;
1653  INTSTATUS status;
1654 
1656  UNREFERENCED_PARAMETER(VirtualAddress);
1657  UNREFERENCED_PARAMETER(PhysicalAddress);
1658 
1659  if (Flags & SWAPMEM_FLAG_ASYNC_CALL)
1660  {
1661  IntPauseVcpus();
1662  }
1663 
1664  status = INT_STATUS_SUCCESS;
1665 
1666  pSwp = Context;
1667  va = pSwp->VirtualAddress;
1668 
1669  // Remove the context. The caller knows this may happen & won't use it after IntSwapMemReadData
1670  RemoveEntryList(&pSwp->Link);
1672 
1673  if (0 == gWinGuest->RemainingSections)
1674  {
1675  ERROR("[ERROR] Callback came after we have no more sections to read...\n");
1677  goto resume_and_exit;
1678  }
1679 
1680  memcpy(gWinGuest->KernelBuffer + va, Data, DataSize);
1681 
1682  gWinGuest->RemainingSections--;
1683 
1684  if ((0 == gWinGuest->RemainingSections) && (Flags & SWAPMEM_FLAG_ASYNC_CALL))
1685  {
1686  TRACE("[WINGUEST STATIC] Since we are called asynchronously we will finish the initialization...\n");
1687 
1688  status = IntWinGuestFinishInit();
1689  if (!INT_SUCCESS(status))
1690  {
1691  ERROR("[ERROR] IntWinGuestFinishInit failed: 0x%08x\n", status);
1692  }
1693  }
1694 
1695 resume_and_exit:
1696  if (Flags & SWAPMEM_FLAG_ASYNC_CALL)
1697  {
1698  IntResumeVcpus();
1699  }
1700 
1701  return status;
1702 }
1703 
1704 
1705 static INTSTATUS
1707  _In_ PBYTE KernelHeaders
1708  )
1729 {
1730  BOOLEAN unmapNtHeaders;
1731  DWORD secStartOffset, secCount;
1732  PIMAGE_DOS_HEADER pDosHeader;
1733  INTSTATUS status;
1734  INTRO_PE_INFO peInfo = { 0 };
1735 
1736  unmapNtHeaders = FALSE;
1737  pDosHeader = (PIMAGE_DOS_HEADER)KernelHeaders;
1738 
1739  // First thing, validate the buffer
1740  status = IntPeValidateHeader(gGuest.KernelVa, KernelHeaders, PAGE_SIZE, &peInfo, 0);
1741  if (!INT_SUCCESS(status))
1742  {
1743  ERROR("[ERROR] IntPeValidateHeader failed: 0x%08x\n", status);
1744  return status;
1745  }
1746 
1747  if (gGuest.Guest64 != peInfo.Image64Bit)
1748  {
1749  ERROR("[ERROR] Inconsistent MZPE image!\n");
1751  }
1752 
1753  // Update the size of the kernel
1754  gGuest.KernelSize = peInfo.SizeOfImage;
1755 
1756  if (peInfo.Image64Bit)
1757  {
1758  PIMAGE_NT_HEADERS64 pNth64;
1759 
1760  if ((QWORD)pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS64) < PAGE_SIZE)
1761  {
1762  // We are in the same page, so it's safe to use this
1763  pNth64 = (PIMAGE_NT_HEADERS64)(KernelHeaders + pDosHeader->e_lfanew);
1764  }
1765  else
1766  {
1767  status = IntVirtMemMap(gGuest.KernelVa + pDosHeader->e_lfanew, sizeof(*pNth64),
1768  gGuest.Mm.SystemCr3, 0, &pNth64);
1769  if (!INT_SUCCESS(status))
1770  {
1771  ERROR("[ERROR] Failed mapping VA 0x%016llx to host: 0x%08x\n",
1772  gGuest.KernelVa + pDosHeader->e_lfanew, status);
1773  return status;
1774  }
1775 
1776  unmapNtHeaders = TRUE;
1777  }
1778 
1779  secCount = 0xffff & pNth64->FileHeader.NumberOfSections;
1780  secStartOffset = pDosHeader->e_lfanew + 4 + sizeof(IMAGE_FILE_HEADER) +
1782 
1783  if (unmapNtHeaders)
1784  {
1785  IntVirtMemUnmap(&pNth64);
1786  }
1787  }
1788  else
1789  {
1790  PIMAGE_NT_HEADERS32 pNth32;
1791 
1792  if ((QWORD)pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS32) < PAGE_SIZE)
1793  {
1794  // We are in the same page, so it's safe to use this
1795  pNth32 = (PIMAGE_NT_HEADERS32)(KernelHeaders + pDosHeader->e_lfanew);
1796  }
1797  else
1798  {
1799  status = IntVirtMemMap(gGuest.KernelVa + pDosHeader->e_lfanew, sizeof(*pNth32),
1800  gGuest.Mm.SystemCr3, 0, &pNth32);
1801  if (!INT_SUCCESS(status))
1802  {
1803  ERROR("[ERROR] Failed mapping VA 0x%016llx to host: 0x%08x\n",
1804  gGuest.KernelVa + pDosHeader->e_lfanew, status);
1805  return status;
1806  }
1807 
1808  unmapNtHeaders = TRUE;
1809  }
1810 
1811  secCount = 0xffff & pNth32->FileHeader.NumberOfSections;
1812  secStartOffset = pDosHeader->e_lfanew + 4 + sizeof(IMAGE_FILE_HEADER) +
1814 
1815  if (unmapNtHeaders)
1816  {
1817  IntVirtMemUnmap(&pNth32);
1818  }
1819  }
1820 
1821  if (secStartOffset + secCount * sizeof(IMAGE_SECTION_HEADER) > PAGE_SIZE)
1822  {
1823  ERROR("[ERROR] Sections gets outside the first page. We don't support this yet!\n");
1824  return INT_STATUS_NOT_SUPPORTED;
1825  }
1826 
1827  if (peInfo.SizeOfImage < PAGE_SIZE)
1828  {
1829  ERROR("[ERROR] SizeOfImage too small: %d!\n", peInfo.SizeOfImage);
1831  }
1832 
1833  // Now finally initialize the lock & kernel buffer
1834  gWinGuest->KernelBuffer = HpAllocWithTag(peInfo.SizeOfImage, IC_TAG_KRNB);
1835  if (NULL == gWinGuest->KernelBuffer)
1836  {
1838  }
1839 
1840  gWinGuest->KernelBufferSize = peInfo.SizeOfImage;
1841  memcpy(gWinGuest->KernelBuffer, KernelHeaders, PAGE_SIZE);
1842 
1843  gWinGuest->RemainingSections = secCount;
1844 
1845  for (DWORD i = 0; i < secCount; i++)
1846  {
1847  DWORD secActualSize;
1848 
1849  PIMAGE_SECTION_HEADER pSec = (PIMAGE_SECTION_HEADER)(gWinGuest->KernelBuffer + secStartOffset +
1850  i * sizeof(IMAGE_SECTION_HEADER));
1851 
1852  secActualSize = ROUND_UP(pSec->Misc.VirtualSize, PAGE_SIZE);
1853 
1854  if (0 == pSec->VirtualAddress)
1855  {
1856  ERROR("[ERROR] We cannot have a section starting at 0!\n");
1857 
1858  return INT_STATUS_NOT_SUPPORTED;
1859  }
1860 
1861  if (0 == pSec->Misc.VirtualSize)
1862  {
1863  ERROR("[ERROR] We cannot have a section starting at 0!\n");
1864 
1865  return INT_STATUS_NOT_SUPPORTED;
1866  }
1867 
1868  // Make sure the section fits within the allocated buffer. We must avoid cases where the SizeOfImage or
1869  // section headers are maliciously altered.
1870  if ((pSec->VirtualAddress >= peInfo.SizeOfImage) ||
1871  (secActualSize > peInfo.SizeOfImage) ||
1872  (pSec->VirtualAddress + secActualSize > peInfo.SizeOfImage))
1873  {
1874  ERROR("[ERROR] Section %d seems corrupted: sizeOfImage = 0x%x, secstart = 0x%x, secsize = 0x%x\n",
1875  i, peInfo.SizeOfImage, pSec->VirtualAddress, pSec->Misc.VirtualSize);
1876 
1878  }
1879 
1881  (0 == memcmp(pSec->Name, "INITKDBG", sizeof("INITKDBG") - 1) ||
1882  (0 == memcmp(pSec->Name, "ERRATA", sizeof("ERRATA") - 1))))
1883  {
1884  memset(gWinGuest->KernelBuffer + pSec->VirtualAddress, 0, secActualSize);
1885 
1886  gWinGuest->RemainingSections--;
1887  }
1888  else if (pSec->Characteristics & IMAGE_SCN_MEM_NOT_PAGED)
1889  {
1890  // The section will be present, so read it now
1892  secActualSize,
1893  gWinGuest->KernelBuffer + pSec->VirtualAddress,
1894  NULL);
1895  if (!INT_SUCCESS(status))
1896  {
1897  ERROR("[ERROR] IntKernVirtMemRead failed for 0x%016llx -> 0x%016llx %s: 0x%08x\n",
1898  gGuest.KernelVa + pSec->VirtualAddress,
1899  gGuest.KernelVa + pSec->VirtualAddress + secActualSize,
1900  pSec->Name, status);
1901 
1902  return status;
1903  }
1904 
1905  gWinGuest->RemainingSections--;
1906  }
1907  else
1908  {
1909  DWORD retSize = 0;
1910 
1911  // Use the swap mechanism only if we can't directly read the memory; this avoids unnecessary
1912  // recursive function calls.
1914  secActualSize,
1915  gWinGuest->KernelBuffer + pSec->VirtualAddress,
1916  &retSize);
1917  if (!INT_SUCCESS(status))
1918  {
1919  PWIN_INIT_SWAP pSwp = NULL;
1920  void *swapHandle = NULL;
1921 
1922  pSwp = HpAllocWithTag(sizeof(*pSwp), IC_TAG_WSWP);
1923  if (NULL == pSwp)
1924  {
1926  }
1927 
1928  pSwp->VirtualAddress = pSec->VirtualAddress;
1929  pSwp->Size = secActualSize;
1930 
1931  InsertTailList(&gWinGuest->InitSwapHandles, &pSwp->Link);
1932 
1933  WARNING("Section %d / %d is not in memory, will do a swap mem read\n", i, secCount);
1934 
1935  status = IntSwapMemReadData(0,
1936  gGuest.KernelVa + pSec->VirtualAddress,
1937  secActualSize,
1939  pSwp,
1940  0,
1942  NULL,
1943  &swapHandle);
1944  if (!INT_SUCCESS(status))
1945  {
1946  ERROR("[ERROR] IntSwapMemReadData failed: 0x%08x\n", status);
1947  return status;
1948  }
1949 
1950  // The callback will be called async, save the handle in case an uninit will come
1951  if (NULL != swapHandle)
1952  {
1953  pSwp->SwapHandle = swapHandle;
1954  }
1955  }
1956  else
1957  {
1958  if (retSize != secActualSize)
1959  {
1960  ERROR("We requested %08x bytes, but got %08x!\n", secActualSize, retSize);
1962  }
1963 
1964  gWinGuest->RemainingSections--;
1965  }
1966  }
1967  }
1968 
1969  // We managed to read everything here, so continue the initialization
1970  if (0 == gWinGuest->RemainingSections)
1971  {
1972  TRACE("[WINGUEST STATIC] All sections were present in memory!\n");
1973 
1974  status = IntWinGuestFinishInit();
1975  if (!INT_SUCCESS(status))
1976  {
1977  ERROR("[ERROR] IntWinGuestFinishInit failed: 0x%08x\n", status);
1978  }
1979  }
1980 
1981  return status;
1982 }
1983 
1984 
1985 static INTSTATUS
1987  _Inout_ WIN_INIT_SWAP *Context,
1988  _In_ QWORD Cr3,
1989  _In_ QWORD VirtualAddress,
1990  _In_ QWORD PhysicalAddress,
1991  _In_reads_bytes_(DataSize) void *Data,
1992  _In_ DWORD DataSize,
1993  _In_ DWORD Flags
1994  )
2012 {
2013  INTSTATUS status;
2014  PWIN_INIT_SWAP pSwp;
2015 
2017  UNREFERENCED_PARAMETER(VirtualAddress);
2018  UNREFERENCED_PARAMETER(PhysicalAddress);
2019  UNREFERENCED_PARAMETER(Flags);
2020  UNREFERENCED_PARAMETER(DataSize);
2021 
2022  IntPauseVcpus();
2023 
2024  // Remove the context. The caller knows this may happen & won't use it after IntSwapMemReadData
2025  pSwp = Context;
2026  RemoveEntryList(&pSwp->Link);
2028 
2029  status = IntWinGuestReadKernel(Data);
2030  if (!INT_SUCCESS(status))
2031  {
2032  ERROR("[ERROR] IntWinGuestReadKernel failed: 0x%08x\n", status);
2033 
2034  // We don't care about specifics, but the introspection MUST be unloaded
2036  }
2037 
2038  IntResumeVcpus();
2039 
2040  return status;
2041 }
2042 
2043 
2044 static INTSTATUS
2046  _In_ QWORD KernelGva,
2047  _In_ BOOLEAN Guest64,
2048  _In_ BOOLEAN IsKptiInstalled,
2049  _Out_ DWORD *NtBuildNumber
2050  )
2068 {
2069  INTSTATUS status;
2070  DWORD limit, cb;
2071  DWORD smallestBuild, biggestBuild;
2072  QWORD gva, cr3;
2073  PDWORD pPage;
2074  BOOLEAN found;
2075  PDWORD pNtList;
2076 
2077  limit = 0;
2078  gva = KernelGva & PAGE_MASK;
2079  pNtList = NULL;
2080  pPage = NULL;
2081  found = FALSE;
2082  cb = 0;
2083 
2084  status = IntCamiGetWinSupportedList(IsKptiInstalled, Guest64, pNtList, &cb);
2085  if (!INT_SUCCESS(status) || 0 == cb)
2086  {
2087  ERROR("[ERROR] IntCamiGetWinSupportedList failed: 0x%08x\n", status);
2088  return status;
2089  }
2090 
2091  TRACE("[INFO] %d supported os versions from cami\n", cb);
2092 
2093  pNtList = HpAllocWithTag(sizeof(*pNtList) * cb, IC_TAG_CAMI);
2094  if (NULL == pNtList)
2095  {
2097  }
2098 
2099  status = IntCamiGetWinSupportedList(IsKptiInstalled, Guest64, pNtList, &cb);
2100  if (!INT_SUCCESS(status) || 0 == cb)
2101  {
2102  ERROR("[ERROR] IntCamiGetWinSupportedList failed: 0x%08x\n", status);
2103  goto cleanup_and_exit;
2104  }
2105 
2106  // pNtList is sorted.
2107  smallestBuild = pNtList[0];
2108  biggestBuild = pNtList[cb - 1];
2109 
2110  cr3 = gGuest.Mm.SystemCr3;
2111 
2112  found = FALSE;
2113  while (limit < KERNEL_SEARCH_LIMIT)
2114  {
2115  status = IntVirtMemMap(gva, PAGE_SIZE, cr3, 0, &pPage);
2116  if (!INT_SUCCESS(status))
2117  {
2118  pPage = NULL;
2119  goto _next_page;
2120  }
2121 
2122  for (DWORD i = 0; i < PAGE_SIZE / sizeof(DWORD); i++)
2123  {
2124  DWORD val = pPage[i];
2125 
2126  if ((val & 0xf0000000) != 0xf0000000 ||
2127  (val & 0xf000ffff) != val ||
2128  (val & 0xffff) > biggestBuild ||
2129  (val & 0xffff) < smallestBuild)
2130  {
2131  continue;
2132  }
2133 
2134  for (DWORD j = 0; j < cb; j++)
2135  {
2136  if (pNtList[j] == (val & 0xFFFF))
2137  {
2138  TRACE("[WINGUEST STATIC] Found an NtBuildNumber 0x%08x (%d) @ 0x%016llx\n",
2139  val, val & 0xffff, gva + i * sizeof(DWORD));
2140 
2141  *NtBuildNumber = pNtList[j];
2142  found = TRUE;
2143  goto cleanup_and_exit;
2144  }
2145  }
2146  }
2147 
2148 _next_page:
2149  if (NULL != pPage)
2150  {
2151  IntVirtMemUnmap(&pPage);
2152  }
2153 
2154  if (found)
2155  {
2156  break;
2157  }
2158 
2159  gva += PAGE_SIZE;
2160  limit += PAGE_SIZE;
2161  }
2162 
2163 cleanup_and_exit:
2164  if (NULL != pPage)
2165  {
2166  IntVirtMemUnmap(&pPage);
2167  }
2168 
2169  if (NULL != pNtList)
2170  {
2171  HpFreeAndNullWithTag(&pNtList, IC_TAG_CAMI);
2172  }
2173 
2174  if (!found)
2175  {
2176  return INT_STATUS_NOT_FOUND;
2177  }
2178 
2179  return INT_STATUS_SUCCESS;
2180 }
2181 
2182 
2183 static INTSTATUS
2185  _In_ QWORD KernelBase,
2186  _In_ QWORD KernelGva,
2187  _In_ BYTE *KernelHeaders
2188  )
2212 {
2213  IMAGE_SECTION_HEADER *pSec = NULL;
2214  const DWORD maxSecCount = 10;
2215  DWORD sectionCount = 0;
2216  INTSTATUS status;
2217  BOOLEAN found = FALSE;
2218  void *mappedSecPage = NULL;
2219 
2220  pSec = HpAllocWithTag(sizeof(*pSec) * maxSecCount, IC_TAG_IMGE);
2221  if (NULL == pSec)
2222  {
2224  }
2225 
2226  // Check if the current GVA is inside any section, as on 20h1 we have some mapped MZPEs inside kernel.
2227  status = IntPeGetSectionHeaderByRva(KernelBase, KernelHeaders, (DWORD)(KernelGva - KernelBase), &pSec[0]);
2228  if (INT_SUCCESS(status))
2229  {
2230  if ((pSec[0].Characteristics & IMAGE_SCN_MEM_DISCARDABLE) ||
2231  (pSec[0].Characteristics & IMAGE_SCN_MEM_EXECUTE) == 0 ||
2232  (pSec[0].Characteristics & IMAGE_SCN_MEM_WRITE))
2233  {
2234  WARNING("[WARNING] Gva 0x%016llx is not in a good section of 0x%016llx, discardable: %d, "
2235  "execute: %d, writable: %d",
2236  KernelGva, KernelBase, !!(pSec[0].Characteristics & IMAGE_SCN_MEM_DISCARDABLE),
2237  !!(pSec[0].Characteristics & IMAGE_SCN_MEM_EXECUTE),
2238  !!(pSec[0].Characteristics & IMAGE_SCN_MEM_WRITE));
2240  goto cleanup_and_exit;
2241  }
2242  }
2243 
2244  // The GVA is inside a good section, now verify if we can find PsNtosImageBase in ALMOSTRO or .data.
2245  status = IntPeGetSectionHeadersByName(KernelBase, KernelHeaders, "ALMOSTRO", maxSecCount - 1, gGuest.Mm.SystemCr3,
2246  pSec, &sectionCount);
2247  if (!INT_SUCCESS(status) || (0 == sectionCount))
2248  {
2249  WARNING("[WARNING] IntPeGetSectionHeadersByName failed for `ALMOSTRO`: 0x%08x, secCount = %d\n", status, sectionCount);
2250  // Overwrite the status, as ALMOSTRO should always be available, we will return INT_STATUS_INVALID_DATA_STATE;
2252  goto cleanup_and_exit;
2253  }
2254 
2255  status = IntPeGetSectionHeaderByName(KernelBase, KernelHeaders, ".data", gGuest.Mm.SystemCr3, &pSec[sectionCount]);
2256  if (!INT_SUCCESS(status))
2257  {
2258  WARNING("[WARNING] Presumed base 0x%016llx has no .data section!\n", KernelBase);
2260  goto cleanup_and_exit;
2261  }
2262 
2263  sectionCount++;
2264 
2265  for (DWORD iSec = 0; iSec < sectionCount; iSec++)
2266  {
2267  if ((pSec[iSec].VirtualAddress & PAGE_OFFSET) != 0)
2268  {
2269  WARNING("[WARNING] Base 0x%016llx has section start 0x%08x which is not page aligned!\n",
2270  KernelBase, pSec[iSec].VirtualAddress);
2272  goto cleanup_and_exit;
2273  }
2274 
2275  if (pSec[iSec].Misc.VirtualSize > 2 * ONE_MEGABYTE)
2276  {
2277  WARNING("[WARNING] Section %d has size too big: 0x%08x\n", iSec, pSec[iSec].Misc.VirtualSize);
2279  goto cleanup_and_exit;
2280  }
2281 
2282  for (QWORD page = KernelBase + pSec[iSec].VirtualAddress;
2283  page < KernelBase + pSec[iSec].VirtualAddress + pSec[iSec].Misc.VirtualSize;
2284  page += PAGE_SIZE)
2285  {
2286  DWORD sizeToMap = PAGE_SIZE;
2287 
2288  if (page == ((KernelBase + pSec[iSec].VirtualAddress + pSec[iSec].Misc.VirtualSize) & PAGE_MASK))
2289  {
2290  sizeToMap = ALIGN_DOWN(PAGE_REMAINING(pSec[iSec].Misc.VirtualSize), gGuest.WordSize);
2291  }
2292 
2293  status = IntVirtMemMap(page, sizeToMap, gGuest.Mm.SystemCr3, 0, &mappedSecPage);
2294  if (!INT_SUCCESS(status))
2295  {
2296  WARNING("[WARNING] IntVirtMemMap failed for 0x%016llx: 0x%08x. .data seems paged "
2297  "out for 0x%016llx!\n", page, status, KernelBase);
2298  goto cleanup_and_exit;
2299  }
2300 
2301  for (DWORD current = 0; current < sizeToMap; current += gGuest.WordSize)
2302  {
2303  QWORD currentptr = gGuest.Guest64 ? *(QWORD *)((size_t)mappedSecPage + current) :
2304  *(DWORD *)((size_t)mappedSecPage + current);
2305 
2306  if (currentptr == KernelBase)
2307  {
2308  TRACE("[INFO] Found PsNtosImageBase = 0x%016llx at address 0x%016llx!\n",
2309  KernelBase, page + current);
2310  found = TRUE;
2311  goto cleanup_and_exit;
2312  }
2313  }
2314 
2315  IntVirtMemUnmap(&mappedSecPage);
2316  }
2317  }
2318 
2319 cleanup_and_exit:
2320  if (NULL != mappedSecPage)
2321  {
2322  IntVirtMemUnmap(&mappedSecPage);
2323  }
2324 
2325  if (NULL != pSec)
2326  {
2328  }
2329 
2330  if (!INT_SUCCESS(status))
2331  {
2332  return status;
2333  }
2334 
2335  if (!found)
2336  {
2337  WARNING("[WARNING] PsNtosImageBase not found for 0x%016llx!\n", KernelBase);
2338  return INT_STATUS_NOT_FOUND;
2339  }
2340 
2341  return INT_STATUS_SUCCESS;
2342 }
2343 
2344 
2345 static INTSTATUS
2347  _In_ QWORD KernelGva,
2348  _Out_ QWORD *KernelBase
2349  )
2363 {
2364  INTSTATUS status;
2365  PBYTE hostPage;
2366  QWORD kernelBase, limit;
2367  BOOLEAN found;
2368 
2369  hostPage = NULL;
2370  kernelBase = KernelGva & PAGE_MASK;
2371  limit = 0;
2372  found = FALSE;
2373 
2374  // Take a walk from page 2 page, until we find the DOS headers or a non-present page (which we
2375  // assume it's the MZPE headers, since the KernelGva points inside .text)
2376  while (limit < KERNEL_SEARCH_LIMIT)
2377  {
2378  status = IntVirtMemMap(kernelBase, PAGE_SIZE, gGuest.Mm.SystemCr3, 0, &hostPage);
2379  if (!INT_SUCCESS(status))
2380  {
2381  hostPage = NULL;
2382  goto _next_page;
2383  }
2384 
2385  if ((hostPage[0] == 'M' && hostPage[1] == 'Z'))
2386  {
2387  status = IntWinGuestValidateKernel(kernelBase, KernelGva, hostPage);
2388  if (!INT_SUCCESS(status))
2389  {
2390  WARNING("[WARNING] IntWinGuestValidateKernel failed: 0x%08x\n", status);
2391  }
2392  else
2393  {
2394  found = TRUE;
2395  break;
2396  }
2397  }
2398 
2399  IntVirtMemUnmap(&hostPage);
2400 
2401 _next_page:
2402  kernelBase -= PAGE_SIZE;
2403  limit += PAGE_SIZE;
2404  }
2405 
2406  if (NULL != hostPage)
2407  {
2408  IntVirtMemUnmap(&hostPage);
2409  }
2410 
2411  if (!found && (limit == KERNEL_SEARCH_LIMIT))
2412  {
2413  ERROR("[ERROR] Could not find the kernel headers in the first %lldMB!\n",
2415  return INT_STATUS_NOT_FOUND;
2416  }
2417 
2418  *KernelBase = kernelBase;
2419 
2420  return INT_STATUS_SUCCESS;
2421 }
2422 
2423 
2424 static DWORD
2426  _In_ DWORD CpuCount
2427  )
2440 {
2441  DWORD activeCount = CpuCount;
2442 
2443  // For each CPU reported by the integrator, try to read and validate CR3.
2444  for (DWORD i = 0; i < CpuCount; i++)
2445  {
2446  INTSTATUS status;
2447  QWORD cr3, cr0;
2448 
2449  status = IntCr3Read(i, &cr3);
2450  if (!INT_SUCCESS(status))
2451  {
2452  cr3 = 0;
2453  }
2454 
2455  status = IntCr0Read(i, &cr0);
2456  if (!INT_SUCCESS(status))
2457  {
2458  cr0 = 0;
2459  }
2460 
2461  if (0 == cr3 || 0 == (cr0 & CR0_PG))
2462  {
2463  activeCount--;
2465  }
2466  }
2467 
2468  if (activeCount != CpuCount)
2469  {
2470  WARNING("[WARNING] The active cpu count (%d) is different than the actual cpu count (%d)\n",
2471  activeCount, CpuCount);
2472  }
2473 
2474  TRACE("[INTRO-INIT] Active CPU Count: %d\n", activeCount);
2475 
2476  return activeCount;
2477 }
2478 
2479 
2480 static INTSTATUS
2482  _In_ QWORD Syscall
2483  )
2503 {
2504  INTSTATUS status;
2505  QWORD kernelCr3OffsetPcr;
2506  QWORD kpcr;
2507  PBYTE pPage;
2508  BOOLEAN bFound;
2509  DWORD csType, mapSize;
2510  INSTRUX offsetInstrux;
2511 
2512  bFound = FALSE;
2513  pPage = NULL;
2515  kernelCr3OffsetPcr = kpcr = 0;
2516  mapSize = PAGE_SIZE;
2517 
2518  //
2519  // We map 4K starting from the syscall
2520  //
2521  status = IntVirtMemMap(Syscall, mapSize, 0, 0, &pPage);
2522  if (!INT_SUCCESS(status))
2523  {
2524  //
2525  // Map the remaining of the page Syscall is in. It SHOULD contain the instructions we're looking for.
2526  //
2527  mapSize -= (Syscall & PAGE_OFFSET);
2528  status = IntVirtMemMap(Syscall, mapSize, 0, 0, &pPage);
2529  if (!INT_SUCCESS(status))
2530  {
2531  ERROR("[ERROR] Failed to map Syscall page: 0x%08x\n", status);
2532  goto cleanup_and_exit;
2533  }
2534  }
2535 
2536  //
2537  // We will search for two instructions here:
2538  // 1. mov regx, q/dword ptr gs/fs:[KernelCr3OffsetInPcr]
2539  // 2. mov cr3, regx
2540  //
2541  for (DWORD i = 0; i < mapSize;)
2542  {
2543  INSTRUX instrux;
2544 
2545  CHAR nd[ND_MIN_BUF_SIZE] = { 0 };
2546 
2547  status = IntDecDecodeInstructionFromBuffer(pPage + i, mapSize - i, csType, &instrux);
2548  if (!INT_SUCCESS(status))
2549  {
2550  i++;
2551  continue;
2552  }
2553 
2554 #ifdef DEBUG
2555  NdToText(&instrux, 0, sizeof(nd), nd);
2556 #endif // DEBUG
2557 
2558  //
2559  // If we haven't found the mov regx, gs/fs:[KernelCr3OffsetInPcr] instr
2560  // or we found an instruction that uses the same register as destination
2561  //
2562  if (ND_INS_MOV == instrux.Instruction && 2 == instrux.ExpOperandsCount &&
2563  ND_OP_REG == instrux.Operands[0].Type &&
2564  ND_REG_GPR == instrux.Operands[0].Info.Register.Type)
2565  {
2566  if (!bFound ||
2567  (instrux.Operands[0].Info.Register.Reg == offsetInstrux.Operands[0].Info.Register.Reg))
2568  {
2569  //
2570  // mov regx, gs/fs:[KernelCr3OffsetInPcr] instr
2571  //
2572  if (ND_OP_MEM == instrux.Operands[1].Type && instrux.Operands[1].Info.Memory.HasSeg &&
2573  instrux.Operands[1].Info.Memory.HasDisp &&
2574  ((gGuest.Guest64 && NDR_GS == instrux.Operands[1].Info.Memory.Seg) ||
2575  (!gGuest.Guest64 && NDR_FS == instrux.Operands[1].Info.Memory.Seg)))
2576  {
2577  memcpy(&offsetInstrux, &instrux, sizeof(instrux));
2578  bFound = TRUE;
2579 
2580  TRACE("[INFO] Found a possible offset instruction: %s\n", nd);
2581  }
2582  //
2583  // This instruction overwrites the register that holds the gs/fs:[something]
2584  // so the last valid instruction we found is no longer valid
2585  //
2586  else
2587  {
2588  memzero(&offsetInstrux, sizeof(offsetInstrux));
2589  bFound = FALSE;
2590  }
2591  }
2592  }
2593  //
2594  // We found the first valid instruction, now we search for a mov cr3, regx
2595  //
2596  else if (bFound && ND_INS_MOV_CR == instrux.Instruction && 2 == instrux.ExpOperandsCount &&
2597  ND_OP_REG == instrux.Operands[0].Type && ND_OP_REG == instrux.Operands[1].Type &&
2598  ND_REG_CR == instrux.Operands[0].Info.Register.Type &&
2599  NDR_CR3 == instrux.Operands[0].Info.Register.Reg &&
2600  offsetInstrux.Operands[0].Info.Register.Reg == instrux.Operands[1].Info.Register.Reg)
2601  {
2602  kernelCr3OffsetPcr = offsetInstrux.Operands[1].Info.Memory.Disp;
2603 
2604  TRACE("[INFO] Found a valid second instruction: %s\n", nd);
2605  TRACE("[INFO] We will use the last possible offset instruction!\n");
2606 
2607  break;
2608  }
2609 
2610  i += instrux.Length;
2611  }
2612 
2613  if (0 == kernelCr3OffsetPcr)
2614  {
2615  ERROR("[ERROR] Could not find a valid instruction to get kernel cr3!");
2616  status = INT_STATUS_NOT_FOUND;
2617  goto cleanup_and_exit;
2618  }
2619 
2620  TRACE("[INTRO-INIT] Found KernelDirectoryTableBase offset in PCR at %llx\n", kernelCr3OffsetPcr);
2621 
2622  status = IntFindKernelPcr(IG_CURRENT_VCPU, &kpcr);
2623  if (!INT_SUCCESS(status))
2624  {
2625  ERROR("[ERROR] IntFindKernelPcr failed: 0x%08x\n", status);
2626  goto cleanup_and_exit;
2627  }
2628 
2629  TRACE("[INTRO-INIT] Found PCR at 0x%016llx\n", kpcr);
2630 
2631  status = IntKernVirtMemRead(kpcr + kernelCr3OffsetPcr, gGuest.WordSize, &gGuest.Mm.SystemCr3, NULL);
2632  if (!INT_SUCCESS(status))
2633  {
2634  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x\n", status);
2635  goto cleanup_and_exit;
2636  }
2637 
2638 cleanup_and_exit:
2639 
2640  if (NULL != pPage)
2641  {
2642  IntVirtMemUnmap(&pPage);
2643  }
2644 
2645  return status;
2646 }
2647 
2648 
2649 INTSTATUS
2651  void
2652  )
2662 {
2663  INTSTATUS status;
2664  QWORD pcr, idleThread, idleProcess;
2665  PBYTE pMap;
2666  DWORD mapSize;
2667  BOOLEAN bFound;
2668 
2669  pcr = idleThread = idleProcess = 0;
2670  bFound = FALSE;
2671  pMap = NULL;
2673 
2674  status = IntFindKernelPcr(IG_CURRENT_VCPU, &pcr);
2675  if (!INT_SUCCESS(status))
2676  {
2677  ERROR("[ERROR] IntFindKernelPcr failed: 0x%08x\n", status);
2678  return status;
2679  }
2680 
2681  TRACE("[INFO] KPCR [%d] @ 0x%016llx\n", gVcpu->Index, pcr);
2682 
2683  // Read the idle thread
2685  gGuest.WordSize,
2686  &idleThread,
2687  NULL);
2688  if (!INT_SUCCESS(status))
2689  {
2690  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x\n", status);
2691  return status;
2692  }
2693 
2694  TRACE("[INFO] Idle thread [%d] @ 0x%016llx\n", gVcpu->Index, idleThread);
2695 
2696  status = IntVirtMemMap(idleThread + PROCESS_SEARCH_LIMIT_THREAD_LOWER, mapSize, gGuest.Mm.SystemCr3, 0, &pMap);
2697  if (!INT_SUCCESS(status))
2698  {
2699  ERROR("[ERROR] IntVirtMemMap failed: 0x%08x\n", status);
2700  pMap = NULL;
2701  goto cleanup_and_exit;
2702  }
2703 
2704  // Iterate through the search limit in order to find the idle process.
2706  procOffset >= 0;
2707  procOffset -= gGuest.WordSize)
2708  {
2709  QWORD supposedCr3 = 0;
2710  QWORD aux = 0;
2711 
2712  idleProcess = gGuest.Guest64 ? *(PQWORD)(pMap + procOffset) : *(PDWORD)(pMap + procOffset);
2713  idleProcess = FIX_GUEST_POINTER(gGuest.Guest64, idleProcess);
2714 
2715  // The idle process isn't in the process list and is inside the kernel, not dynamically allocated.
2716  if (!IS_KERNEL_POINTER_WIN(gGuest.Guest64, idleProcess) ||
2717  (idleProcess < gGuest.KernelVa) || (idleProcess > (gGuest.KernelVa + gGuest.KernelSize)))
2718  {
2719  continue;
2720  }
2721 
2722  TRACE("[INFO] Found a potentially valid idle process at offset 0x%08x -> 0x%016llx\n",
2723  procOffset + PROCESS_SEARCH_LIMIT_THREAD_LOWER, idleProcess);
2724 
2725  // The way we'll do the check is we read the supposed cr3,
2726  // translate it's address with it, and see if it matches.
2727  // THEORETICALLY, it will only work with the cr3, it shouldn't work with junk.
2728 
2729  status = IntKernVirtMemRead(idleProcess + CR3_OFFSET_IN_KPROCESS, gGuest.WordSize, &supposedCr3, NULL);
2730  if (!INT_SUCCESS(status) || (0 == supposedCr3))
2731  {
2732  continue;
2733  }
2734 
2735  status = IntVirtMemRead(idleProcess + CR3_OFFSET_IN_KPROCESS, gGuest.WordSize, supposedCr3, &aux, NULL);
2736  if (!INT_SUCCESS(status) || (aux != supposedCr3))
2737  {
2738  continue;
2739  }
2740 
2741  TRACE("[INFO] Found a valid idle process cr3 at offset 0x%08x @ 0x%016llx -> 0x%016llx\n",
2742  CR3_OFFSET_IN_KPROCESS, idleProcess + CR3_OFFSET_IN_KPROCESS, supposedCr3);
2743 
2744  bFound = TRUE;
2745  gGuest.Mm.SystemCr3 = supposedCr3;
2746  break;
2747  }
2748 
2749 cleanup_and_exit:
2750  if (NULL != pMap)
2751  {
2752  IntVirtMemUnmap(&pMap);
2753  }
2754 
2755  return bFound ? INT_STATUS_SUCCESS : INT_STATUS_NOT_FOUND;
2756 }
2757 
2758 
2759 INTSTATUS
2761  void
2762  )
2777 {
2778  INTSTATUS status;
2779  DWORD activeCpuCount;
2780 
2781  WIN_INIT_SWAP *pSwp = NULL;
2782  void *swapHandle = NULL;
2783  QWORD msrValue = 0;
2784  QWORD idtValue = 0;
2785  QWORD kernelBase = 0;
2786  QWORD kernelBaseIdt = 0;
2787  DWORD ntBuildNumber = 0;
2788 
2789  // Uninitialize some things which may have been left here from the previous retry
2790  if (gWinGuest)
2791  {
2793 
2794  memzero(gWinGuest, sizeof(*gWinGuest));
2795  }
2796 
2797  gWinGuest = &gGuest._WindowsGuest;
2798 
2799  // Init this early because if anything fails here, we'll end up calling IntWinGuestCancelKernelRead
2800  // which will iterate the list, causing intro to crash if it's uninitialized.
2801  InitializeListHead(&gWinGuest->InitSwapHandles);
2802 
2803  // Initialize the #VE state. We do this here because only Windows guests are supported for #VE.
2804  status = IntVeInit();
2805  if (!INT_SUCCESS(status))
2806  {
2807  ERROR("[ERROR] IntVeInit failed: 0x%08x; will continue, but will not use #VE.\n", status);
2808  }
2809  else
2810  {
2811  TRACE("[INTRO-INIT] #VE initialized successfully!\n");
2812  }
2813 
2814  if (gGuest.Guest64)
2815  {
2816  QWORD gsBase = 0;
2817 
2818  status = IntGsRead(IG_CURRENT_VCPU, &gsBase);
2819  if (!INT_SUCCESS(status))
2820  {
2821  ERROR("[ERROR] IntGsRead failed: 0x%08x\n", status);
2822  return status;
2823  }
2824 
2825  TRACE("[INTRO-INIT] IA32_GS_BASE_MSR = 0x%016llx \n", gsBase);
2826  }
2827  else
2828  {
2829  QWORD fsBase = 0;
2830 
2831  status = IntFsRead(IG_CURRENT_VCPU, &fsBase);
2832  if (!INT_SUCCESS(status))
2833  {
2834  ERROR("[ERROR] IntFsRead failed: 0x%08x\n", status);
2835  return status;
2836  }
2837 
2838  TRACE("[INTRO-INIT] IA32_FS_BASE_MSR = 0x%016llx \n", fsBase);
2839  }
2840 
2841  activeCpuCount = IntWinGetActiveCpuCount(gGuest.CpuCount);
2842 
2843  if (gGuest.Guest64)
2844  {
2845  status = IntSyscallRead(IG_CURRENT_VCPU, NULL, &msrValue);
2846  }
2847  else
2848  {
2849  status = IntSysenterRead(IG_CURRENT_VCPU, NULL, &msrValue, NULL);
2850  }
2851 
2852  if (!INT_SUCCESS(status))
2853  {
2854  ERROR("[ERROR] Failed reading the syscall msr: 0x%08x\n", status);
2855  return status;
2856  }
2857  else if (!IS_KERNEL_POINTER_WIN(gGuest.Guest64, msrValue))
2858  {
2859  ERROR("[ERROR] SYSCALL MSR 0x%016llx is not valid!\n", msrValue);
2860 
2861  // We can actually try again, no need to say it's not supported
2863  }
2864 
2865  TRACE("[INTRO-INIT] Found SYSCALL handler @ 0x%016llx\n", msrValue);
2866 
2868  if (!INT_SUCCESS(status))
2869  {
2870  ERROR("[ERROR] IntCr3Read failed: 0x%08x\n", status);
2871  return status;
2872  }
2873 
2874  if (gGuest.KptiActive)
2875  {
2876  // With KPTI, we might get here using any cr3 so read the one from PCR
2877  status = IntWinGuestFindKernelCr3(msrValue);
2878  if (!INT_SUCCESS(status))
2879  {
2880  ERROR("[ERROR] IntWinGuestFindKernelCr3 failed: 0x%08x\n", status);
2881 
2883 
2884  // There is no point in retrying the init, as this will always fail if it managed to fail once.
2886 
2887  return status;
2888  }
2889  }
2890 
2891  TRACE("[INTRO-INIT] Found a valid cr3 at: 0x%016llx\n", gGuest.Mm.SystemCr3);
2892 
2893  // We have the IDT and cr3, now get the #PF interrupt handler
2894  status = IntIdtGetEntry(IG_CURRENT_VCPU, VECTOR_PF, &idtValue);
2895  if (!INT_SUCCESS(status))
2896  {
2897  ERROR("[ERROR] IntIdtGetEntry failed: %08x\n", status);
2898  return status;
2899  }
2900 
2901  if (IS_KERNEL_POINTER_WIN(gGuest.Guest64, idtValue))
2902  {
2903  TRACE("[INTRO-INIT] Found first interrupt handler @ 0x%016llx\n", idtValue);
2904  }
2905  else
2906  {
2907  WARNING("[WARNING] First interrupt handler @ 0x%016llx is not valid\n", idtValue);
2908  }
2909 
2910  // Find the kernel. We must have a kernel base from the msr value, and that must be valid, or
2911  // else the agent injection won't work
2912  status = IntWinGuestFindKernel(msrValue, &kernelBase);
2913  if (!INT_SUCCESS(status))
2914  {
2915  ERROR("[ERROR] IntWinGuestFindKernel failed: %08X\n", status);
2916  kernelBase = 0;
2917  }
2918  else
2919  {
2920  LOG("[INTRO-INIT] Found the base of the ntoskrnl.exe [SYSCALL] @ VA 0x%016llx\n", kernelBase);
2921  }
2922 
2923  // Find the kernel using the first interrupt handler as well.
2924  if (IS_KERNEL_POINTER_WIN(gGuest.Guest64, idtValue))
2925  {
2926  status = IntWinGuestFindKernel(idtValue, &kernelBaseIdt);
2927  if (!INT_SUCCESS(status))
2928  {
2929  ERROR("[ERROR] IntWinGuestFindKernel failed: 0x%08X\n", status);
2930 
2932 
2934 
2935  return status;
2936  }
2937 
2938  LOG("[INTRO-INIT] Found the base of the ntoskrnl.exe [IDT] @ VA 0x%016llx\n", kernelBaseIdt);
2939  }
2940 
2941  if (kernelBaseIdt && kernelBase != kernelBaseIdt)
2942  {
2943  WARNING("[WARNING] SYSCALL & IDT handlers point in different drivers (0x%016llx vs 0x%016llx).\n",
2944  kernelBase, kernelBaseIdt);
2945 
2946  kernelBase = kernelBaseIdt;
2947  }
2948 
2949  // Find the NtBuildNumber inside the kernel
2950  status = IntWinGuestFindBuildNumber(kernelBase, gGuest.Guest64, gGuest.KptiInstalled, &ntBuildNumber);
2951  if (!INT_SUCCESS(status))
2952  {
2953  ERROR("[ERROR] IntWinGuestFindBuildNumber failed: 0x%08X\n", status);
2954 
2956 
2958 
2959  return status;
2960  }
2961 
2962  gGuest.ActiveCpuCount = activeCpuCount;
2964  gGuest.KernelVa = kernelBase;
2965 
2966  // Set this temporarily
2967  gGuest.OSVersion = ntBuildNumber & 0xffff;
2968 
2969  // Set this temporarily until we find the real size
2971 
2972  // We need these early, for agent injection (BP page faults)
2973  gWinGuest->SyscallAddress = msrValue;
2974 
2975  IntWinAgentInit();
2976 
2977  // We have a kernel cr3 and the OS version so we can query the PCR
2978  // and find the idle process cr3.
2979  // We can place hooks on this one since it SHOULDN'T terminate
2980  status = IntWinGuestFindIdleCr3();
2981  if (!INT_SUCCESS(status))
2982  {
2983  ERROR("[ERROR] IntWinGuestFindIdleCr3 failed: 0x%08x\n", status);
2984 
2986 
2987  // There is no point in retrying the init, as this will always fail if it managed to fail once.
2989 
2990  return status;
2991  }
2992 
2993  TRACE("[INTRO-INIT] Found idle process CR3: 0x%016llx\n", gGuest.Mm.SystemCr3);
2994 
2995  // We only search for this here since we don't really need it before...
2996  status = IntWinGuestFindSelfMapIndex();
2997  if (!INT_SUCCESS(status))
2998  {
2999  ERROR("[ERROR] IntWinGuestFindSelfMapIndex failed: 0x%08x\n", status);
3000 
3002 
3003  // There is no point in retrying the init, as this will always fail if it managed to fail once.
3005 
3006  return status;
3007  }
3008 
3009  for (DWORD i = 0; i < gGuest.CpuCount; i++)
3010  {
3012  if (!INT_SUCCESS(status))
3013  {
3014  gGuest.VcpuArray[i].IdtBase = 0;
3015  gGuest.VcpuArray[i].IdtLimit = 0;
3016  }
3017  }
3018 
3019  status = IntWinGuestInit();
3020  if (!INT_SUCCESS(status))
3021  {
3022  ERROR("[ERROR] IntWinGuestInit failed: 0x%08x\n", status);
3023  return status;
3024  }
3025 
3026  // We are done here, now read the headers and go forward
3027  pSwp = HpAllocWithTag(sizeof(*pSwp), IC_TAG_WSWP);
3028  if (NULL == pSwp)
3029  {
3031  }
3032 
3033  pSwp->VirtualAddress = kernelBase;
3034  pSwp->Size = PAGE_SIZE;
3035 
3036  InsertTailList(&gWinGuest->InitSwapHandles, &pSwp->Link);
3037 
3038  status = IntSwapMemReadData(0,
3039  kernelBase,
3040  PAGE_SIZE,
3042  pSwp,
3043  0,
3045  NULL,
3046  &swapHandle);
3047  if (!INT_SUCCESS(status))
3048  {
3049  ERROR("[ERROR] Failed reading the kernel headers: 0x%08x\n", status);
3050  return status;
3051  }
3052 
3053  // The callback will be called async, save the handle in case an uninit will come
3054  if (NULL != swapHandle)
3055  {
3056  pSwp->SwapHandle = swapHandle;
3057  }
3058 
3059  return INT_STATUS_SUCCESS;
3060 }
3061 
3062 
3063 INTSTATUS
3065  _In_ DWORD FullStringSize,
3066  _In_ DWORD VersionStringSize,
3067  _Out_ CHAR *FullString,
3068  _Out_ CHAR *VersionString
3069  )
3082 {
3083  DWORD count;
3084 
3085  if (NULL == gWinGuest->NtBuildLabString)
3086  {
3087  return INT_STATUS_NOT_READY;
3088  }
3089 
3090  if (NULL == gWinGuest->VersionString)
3091  {
3092  return INT_STATUS_NOT_READY;
3093  }
3094 
3095  if (NULL == gWinGuest->ServerVersionString)
3096  {
3097  return INT_STATUS_NOT_READY;
3098  }
3099 
3100  if (winProductTypeNotYetLoaded == gWinGuest->ProductType)
3101  {
3102  return INT_STATUS_NOT_READY;
3103  }
3104 
3105  if (strlen(gWinGuest->NtBuildLabString) >= FullStringSize)
3106  {
3108  }
3109 
3110  strcpy(FullString, gWinGuest->NtBuildLabString);
3111 
3112  if (gWinGuest->ProductType == winProductTypeServer && strlen(gWinGuest->ServerVersionString) != 0)
3113  {
3114  count = snprintf(VersionString, VersionStringSize, "%s %s", gWinGuest->ServerVersionString,
3115  strcasestr(FullString, "lts") ? "ltsb" : "");
3116  }
3117  else
3118  {
3119  const char *appendix = gWinGuest->ProductType == winProductTypeUnknown ? "(could not determine if server)" :
3120  gWinGuest->ProductType == winProductTypeServer ? "(possible server)" : "";
3121 
3122  count = snprintf(VersionString, VersionStringSize, "%s %s %s", gWinGuest->VersionString,
3123  strcasestr(FullString, "lts") ? "ltsb" : "", appendix);
3124  }
3125 
3126  if (count >= VersionStringSize)
3127  {
3129  }
3130 
3131  return INT_STATUS_SUCCESS;
3132 }
Measures kernel mode exceptions checks.
Definition: stats.h:51
#define IMAGE_SCN_MEM_EXECUTE
Definition: winpe.h:472
static INTSTATUS IntWinGuestFetchProductType(WIN_PRODUCT_TYPE *ProductType)
Obtains the Windows product type.
Definition: winguest.c:1364
static INTSTATUS IntWinGuestFindSelfMapIndex(void)
Finds the self map index.
Definition: winguest.c:373
#define INT_STATUS_GUEST_OS_NOT_SUPPORTED
Indicates that the guest operating system is not supported.
Definition: introstatus.h:446
INTSTATUS IntWinAgentUnInit(void)
Uninit the agents state.
Definition: winagent.c:3352
#define THS_CHECK_SWAPGS
Will check if any RIP is inside a mitigated SWAPGS gadget.
static INTSTATUS IntWinGuestFindKernel(QWORD KernelGva, QWORD *KernelBase)
Searches for the base of the Windows kernel image.
Definition: winguest.c:2346
UINT16 NumberOfSections
Definition: winpe.h:65
struct _IMAGE_NT_HEADERS64 * PIMAGE_NT_HEADERS64
Advanced server.
Definition: winguest.h:785
QWORD PhysicalAddress
The physical address to which VirtualAddress translates to.
Definition: introcore.h:107
enum _INTRO_ACTION_REASON INTRO_ACTION_REASON
The reason for which an INTRO_ACTION was taken.
INTRO_CODEBLOCKS CodeBlocks
Code blocks extracted for the alert.
Definition: intro_types.h:1145
static INTSTATUS IntWinGuestHandleKernelSudExec(QWORD Address, INTRO_ACTION *Action)
Handles a kernel mode execution inside SharedUserData.
Definition: winguest.c:808
INTSTATUS IntPeGetSectionHeaderByRva(QWORD ImageBase, BYTE *ImageBaseBuffer, DWORD GuestRva, IMAGE_SECTION_HEADER *SectionHeader)
Given a relative virtual address, return the section header which describes the section the RVA lies ...
Definition: winpe.c:682
#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
#define CONTAINING_RECORD(List, Type, Member)
Definition: introlists.h:36
#define ROUND_UP(what, to)
Definition: introdefs.h:158
void IntGuestSetIntroErrorState(INTRO_ERROR_STATE State, INTRO_ERROR_CONTEXT *Context)
Updates the value of the gErrorState and the value of the gErrorStateContext.
Definition: guests.c:88
#define INTRO_OPT_VE
Enable the Virtualization exception page table access pre-filtering agent (64-bit Windows only)...
Definition: intro_types.h:459
#define IC_TAG_CAMI
Live update allocations.
Definition: memtags.h:122
struct _IMAGE_FILE_HEADER IMAGE_FILE_HEADER
INTSTATUS IntVirtMemUnmap(void **HostPtr)
Unmaps a memory range previously mapped with IntVirtMemMap.
Definition: introcore.c:2234
Success.
Definition: intro_types.h:2272
QWORD MmPfnDatabase
Guest virtual address of the PFN data base.
Definition: winguest.h:823
QWORD IntAlertCoreGetFlags(QWORD ProtectionFlag, INTRO_ACTION_REASON Reason)
Returns the flags for an alert.
Definition: alerts.c:366
#define WIN_SHARED_USER_DATA_PTR
The address where the SharedUserData is mapped in the Windows kernel.
Definition: winguest.c:70
An internal error occurred (no memory, pages not present, etc.).
Definition: intro_types.h:195
BOOLEAN IntPolicyCoreForceBetaIfNeeded(QWORD Flag, INTRO_ACTION *Action)
Checks if a forced action should be taken even if the log-only mode is active.
Definition: introcore.c:2803
INTSTATUS IntWinGuestActivateProtection(void)
Activates the protection for a Windows guest.
Definition: winguest.c:1163
static INTSTATUS IntWinGuestFindKernelObjects(void)
Searches for kernel objects.
Definition: winguest.c:315
uint8_t BYTE
Definition: intro_types.h:47
#define CR0_PG
Definition: processor.h:40
IG_ARCH_REGS Regs
The current state of the guest registers.
Definition: guests.h:95
INTSTATUS IntIdtGetEntry(DWORD CpuNumber, DWORD Entry, QWORD *Handler)
Get the handler of an interrupt from the IDT.
Definition: introcpu.c:145
DWORD Index
The VCPU number.
Definition: guests.h:172
INTSTATUS IntIdtrProtect(void)
Enable IDTR protection.
BOOLEAN Initialized
True if the VCPU is initialized and used by the guest, False if it is not.
Definition: guests.h:199
#define _In_
Definition: intro_sal.h:21
QWORD SyscallAddress
Guest virtual address of the SYSCALL/SYSENTER handler.
Definition: winguest.h:801
MITRE_ID MitreID
The Mitre ID that corresponds to this attack.
Definition: intro_types.h:1088
INTSTATUS IntPeFindKernelExport(const char *Name, QWORD *ExportGva)
Find an export inside the NT kernel image.
Definition: winpe.c:1723
QWORD SystemCr3
The Cr3 used to map the kernel.
Definition: guests.h:207
#define INT_STATUS_SUCCESS
Definition: introstatus.h:54
BOOLEAN IntPolicyCoreTakeAction(QWORD Flag, INTRO_ACTION *Action, INTRO_ACTION_REASON *Reason)
Returns the action that should be taken for a core introspection option.
Definition: introcore.c:2693
INTSTATUS IntFsRead(DWORD CpuNumber, QWORD *FsValue)
Reads the IA32_FS_BASE guest MSR.
Definition: introcpu.c:252
Measures user mode exceptions checks.
Definition: stats.h:50
#define PAGE_REMAINING(addr)
Definition: pgtable.h:163
WIN_KERNEL_DRIVER Win
Valid only for Windows guests.
Definition: drivers.h:70
INTSTATUS IntReadString(QWORD StrGva, DWORD MinimumLength, BOOLEAN AnsiOnly, char **String, DWORD *StringLength)
Reads a string from the guest kernel memory.
Definition: introcore.c:2880
INTSTATUS IntWinHalCreateHalData(void)
Initializes gHalData.
Definition: winhal.c:1258
QWORD ReturnRip
The RIP at which the code that triggered the alert returns.
Definition: intro_types.h:1164
#define STATS_EXIT(id)
Definition: stats.h:148
struct _EVENT_EPT_VIOLATION::@276 Originator
DWORD KernelSize
The size of the kernel.
Definition: guests.h:280
#define KERNEL_SEARCH_LIMIT
The maximum size of the area of memory in which the kernel base is searched for.
Definition: winguest.c:41
INTSTATUS IntWinGuestNew(void)
Starts the initialization and protection process for a new Windows guest.
Definition: winguest.c:2760
User-mode exception.
Definition: exceptions.h:61
WORD IdtLimit
The current IDT limit.
Definition: guests.h:111
INTSTATUS IntSwapMemReadData(QWORD Cr3, QWORD VirtualAddress, DWORD Length, DWORD Options, void *Context, DWORD ContextTag, PFUNC_PagesReadCallback Callback, PFUNC_PreInjectCallback PreInject, void **SwapHandle)
Reads a region of guest virtual memory, and calls the indicated callback when all the data is availab...
Definition: swapmem.c:417
struct _LIST_ENTRY * Flink
Definition: introlists.h:20
#define IC_TAG_WSWP
Win init swap handle.
Definition: memtags.h:108
Information not yet loaded.
Definition: winguest.h:783
#define VECTOR_UD
Definition: processor.h:109
The product type is unknown.
Definition: winguest.h:790
void IntWinHalUninit(void)
Frees any resources held by gHalData and removes all the HAL protections.
Definition: winhal.c:1445
QWORD BaseVa
The guest virtual address of the kernel module that owns this driver object.
Definition: drivers.h:41
#define IMAGE_SCN_MEM_WRITE
Definition: winpe.h:474
#define PROCESS_SEARCH_LIMIT_THREAD_UPPER
The upper limit of the area in which the idle process is searched.
Definition: winguest.c:56
#define INT_SUCCESS(Status)
Definition: introstatus.h:42
QWORD ExFreePoolWithTag
Guest virtual address of the ExFreePoolWithTag kernel function.
Definition: winguest.h:800
INTSTATUS IntWinThrGetCurrentThread(DWORD CpuNumber, QWORD *EthreadAddress)
Get the ETHREAD structure address of the thread currently running on the given CPU.
Definition: winthread.c:26
LIST_ENTRY Link
Link inside the WINDOWS_GUEST.InitSwapHandles list.
Definition: winguest.h:853
A critical structure was not found inside the guest kernel.
Definition: intro_types.h:2278
static INTSTATUS IntWinGuestFindBuildNumber(QWORD KernelGva, BOOLEAN Guest64, BOOLEAN IsKptiInstalled, DWORD *NtBuildNumber)
Finds the NtBuildNumber kernel variable.
Definition: winguest.c:2045
QWORD Flags
A combination of ALERT_FLAG_* values describing the alert.
Definition: intro_types.h:1087
INTSTATUS IntResumeVcpus(void)
Resumes the VCPUs previously paused with IntPauseVcpus.
Definition: introcore.c:2355
void IntWinAgentInit(void)
Initialize the agents state.
Definition: winagent.c:3333
BOOLEAN ProtectionActivated
Definition: guests.h:293
IMAGE_FILE_HEADER FileHeader
Definition: winpe.h:220
#define PAGE_OFFSET
Definition: pgtable.h:32
WIN_PRODUCT_TYPE ProductType
The product type. Obtained directly from the guest during initialization.
Definition: winguest.h:819
int32_t INT32
Definition: intro_types.h:44
INTSTATUS IntWinGuestProtectSudExec(void)
Protects SharedUserData against executions by establishing an EPT hook on it.
Definition: winguest.c:1100
static INTSTATUS IntWinGuestFindKernelCr3(QWORD Syscall)
Searches for the kernel Cr3.
Definition: winguest.c:2481
INFO_UD_PENDING * IntUDGetEntry(const QWORD Cr3, const QWORD Rip, const QWORD Thread)
Get a UD entry for the provided Cr3, Rip and Thread ID.
Definition: udlist.c:150
#define INT_STATUS_NOT_NEEDED_HINT
Definition: introstatus.h:317
#define ERROR(fmt,...)
Definition: glue.h:62
#define ONE_MEGABYTE
Definition: introdefs.h:90
Describes a user-mode originator.
Definition: exceptions.h:933
INTSTATUS IntPeGetSectionHeadersByName(QWORD ImageBase, BYTE *ImageBaseBuffer, PCHAR Name, DWORD NumberOfSectionHeadersAllocated, QWORD Cr3, IMAGE_SECTION_HEADER *SectionHeaders, DWORD *NumberOfSectionHeadersFilled)
Return all the section headers matching the indicated Name.
Definition: winpe.c:917
#define HpAllocWithTag(Len, Tag)
Definition: glue.h:516
#define PHYS_PAGE_MASK
Definition: pgtable.h:38
INTSTATUS IntWinDrvObjUninit(void)
Removes all the driver objects in the gWinDriverObjects.
Definition: windrvobj.c:1374
int INTSTATUS
The status data type.
Definition: introstatus.h:24
QWORD Size
The size of the kernel module that owns this driver object.
Definition: drivers.h:43
The operating system version is not supported.
Definition: intro_types.h:2274
INTSTATUS IntMsrSyscallProtect(void)
Enable protection for all SYSCALL and SYSENTER MSRs.
DWORD OSVersion
Os version.
Definition: guests.h:277
QWORD gEventId
The ID of the current event.
Definition: glue.c:55
#define INT_STATUS_NOT_FOUND
Definition: introstatus.h:284
DWORD NumberOfServices
The number of entries in the SSDT.
Definition: winguest.h:805
#define WIN_SHARED_USER_DATA_OFFSET_PRODUCT
QWORD IntroActiveEventId
The event ID on which introcore became active.
Definition: guests.h:377
QWORD ExAllocatePoolWithTag
Guest virtual address of the ExAllocatePoolWithTag kernel function.
Definition: winguest.h:799
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
PCHAR ServerVersionString
A NULL terminated string containing Windows server version information.
Definition: winguest.h:817
void IntWinGuestUninit(void)
Uninits a Windows guest.
Definition: winguest.c:675
WINDOWS_GUEST _WindowsGuest
Linux specific information. Valid when OSType is introGuestWindows.
Definition: guests.h:416
Describes a kernel-mode originator.
Definition: exceptions.h:897
PVCPU_STATE VcpuArray
Array of the VCPUs assigned to this guest. The index in this array matches the VCPU number...
Definition: guests.h:368
BOOLEAN IntWinGuestIsIncreasedUserVa(void)
Check if the guest has an increased user address space.
INTSTATUS IntPhysicalMemWrite(QWORD PhysicalAddress, DWORD Length, void *Buffer)
Writes data to a guest physical memory range, but only for a single page.
Definition: introcore.c:744
static INTSTATUS IntWinGuestFindSystemCr3(QWORD KernelAddress, QWORD *SystemCr3, QWORD StartPhysical, QWORD EndPhysical)
Searches for the system Cr3 in a range of physical addresses.
Definition: winguest.c:541
#define INT_STATUS_LOAD_ABORTED
Indicates that Introcore loading was aborted.
Definition: introstatus.h:454
static INTSTATUS IntWinGuestResolveImports(void)
Obtains the addresses of public variable and functions exposed by the Windows kernel.
Definition: winguest.c:1246
INTSTATUS IntCr4Unprotect(void)
Disables the CR4 protection.
INTSTATUS IntPauseVcpus(void)
Pauses all the guest VCPUs.
Definition: introcore.c:2320
#define INTRO_OPT_PROT_KM_IDT
Definition: intro_types.h:396
INTSTATUS IntDumpCodeAndRegs(QWORD Gva, QWORD Gpa, IG_ARCH_REGS *Registers)
This function dumps an entire page (textual disassembly and opcodes) as well as the values of the reg...
Definition: dumper.c:622
INTRO_GUEST_TYPE OSType
The type of the guest.
Definition: guests.h:274
void IntAlertFillCpuContext(BOOLEAN CopyInstruction, INTRO_CPUCTX *CpuContext)
Fills the current CPU context for an alert.
Definition: alerts.c:492
INSTRUX Instruction
The current instruction, pointed by the guest RIP.
Definition: guests.h:88
INTSTATUS IntThrSafeCheckThreads(QWORD Options)
Checks if any of the guest threads have their RIP or have any stack pointers pointing to regions of c...
INTSTATUS IntNotifyIntroActive(void)
Definition: glue.c:927
INTSTATUS IntWinDrvCreateFromAddress(QWORD ModuleInfo, QWORD Flags)
Adds a driver to introspection&#39;s LoadedModuleList (gKernelDrivers). This way we avoid lots of mapping...
Definition: windriver.c:289
EVENT_EPT_VIOLATION Ept
Definition: alerts.h:16
static DWORD IntWinGetActiveCpuCount(DWORD CpuCount)
Gets the number of active CPUs used by the guest.
Definition: winguest.c:2425
DWORD TimeDateStamp
The driver`s internal timestamp (from the _IMAGE_FILE_HEADER).
Definition: windriver.h:23
#define LOG(fmt,...)
Definition: glue.h:61
32-bit selector.
Definition: glueiface.h:187
#define WIN_SHARED_USER_DATA_OFFSET_PROD_VALID
uint32_t * PDWORD
Definition: intro_types.h:49
INTSTATUS IntHookGvaSetHook(QWORD Cr3, QWORD Gva, DWORD Length, BYTE Type, void *Callback, void *Context, void *ParentHook, DWORD Flags, HOOK_GVA **GvaHook)
Set a read, write, execute or swap hook on a guest virtual address.
Definition: hook_gva.c:345
void IntAlertFillVersionInfo(INTRO_VIOLATION_HEADER *Header)
Fills version information for an alert.
Definition: alerts.c:327
INTSTATUS IntGdtrProtect(void)
Enable GDTR protection.
INTRO_VIOLATION_HEADER Header
The alert header.
Definition: intro_types.h:1106
BOOLEAN KptiActive
True if KPTI is enabled on this guest, False if it is not.
Definition: guests.h:287
QWORD PsCreateSystemThread
Guest virtual address of the PsCreateSystemThread kernel function.
Definition: winguest.h:798
#define INTRO_OPT_PROT_KM_MSR_SYSCALL
Definition: intro_types.h:411
INTRO_ACTION_REASON Reason
The reason for which Action was taken.
Definition: intro_types.h:1084
INTSTATUS IntAlertFillCodeBlocks(QWORD Rip, QWORD Cr3, BOOLEAN Execute, INTRO_CODEBLOCKS *CodeBlocks)
Fills the code blocks pattern for an alert.
Definition: alerts.c:71
static INTSTATUS IntWinGuestHandleUserSudExec(QWORD Address, INTRO_ACTION *Action)
Handles an user-mode execution inside SharedUserData.
Definition: winguest.c:886
Exposes the functions used to provide Windows Threads related support.
DWORD MappingsCount
The number of entries inside the MappingsTrace and MappingsEntries arrays.
Definition: introcore.h:123
void * Library
The library that&#39;s modifying the memory (if that&#39;s the case).
Definition: exceptions.h:952
A kernel export was not found.
Definition: intro_types.h:2277
The object was detected after it was created.
Definition: winguest.h:148
void IntAlertEptFillFromKmOriginator(const EXCEPTION_KM_ORIGINATOR *Originator, EVENT_EPT_VIOLATION *EptViolation)
Fills kernel mode originator information inside an EPT alert.
Definition: alerts.c:832
GENERIC_ALERT gAlert
Global alert buffer.
Definition: alerts.c:27
#define INT_STATUS_ALIGNMENT_INCONSISTENCY
Definition: introstatus.h:189
#define _Inout_
Definition: intro_sal.h:20
5-level paging
Definition: introcore.h:72
INTSTATUS IntSwapMemRemoveTransaction(void *Transaction)
Remove a transaction.
Definition: swapmem.c:942
DWORD Size
The size of the read.
Definition: winguest.h:857
QWORD Flags
The entry that maps VirtualAddress to PhysicalAddress, together with all the control bits...
Definition: introcore.h:119
INTSTATUS IntKernVirtMemFetchDword(QWORD GuestVirtualAddress, DWORD *Data)
Reads 4 bytes from the guest kernel memory.
Definition: introcore.c:829
#define IDLE_THREAD_OFFSET_PCR
The offset of the IdleThread field inside the _KPCR.
Definition: winguest.c:53
static INTSTATUS IntWinGuestFinishInit(void)
Finalizes the Windows initialization once the entire kernel is read.
Definition: winguest.c:1428
INTSTATUS IntFindKernelPcr(DWORD CpuNumber, QWORD *Pcr)
Finds the address of the Windows kernel _KPCR.
Definition: introcpu.c:1116
#define INT_STATUS_EXCEPTION_BLOCK
Definition: introstatus.h:421
QWORD PsActiveProcessHead
Guest virtual address of the PsActiveProcessHead kernel variable.
Definition: winguest.h:821
#define INT_STATUS_ALREADY_INITIALIZED
Definition: introstatus.h:263
void IntAlertEptFillFromVictimZone(const EXCEPTION_VICTIM_ZONE *Victim, EVENT_EPT_VIOLATION *EptViolation)
Fills the victim information inside an EPT alert.
Definition: alerts.c:868
QWORD PsLoadedModuleList
Guest virtual address of the PsLoadedModuleList kernel variable.
Definition: winguest.h:822
#define INT_STATUS_NOT_INITIALIZED
Definition: introstatus.h:266
INTSTATUS IntSysenterRead(DWORD CpuNumber, QWORD *SysCs, QWORD *SysEip, QWORD *SysEsp)
Queries the IA32_SYSENTER_CS, IA32_SYSENTER_EIP, and IA32_SYSENTER_ESP guest MSRs.
Definition: introcpu.c:571
INTSTATUS IntKernVirtMemFetchQword(QWORD GuestVirtualAddress, QWORD *Data)
Reads 8 bytes from the guest kernel memory.
Definition: introcore.c:811
The kernel image was not found.
Definition: intro_types.h:2275
#define IG_CURRENT_VCPU
For APIs that take a VCPU number as a parameter, this can be used to specify that the current VCPU sh...
Definition: glueiface.h:324
#define CR3_OFFSET_IN_KPROCESS
The offset of the DirectoryTableBase field inside _KPROCESS.
Definition: winguest.c:67
WIN_PRODUCT_TYPE
The type of the Windows OS.
Definition: winguest.h:781
#define STATS_ENTER(id)
Definition: stats.h:141
INTRO_CPUCTX CpuContext
The context of the CPU that triggered the alert.
Definition: intro_types.h:1085
uint8_t * PBYTE
Definition: intro_types.h:47
__noreturn void IntBugCheck(void)
Definition: glue.c:917
INTSTATUS IntNotifyIntroEvent(INTRO_EVENT_TYPE EventClass, void *Param, size_t EventSize)
Notifies the integrator about an introspection alert.
Definition: glue.c:1042
static BOOLEAN RemoveEntryList(LIST_ENTRY *Entry)
Definition: introlists.h:87
#define ZONE_EXECUTE
Used for execute violation.
Definition: exceptions.h:700
#define memzero(a, s)
Definition: introcrt.h:35
#define PT_P
Definition: pgtable.h:83
BOOLEAN Guest64
True if this is a 64-bit guest, False if it is a 32-bit guest.
Definition: guests.h:286
INTSTATUS IntExceptGetVictimEpt(void *Context, QWORD Gpa, QWORD Gva, INTRO_OBJECT_TYPE Type, DWORD ZoneFlags, EXCEPTION_VICTIM_ZONE *Victim)
Fills an EXCEPTION_VICTIM_ZONE with relevant information from an EPT violation.
Definition: exceptions.c:742
unsigned long long QWORD
Definition: intro_types.h:53
QWORD Current
The currently used options.
Definition: guests.h:232
INTSTATUS IntCr0Read(DWORD CpuNumber, QWORD *Cr0Value)
Reads the value of the guest CR0.
Definition: introcpu.c:363
INTSTATUS IntWinPfnIsMmPfnDatabase(QWORD MmPfnDatabase)
Checks if a a guest virtual address points to MmPfnDatabase.
Definition: winpfn.c:24
static INTSTATUS IntWinGuestKernelHeadersInMemory(WIN_INIT_SWAP *Context, QWORD Cr3, QWORD VirtualAddress, QWORD PhysicalAddress, void *Data, DWORD DataSize, DWORD Flags)
Handles the swap in of the kernel MZPE headers.
Definition: winguest.c:1986
WINDOWS_GUEST * gWinGuest
Global variable holding the state of a Windows guest.
Definition: winguest.c:35
INTSTATUS IntVeInit(void)
Initialize the VE system.
Definition: vecore.c:2493
union _IMAGE_SECTION_HEADER::@209 Misc
#define INTRO_OPT_PROT_KM_IDTR
Enable interrupt descriptor-table registers protection.
Definition: intro_types.h:413
QWORD IdtBase
Original IDT base.
Definition: guests.h:110
void IntExceptUserLogInformation(EXCEPTION_VICTIM_ZONE *Victim, EXCEPTION_UM_ORIGINATOR *Originator, INTRO_ACTION Action, INTRO_ACTION_REASON Reason)
Print the information about a user-mode violation, dumps the code-blocks and the injection buffer...
#define TRUE
Definition: intro_types.h:30
QWORD Ssdt
Guest virtual address of the SSDT structure inside the kernel.
Definition: winguest.h:804
UINT32 VirtualAddress
Definition: winpe.h:85
INTSTATUS IntDecDecodeInstructionFromBuffer(PBYTE Buffer, size_t BufferSize, IG_CS_TYPE CsType, void *Instrux)
Decode an instruction from the provided buffer.
Definition: decoder.c:308
#define IS_KERNEL_POINTER_WIN(is64, p)
Checks if a guest virtual address resides inside the Windows kernel address space.
Definition: wddefs.h:76
BOOLEAN GuestInitialized
True if the OS-specific portion has been initialized.
Definition: guests.h:289
#define HpFreeAndNullWithTag(Add, Tag)
Definition: glue.h:517
#define TRACE(fmt,...)
Definition: glue.h:58
INFO_UD_PENDING * CurrentUD
The currently pending #UD injection on this CPU.
Definition: guests.h:123
INTSTATUS IntGsRead(DWORD CpuNumber, QWORD *GsValue)
Reads the IA32_GS_BASE guest MSR.
Definition: introcpu.c:289
static BOOLEAN IntWinGuestIsSystemCr3(QWORD KernelAddress, const VA_TRANSLATION *GvaTranslation, QWORD Cr3)
Checks if a Cr3 is the system Cr3.
Definition: winguest.c:435
PWIN_PROCESS_OBJECT IntWinProcFindObjectByCr3(QWORD Cr3)
Finds a process by its kernel CR3.
Definition: winprocesshp.c:122
#define INT_STATUS_INVALID_DATA_STATE
Definition: introstatus.h:183
Executions inside the SharedUserData region.
Definition: intro_types.h:264
#define INT_STATUS_INVALID_INTERNAL_STATE
Definition: introstatus.h:272
QWORD KernelVa
The guest virtual address at which the kernel image.
Definition: guests.h:279
INTSTATUS IntWinProcIterateGuestProcesses(PFUNC_IterateListCallback Callback, QWORD Aux)
Iterates the in-guest process list and calls Callback for each entry.
Definition: winprocesshp.c:428
INT32 e_lfanew
Definition: winpe.h:58
void IntExceptKernelLogInformation(EXCEPTION_VICTIM_ZONE *Victim, EXCEPTION_KM_ORIGINATOR *Originator, INTRO_ACTION Action, INTRO_ACTION_REASON Reason)
Print the information about a kernel-mode violation and dumps the code-blocks.
static INTSTATUS IntWinGuestHandleSudExec(void *Context, void *Hook, QWORD Address, INTRO_ACTION *Action)
Ept callback which handles executions inside SharedUserData.
Definition: winguest.c:966
Kernel-mode exception.
Definition: exceptions.h:62
struct _IMAGE_NT_HEADERS * PIMAGE_NT_HEADERS32
BYTE WordSize
Guest word size. Will be 4 for 32-bit guests and 8 for 64-bit guests.
Definition: guests.h:363
INTSTATUS IntWinDrvIsListHead(QWORD PsLoadedModuleListGva, void *PsLoadedModuleList, QWORD KernelLdr)
Used to identify WINDOWS_GUEST::PsLoadedModuleList.
Definition: windriver.c:67
static void InsertTailList(LIST_ENTRY *ListHead, LIST_ENTRY *Entry)
Definition: introlists.h:135
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 IntPeGetSectionHeaderByName(Base, Buff, Name, Cr3, Sec)
Definition: winpe.h:765
INTRO_EXEC_CONTEXT ExecContext
Information about the instruction that triggered the alert.
Definition: intro_types.h:1191
32-bit paging with PAE
Definition: introcore.h:70
QWORD VirtualAddress
The guest virtual address that will be read.
Definition: winguest.h:856
#define SWAPMEM_FLAG_ASYNC_CALL
Definition: swapmem.h:12
void IntWinGuestCancelKernelRead(void)
Cancels the kernel read.
Definition: winguest.c:611
#define WARNING(fmt,...)
Definition: glue.h:60
static INTSTATUS IntWinGuestSectionInMemory(WIN_INIT_SWAP *Context, QWORD Cr3, QWORD VirtualAddress, QWORD PhysicalAddress, void *Data, DWORD DataSize, DWORD Flags)
Handles the swap in of a kernel section done while the WINDOWS_GUEST.KernelBuffer is read...
Definition: winguest.c:1617
DWORD SelfMapIndex
The self map index.
Definition: guests.h:216
4-level paging
Definition: introcore.h:71
INTSTATUS IntHookGvaRemoveHook(HOOK_GVA **Hook, DWORD Flags)
Remove a GVA hook.
Definition: hook_gva.c:507
Sent when an EPT violation triggers an alert. See EVENT_EPT_VIOLATION.
Definition: intro_types.h:84
static void InitializeListHead(LIST_ENTRY *ListHead)
Definition: introlists.h:69
DWORD CpuCount
The number of logical CPUs.
Definition: guests.h:275
#define PAGE_SIZE
Definition: common.h:53
INTSTATUS IntWinApiHookAll(void)
Iterates through all hookable APIs and sets requested hooks.
Definition: winapi.c:209
Describes the modified zone.
Definition: exceptions.h:847
#define UNREFERENCED_PARAMETER(P)
Definition: introdefs.h:29
#define IC_TAG_IMGE
PE image buffer.
Definition: memtags.h:51
void IntDriverUninit(void)
Uninitializes the drivers submodule.
Definition: drivers.c:354
#define INT_STATUS_DATA_BUFFER_TOO_SMALL
Definition: introstatus.h:194
INTSTATUS IntWinGetVersionString(DWORD FullStringSize, DWORD VersionStringSize, CHAR *FullString, CHAR *VersionString)
Gets the version string for a Windows guest.
Definition: winguest.c:3064
#define WIN_KM_FIELD(Structure, Field)
Macro used to access kernel mode fields inside the WIN_OPAQUE_FIELDS structure.
Definition: winguest.h:726
static HOOK_GVA * gSudExecHook
The hook object protecting against executions on SharedUserData.
Definition: winguest.c:73
uint32_t DWORD
Definition: intro_types.h:49
UINT32 VirtualSize
Definition: winpe.h:83
DWORD KernelBufferSize
The size of the KernelBuffer.
Definition: winguest.h:838
#define INT_STATUS_INVALID_DATA_VALUE
Definition: introstatus.h:136
void IntWinProcUninit(void)
This function removes all process objects from the list, and registers the calls the cleanup function...
Definition: winprocess.c:3764
UINT32 Characteristics
Definition: winpe.h:92
#define THS_CHECK_DETOURS
Will check if any RIP is inside detours.
INTSTATUS IntPeValidateHeader(QWORD ImageBase, BYTE *ImageBaseBuffer, DWORD ImageBaseBufferSize, INTRO_PE_INFO *PeInfo, QWORD Cr3)
Validates a PE header.
Definition: winpe.c:131
PCHAR VersionString
A NULL terminated string containing Windows version information.
Definition: winguest.h:813
INTSTATUS IntSyscallRead(DWORD CpuNumber, QWORD *SysStar, QWORD *SysLstar)
Queries the IA32_STAR, and IA32_LSTAR guest MSRs.
Definition: introcpu.c:635
#define NO_ERRORCODE
Definition: processor.h:123
enum _INTRO_ACTION INTRO_ACTION
Event actions.
#define _In_reads_bytes_(expr)
Definition: intro_sal.h:25
#define PROCESS_SEARCH_LIMIT_THREAD_LOWER
The lower limit of the area in which the idle process is searched.
Definition: winguest.c:59
Holds information about a Windows guest.
Definition: winguest.h:796
#define INT_STATUS_INVALID_OBJECT_TYPE
Definition: introstatus.h:145
#define SWAPMEM_OPT_BP_FAULT
If set, the #PF will be generated from an int3 detour. Use this when injecting kernel PFs...
Definition: swapmem.h:27
#define IC_TAG_NAME
Object name.
Definition: memtags.h:56
__must_check INTSTATUS IntVirtMemMap(QWORD Gva, DWORD Length, QWORD Cr3, DWORD Flags, void **HostPtr)
Maps a guest virtual memory range inside Introcore virtual address space.
Definition: introcore.c:2134
INTSTATUS IntWinDrvIterateLoadedModules(PFUNC_IterateListCallback Callback, QWORD Aux)
Used to iterate trough the WINDOWS_GUEST::PsLoadedModuleList.
Definition: windriver.c:211
INTSTATUS IntWinGuestInit(void)
Initializes a new Windows guest.
Definition: winguest.c:645
INTSTATUS IntWinProcAdd(QWORD Eprocess, QWORD Aux)
Adds a new process to the Introcore list of processes.
Definition: winprocesshp.c:323
MM Mm
Guest memory information, such as paging mode, system Cr3 value, etc.
Definition: guests.h:370
INTSTATUS IntRegisterBreakpointHandler(PFUNC_IntBreakpointCallback Callback)
Definition: glue.c:583
INTSTATUS IntWinProcIsPsActiveProcessHead(QWORD Gva)
Checks if a guest memory area is the list head of the process list (PsActiveProcessHead) ...
Definition: winprocesshp.c:185
QWORD KeServiceDescriptorTable
Guest virtual address of the KeServiceDescriptorTable variable.
Definition: winguest.h:803
GUEST_STATE gGuest
The current guest state.
Definition: guests.c:48
void IntAlertFillWinProcessByCr3(QWORD ProcessCr3, INTRO_PROCESS *EventProcess)
Saves information about a Windows process inside an alert. The process is searched by its kernel CR3...
Definition: alerts.c:756
struct _EXCEPTION_UM_ORIGINATOR::@72 Return
INTSTATUS IntWinIdtProtectAll(void)
Activates the IDT protection for all the guest CPUs.
Definition: winidt.c:560
INTSTATUS IntWinGuestUnprotectSudExec(void)
Removes the execution EPT hook on SharedUserData.
Definition: winguest.c:1135
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 FIX_GUEST_POINTER(is64, x)
Masks the unused part of a Windows guest virtual address.
Definition: wddefs.h:87
struct _IMAGE_SECTION_HEADER * PIMAGE_SECTION_HEADER
#define IMAGE_SCN_MEM_DISCARDABLE
Definition: winpe.h:468
TIMER_FRIENDLY void IntDumpBuffer(void *Buffer, QWORD Gva, DWORD Length, DWORD RowLength, DWORD ElementLength, BOOLEAN LogHeader, BOOLEAN DumpAscii)
This function dumps a given buffer in a user friendly format.
Definition: dumper.c:48
QWORD Rsp
The value of the guest RSP register at the moment of execution.
Definition: intro_types.h:915
LIST_HEAD InitSwapHandles
A list of swap handles used to read KernelBuffer.
Definition: winguest.h:841
DWORD RemainingSections
The number of kernel sections not yet read into KernelBuffer.
Definition: winguest.h:840
INTRO_ACTION Action
The action that was taken as the result of this alert.
Definition: intro_types.h:1083
INTSTATUS IntKernVirtMemRead(QWORD KernelGva, DWORD Length, void *Buffer, DWORD *RetLength)
Reads data from a guest kernel virtual memory range.
Definition: introcore.c:674
KERNEL_DRIVER * IntDriverFindByAddress(QWORD Gva)
Returns the driver in which Gva resides.
Definition: drivers.c:164
#define VECTOR_PF
Definition: processor.h:116
INTSTATUS IntUDAddToPendingList(const QWORD Cr3, const QWORD Rip, const QWORD Thread, INFO_UD_PENDING **CurrentPendingUD)
Add a new UD to the list of pending injections.
Definition: udlist.c:30
INTSTATUS IntSwapgsStartMitigation(void)
Scan the kernel for vulnerable SWAPGS gadgets, and mitigate CVE-2019-1125, when such gadgets are foun...
Definition: swapgs.c:190
BOOLEAN KptiInstalled
True if KPTI was detected as installed (not necessarily active).
Definition: guests.h:288
IMAGE_FILE_HEADER FileHeader
Definition: winpe.h:227
The initialization swap handle.
Definition: winguest.h:851
PAGING_MODE Mode
The paging mode used by the guest.
Definition: guests.h:217
INTSTATUS IntWinGuestIsSupported(void)
Load os information from cami.
INTSTATUS IntCr3Read(DWORD CpuNumber, QWORD *Cr3Value)
Reads the value of the guest CR3.
Definition: introcpu.c:415
__must_check INTSTATUS IntPhysMemMap(QWORD PhysAddress, DWORD Length, DWORD Flags, void **HostPtr)
Maps a guest physical address inside Introcore VA space.
Definition: glue.c:338
INTSTATUS IntExceptUserGetExecOriginator(void *Process, EXCEPTION_UM_ORIGINATOR *Originator)
This function is used to get the originator for heap execution.
Workstation.
Definition: winguest.h:784
PCHAR NtBuildLabString
Definition: winguest.h:809
Encapsulates information about a virtual to physical memory translation.
Definition: introcore.h:102
BYTE * KernelBuffer
A buffer containing the entire kernel image.
Definition: winguest.h:837
KERNEL_DRIVER * KernelDriver
Points to the driver object that describes the kernel image.
Definition: guests.h:381
void IntWinUmCacheUninit(void)
Uninit the module cache system. This will remove all cache entries. Use this during Introcore uninit...
Definition: winumcache.c:1082
#define INT_STATUS_NOT_SUPPORTED
Definition: introstatus.h:287
INTRO_PROCESS CurrentProcess
The current process.
Definition: intro_types.h:1086
#define INTRO_OPT_PROT_KM_SUD_EXEC
Enable protection against executions on SharedUserData.
Definition: intro_types.h:499
VCPU_STATE * gVcpu
The state of the current VCPU.
Definition: guests.c:57
The action was blocked because there was no exception for it.
Definition: intro_types.h:189
static void IntWinGuestSendSudAlert(void *Originator, EXCEPTION_VICTIM_ZONE *Victim, BOOLEAN IsKernel, INTRO_ACTION Action, INTRO_ACTION_REASON Reason)
Constructs and sends an EVENT_EPT_VIOLATION alert which occurred due to an execution in SharedUserDat...
Definition: winguest.c:741
void IntAlertFillWinUmModule(const WIN_PROCESS_MODULE *Module, INTRO_MODULE *EventModule)
Fills information about a user mode module inside an alert.
Definition: alerts.c:653
UINT8 Name[IMAGE_SIZEOF_SHORT_NAME]
Definition: winpe.h:79
DWORD NtBuildNumberValue
The value of the NtBuildNumber kernel variable.
Definition: winguest.h:802
64-bit selector.
Definition: glueiface.h:188
INTSTATUS IntWinGuestFindIdleCr3(void)
Searches the Cr3 used by the idle process.
Definition: winguest.c:2650
DWORD ActiveCpuCount
The number of CPUs actually used by the guest.
Definition: guests.h:276
#define INTRO_OPT_PROT_KM_CR4
Enable CR4.SMEP and CR4.SMAP protection.
Definition: intro_types.h:410
DWORD SizeOfImage
Size of the image.
Definition: winpe.h:599
INTRO_EXEC_INFO ExecInfo
Execution information. Valid only if Violation is IG_EPT_HOOK_EXECUTE.
Definition: intro_types.h:1142
Event structure for EPT violations.
Definition: intro_types.h:1104
#define INT_STATUS_NOT_READY
Definition: introstatus.h:308
BOOLEAN Image64Bit
True if the image is 64 bit.
Definition: winpe.h:596
INTSTATUS IntGetCurrentRing(DWORD CpuNumber, DWORD *Ring)
Read the current protection level.
Definition: introcpu.c:959
Execute-access hook.
Definition: glueiface.h:300
struct _IMAGE_DOS_HEADER * PIMAGE_DOS_HEADER
BOOLEAN DisableOnReturn
Set to True if after returning from this event handler, introcore must be unloaded.
Definition: guests.h:324
DWORD Length
The length of the instruction.
Definition: intro_types.h:918
static INTSTATUS IntWinGuestReadKernel(PBYTE KernelHeaders)
Reads the whole kernel image in memory, including swapped-out sections.
Definition: winguest.c:1706
char CHAR
Definition: intro_types.h:56
INTSTATUS IntWinAgentInjectTrampoline(void)
Inject the agent trampoline inside the guest.
Definition: winagent.c:364
unsigned long long * PQWORD
Definition: intro_types.h:53
#define IC_TAG_KRNB
Kernel Buffer, cached by the introspection.
Definition: memtags.h:91
INTSTATUS IntCr4Protect(void)
Activates the Cr4 protection.
INTSTATUS IntVeDeployAgent(void)
Inject the VE agent inside the guest.
Definition: vecore.c:2063
Exploitation of Remote Services.
Definition: intro_types.h:1048
void * SwapHandle
The actual swap handle returned by IntSwapMemRead.
Definition: winguest.h:854
INTSTATUS IntPhysMemUnmap(void **HostPtr)
Unmaps an address previously mapped with IntPhysMemMap.
Definition: glue.c:396
#define PAGE_MASK
Definition: pgtable.h:35
#define IMAGE_SCN_MEM_NOT_PAGED
Definition: winpe.h:470
INTSTATUS IntExceptKernelGetOriginator(EXCEPTION_KM_ORIGINATOR *Originator, DWORD Options)
This function is used to get the information about the kernel-mode originator.
INTSTATUS IntAlertFillExecContext(QWORD Cr3, INTRO_EXEC_CONTEXT *ExecContext)
Fills the current execution context.
Definition: alerts.c:31
INTSTATUS IntNotifyIntroDetectedOs(INTRO_GUEST_TYPE OsType, DWORD OsVersion, BOOLEAN Is64)
Wrapper over GLUE_IFACE.NotifyIntrospectionDetectedOs.
Definition: glue.c:955
#define INT_STATUS_INVALID_PARAMETER_2
Definition: introstatus.h:65
static INTSTATUS IntWinGuestValidateKernel(QWORD KernelBase, QWORD KernelGva, BYTE *KernelHeaders)
Validates if the presumed kernel base is the real kernel base, based on various checks.
Definition: winguest.c:2184
INTSTATUS IntHandleBreakpoint(void *GuestHandle, QWORD GuestPhysicalAddress, DWORD CpuNumber)
Handle guest breakpoints.
Definition: callbacks.c:2554
INTRO_PROT_OPTIONS CoreOptions
The activation and protection options for this guest.
Definition: guests.h:267
QWORD Rip
Where the write/exec came.
Definition: exceptions.h:958
UINT16 SizeOfOptionalHeader
Definition: winpe.h:69
INTSTATUS IntCamiGetWinSupportedList(BOOLEAN KptiInstalled, BOOLEAN Guest64, DWORD *NtBuildNumberList, DWORD *Count)
Return a list of supported Windows NtBuildNumbers.
void IntExcept(EXCEPTION_VICTIM_ZONE *Victim, void *Originator, EXCEPTION_TYPE Type, INTRO_ACTION *Action, INTRO_ACTION_REASON *Reason, INTRO_EVENT_TYPE EventClass)
This function is the entry point for the exception mechanism.
Definition: exceptions.c:3317
#define INTRO_OPT_PROT_KM_GDTR
Enable global descriptor-table registers protection.
Definition: intro_types.h:424
static INTSTATUS IntWinGuestFindKernelObjectsInternal(void)
Finds all the objects of interest from the Windows kernel.
Definition: winguest.c:77
#define INT_STATUS_INVALID_DATA_SIZE
Definition: introstatus.h:142
INTRO_MODULE ReturnModule
The module to which the current code returns to.
Definition: intro_types.h:1111
#define FALSE
Definition: intro_types.h:34
#define ALIGN_DOWN(x, a)
Definition: introdefs.h:165
This structure describes a running process inside the guest.
Definition: winprocess.h:81
#define INT_STATUS_INSUFFICIENT_RESOURCES
Definition: introstatus.h:281