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 #include "winsud.h"
30 #include "winintobj.h"
31 
38 
43 #define KERNEL_SEARCH_LIMIT (16 * ONE_MEGABYTE)
44 
45 
46 //
47 // These are mostly constant on supported os versions
48 // If any windows update would fail the init because we can't find the idle process'
49 // cr3, simply change those and it SHOULD work.
50 //
51 
55 #define IDLE_THREAD_OFFSET_PCR ((gGuest.Guest64) ? 0x198 : 0x12C)
56 
58 #define PROCESS_SEARCH_LIMIT_THREAD_UPPER (DWORD)((gGuest.Guest64) ? 0x220 : 0x150)
59 
61 #define PROCESS_SEARCH_LIMIT_THREAD_LOWER (DWORD)((gGuest.Guest64) ? 0x210 : 0x150)
62 
69 #define CR3_OFFSET_IN_KPROCESS (DWORD)((gGuest.Guest64) ? 0x28 : 0x18)
70 
71 
72 static INTSTATUS
74  void
75  )
89 {
90  INTSTATUS status;
91  DWORD sectionCount;
92 
93  // hopefully, we won't have a total of more than 10 sections with names .data or ALMOSTRO
94  const DWORD maxSecCount = 10;
95  const DWORD objCount = 3;
96 
97  DWORD objectsFound = 0;
98 
99  QWORD kernelBase = gGuest.KernelVa;
100  BYTE *pKernel = gWinGuest->KernelBuffer;
101 
102  IMAGE_SECTION_HEADER *pSections = HpAllocWithTag(sizeof(*pSections) * maxSecCount, IC_TAG_IMGE);
103  if (NULL == pSections)
104  {
106  }
107 
108  status = IntPeGetSectionHeadersByName(kernelBase, pKernel, "ALMOSTRO", maxSecCount - 1, gGuest.Mm.SystemCr3,
109  pSections, &sectionCount);
110  if (!INT_SUCCESS(status) || (0 == sectionCount))
111  {
112  ERROR("[ERROR] IntPeGetSectionHeadersByName failed for `ALMOSTRO`: 0x%08x\n", status);
113  goto cleanup_and_exit;
114  }
115 
116  status = IntPeGetSectionHeadersByName(kernelBase, pKernel, ".data", 1, gGuest.Mm.SystemCr3,
117  &pSections[sectionCount], NULL);
118  if (!INT_SUCCESS(status))
119  {
120  ERROR("[ERROR] IntPeGetSectionHeadersByName failed for `.data`: 0x%08x\n", status);
121  goto cleanup_and_exit;
122  }
123 
124  sectionCount++;
125 
126  // Iterate the kernel sections in order to get the internal variables.
127  for (DWORD i = 0; i < sectionCount; i++)
128  {
129  PIMAGE_SECTION_HEADER pSec = &pSections[i];
130  DWORD pageCount = ROUND_UP(pSec->Misc.VirtualSize, PAGE_SIZE) / PAGE_SIZE;
131 
132  for (DWORD j = 0; j < pageCount; j++)
133  {
134  DWORD offset = pSec->VirtualAddress + j * PAGE_SIZE;
135  DWORD sizeToParse;
136  union
137  {
138  void *ptrValue;
139  DWORD *pPage32;
140  QWORD *pPage64;
141  } pPage;
142 
143  QWORD target = kernelBase + offset;
144 
145  pPage.ptrValue = NULL;
146 
147  if (offset > gWinGuest->KernelBufferSize)
148  {
149  status = IntVirtMemMap(target, PAGE_SIZE, gGuest.Mm.SystemCr3, 0, &pPage.ptrValue);
150  if (!INT_SUCCESS(status))
151  {
152  ERROR("[ERROR] Failed mapping page %d of section %d: 0x%08x\n", j, i, status);
153  continue;
154  }
155  }
156  else
157  {
158  pPage.ptrValue = pKernel + offset;
159  }
160 
161  // If this is the last page, parse only what's left
162  if (j == pageCount - 1)
163  {
164  sizeToParse = pSec->Misc.VirtualSize & PAGE_OFFSET;
165  }
166  else
167  {
168  sizeToParse = PAGE_SIZE;
169  }
170 
171  // Analyze this page - we will parse DWORD entities, since we're running on x86 here.
172  for (DWORD parsed = 0; parsed < sizeToParse / gGuest.WordSize;)
173  {
174  QWORD p = gGuest.Guest64 ? pPage.pPage64[parsed] : pPage.pPage32[parsed];
175  void *hostPtr = gGuest.Guest64 ? (void *)&pPage.pPage64[parsed] : (void *)&pPage.pPage32[parsed];
176 
177  if (!gGuest.Guest64 &&
179  (p == 0xffffffff) ||
180  (p % 4 != 0)))
181  {
182  goto _continue;
183  }
184 
185  if (gGuest.Guest64 &&
187  (p % 4 != 0) ||
188  ((p & 0xFFFFFFFF00000000) == 0xFFFFFFFF00000000)))
189  {
190  goto _continue;
191  }
192 
193  if (pSec->Name[0] != '.')
194  {
195  goto _almostro;
196  }
197 
198  if (0 == gWinGuest->PsLoadedModuleList)
199  {
200  status = IntWinDrvIsListHead(target, hostPtr, p);
201  if (INT_SUCCESS(status))
202  {
203  gWinGuest->PsLoadedModuleList = target;
204 
205  TRACE("[INTRO-INIT] Found loaded module list: 0x%llx\n", target);
206 
207  objectsFound++;
208 
209  goto _continue;
210  }
211  }
212 
213  if (0 == gWinGuest->PsActiveProcessHead)
214  {
215  // p now points to what we believe is the EPROCESS of the System process.
217  if (INT_SUCCESS(status))
218  {
219  gWinGuest->PsActiveProcessHead = target;
220 
221  TRACE("[INTRO-INIT] Found process list head: 0x%llx\n", target);
222 
223  objectsFound++;
224 
225  goto _continue;
226  }
227  }
228 
229 _almostro:
230  if (pSec->Name[0] == '.')
231  {
232  goto _continue;
233  }
234 
235  if (gWinGuest->MmPfnDatabase == 0)
236  {
237  status = IntWinPfnIsMmPfnDatabase(p);
238  if (INT_SUCCESS(status))
239  {
240  // Save the actual location, not the kernel pointer to it
241  gWinGuest->MmPfnDatabase = p;
242 
243  TRACE("[INTRO-INIT] Found PFN database: 0x%llx\n", p);
244 
245  // There are cases where pfn is before non-paged pool size
246  objectsFound++;
247 
248  goto _continue;
249  }
250  }
251 
252 _continue:
253  parsed++;
254 
255  target += gGuest.WordSize;
256 
257  if (objectsFound == objCount)
258  {
259  break;
260  }
261  }
262 
263  if (offset > gWinGuest->KernelBufferSize)
264  {
265  IntVirtMemUnmap(&pPage.ptrValue);
266  }
267 
268  if (objectsFound == objCount)
269  {
270  break;
271  }
272  }
273  }
274 
275  if (objectsFound < objCount)
276  {
277  status = INT_STATUS_NOT_FOUND;
278 
279  // Be more explicit
280  if (0 == gWinGuest->MmPfnDatabase)
281  {
282  ERROR("[ERROR] MmPfnDatabase not found!\n");
283  }
284 
285  if (0 == gWinGuest->PsLoadedModuleList)
286  {
287  ERROR("[ERROR] PsLoadedModuleList not found!\n");
288  }
289 
290  if (0 == gWinGuest->PsActiveProcessHead)
291  {
292  ERROR("[ERROR] PsActiveProcessHead not found!\n");
293  }
294  }
295  else
296  {
297  status = INT_STATUS_SUCCESS;
298  }
299 
300 cleanup_and_exit:
301  if (NULL != pSections)
302  {
303  HpFreeAndNullWithTag(&pSections, IC_TAG_IMGE);
304  }
305 
306  return status;
307 }
308 
309 
310 static INTSTATUS
312  void
313  )
325 {
326  INTSTATUS status;
327  QWORD systemProcess = 0;
328 
330  if (!INT_SUCCESS(status))
331  {
332  TRACE("[INTRO-INIT] IntWinGuestFindKernelObjects%d: 0x%08x\n", gGuest.Guest64 ? 64 : 32, status);
333  return status;
334  }
335 
336  status = IntKernVirtMemRead(gWinGuest->PsActiveProcessHead,
338  &systemProcess,
339  NULL);
340  if (!INT_SUCCESS(status))
341  {
342  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x\n", status);
343  return status;
344  }
345 
346  status = IntKernVirtMemFetchQword(systemProcess -
347  WIN_KM_FIELD(Process, ListEntry) +
348  WIN_KM_FIELD(Process, Cr3),
349  &gGuest.Mm.SystemCr3);
350  if (!INT_SUCCESS(status))
351  {
352  ERROR("[ERROR] IntKernVirtMemFetchQword failed: 0x%08x\n", status);
353  return status;
354  }
355 
356  // The CR3 (DirectoryTableBase field in KPROCESS) is 8 bytes only in long mode.
357  if (!gGuest.Guest64)
358  {
359  gGuest.Mm.SystemCr3 &= 0xFFFFFFFF;
360  }
361 
362  TRACE("[INTRO-INIT] System CR3 is 0x%016llx!\n", gGuest.Mm.SystemCr3);
363 
364  return INT_STATUS_SUCCESS;
365 }
366 
367 
368 static INTSTATUS
370  void
371  )
389 {
390  INTSTATUS status;
391  QWORD *p;
392 
395  {
397  }
398 
400  if (!INT_SUCCESS(status))
401  {
402  ERROR("[ERROR] IntPhysMemMap failed for 0x%016llx: 0x%08x\n", gGuest.Mm.SystemCr3 & PHYS_PAGE_MASK, status);
403  return status;
404  }
405 
406  for (DWORD i = 256; i < 512; i++)
407  {
408  if (((p[i] & PHYS_PAGE_MASK) == (gGuest.Mm.SystemCr3 & PHYS_PAGE_MASK)) && (0 == (p[i] & 0x800)))
409  {
410  LOG("[SELFMAP] Found index = %d (0x%x)\n", i, i);
411  gGuest.Mm.SelfMapIndex = i;
412  status = INT_STATUS_SUCCESS;
413  break;
414  }
415  }
416 
417  if (0 == gGuest.Mm.SelfMapIndex)
418  {
419  ERROR("[ERROR] Self map index not found!\n");
421  status = INT_STATUS_NOT_FOUND;
422  }
423 
424  IntPhysMemUnmap(&p);
425 
426  return status;
427 }
428 
429 
430 static BOOLEAN
432  _In_ QWORD KernelAddress,
433  _In_ const VA_TRANSLATION *GvaTranslation,
434  _In_ QWORD Cr3
435  )
448 {
449  INTSTATUS status;
450  VA_TRANSLATION nkt;
451  BOOLEAN valid = FALSE;
452  QWORD *p;
453 
454  // Try to translate the kernel virtual address and see if it matches
455  // Also, don't add the GPAs to the cache since they will be mostly wrong
456 
457  status = IntTranslateVirtualAddressEx(KernelAddress, Cr3, TRFLG_NONE, &nkt);
458  if (!INT_SUCCESS(status))
459  {
460  return FALSE;
461  }
462 
463  if (0 == (nkt.Flags & PT_P))
464  {
465  return FALSE;
466  }
467 
468  if ((nkt.PhysicalAddress != GvaTranslation->PhysicalAddress) ||
469  (nkt.MappingsCount != GvaTranslation->MappingsCount))
470  {
471  return FALSE;
472  }
473 
474  status = IntPhysMemMap(Cr3, PAGE_SIZE, 0, &p);
475  if (!INT_SUCCESS(status))
476  {
477  return FALSE;
478  }
479 
480  if (GvaTranslation->PagingMode == PAGING_4_LEVEL_MODE ||
481  GvaTranslation->PagingMode == PAGING_5_LEVEL_MODE)
482  {
483  // On 4-level paging, make sure the self map entry doesn't have bit 11 set.
484  BOOLEAN selfMapOk = FALSE;
485 
486  for (DWORD i = 256; i < 512; i++)
487  {
488  if (((p[i] & PHYS_PAGE_MASK) == Cr3) && (0 == (p[i] & 0x800)))
489  {
490  selfMapOk = TRUE;
491  break;
492  }
493  }
494 
495  if (!selfMapOk)
496  {
497  valid = FALSE;
498  goto _cleanup_and_leave;
499  }
500  }
501  else if (GvaTranslation->PagingMode == PAGING_PAE_MODE)
502  {
503  // On PAE paging, make sure the rest of the page is zero.
504  for (DWORD i = 4; i < 512; i++)
505  {
506  if (p[i] != 0)
507  {
508  valid = FALSE;
509  goto _cleanup_and_leave;
510  }
511  }
512  }
513  else
514  {
515  // Ignore normal paging mode.
516  }
517 
518  if (GvaTranslation->PhysicalAddress != nkt.PhysicalAddress)
519  {
520  WARNING("[WARNING] We have a translation, but different physical addresses for 0x%016llx: "
521  "0x%016llx != 0x%016llx\n", KernelAddress, GvaTranslation->PhysicalAddress, nkt.PhysicalAddress);
522 
523  valid = FALSE;
524  goto _cleanup_and_leave;
525  }
526 
527  valid = TRUE;
528 
529 _cleanup_and_leave:
530  IntPhysMemUnmap(&p);
531 
532  return valid;
533 }
534 
535 
536 static INTSTATUS
538  _In_ QWORD KernelAddress,
539  _Out_ QWORD *SystemCr3,
540  _In_ QWORD StartPhysical,
541  _In_ QWORD EndPhysical
542  )
557 {
558  INTSTATUS status;
559  const QWORD startPhys = StartPhysical;
560  const QWORD endPhys = EndPhysical;
561  QWORD currentCr3 = 0;
562  VA_TRANSLATION kt;
563 
564  if (NULL == SystemCr3)
565  {
567  }
568 
569  status = IntCr3Read(IG_CURRENT_VCPU, &currentCr3);
570  if (!INT_SUCCESS(status))
571  {
572  ERROR("[ERROR] IntCr3Read failed: 0x%08x\n", status);
573  return status;
574  }
575 
576  if (currentCr3 >= startPhys && currentCr3 < endPhys)
577  {
578  LOG("[WINGUEST STATIC] The current CR3 0x%016llx is already the system cr3!\n", currentCr3);
579  *SystemCr3 = currentCr3;
580 
581  return INT_STATUS_SUCCESS;
582  }
583 
584  status = IntTranslateVirtualAddressEx(KernelAddress, currentCr3, TRFLG_NONE, &kt);
585  if (!INT_SUCCESS(status))
586  {
587  ERROR("[ERROR] IntTranslateVirtualAddressEx failed for 0x%016llx with cr3 0x%016llx: 0x%08x\n",
588  KernelAddress, currentCr3, status);
589  return status;
590  }
591 
592  for (QWORD gpa = startPhys; gpa < endPhys; gpa += PAGE_SIZE)
593  {
594  if (IntWinGuestIsSystemCr3(KernelAddress, &kt, gpa))
595  {
596  *SystemCr3 = gpa;
597 
598  return INT_STATUS_SUCCESS;
599  }
600  }
601 
602  return INT_STATUS_NOT_FOUND;
603 }
604 
605 
606 void
608  void
609  )
616 {
617  INTSTATUS status;
618  LIST_ENTRY *initEntry;
619 
620  initEntry = gWinGuest->InitSwapHandles.Flink;
621  while (initEntry != &gWinGuest->InitSwapHandles)
622  {
623  PWIN_INIT_SWAP pInitSwap = CONTAINING_RECORD(initEntry, WIN_INIT_SWAP, Link);
624  initEntry = initEntry->Flink;
625 
626  status = IntSwapMemRemoveTransaction(pInitSwap->SwapHandle);
627  if (!INT_SUCCESS(status))
628  {
629  ERROR("[ERROR] IntSwapMemRemoveTransaction failed for %llx:%x: 0x%08x\n",
630  pInitSwap->VirtualAddress, pInitSwap->Size, status);
631  }
632 
633  RemoveEntryList(&pInitSwap->Link);
634 
635  HpFreeAndNullWithTag(&pInitSwap, IC_TAG_WSWP);
636  }
637 }
638 
639 
640 INTSTATUS
642  void
643  )
654 {
655  INTSTATUS status;
656 
658  if (!INT_SUCCESS(status))
659  {
660  ERROR("[ERROR] IntRegisterBreakpointHandler failed: 0x%08x\n", status);
661  return status;
662  }
663 
665 
666  return INT_STATUS_SUCCESS;
667 }
668 
669 
670 void
672  void
673  )
680 {
681  INTSTATUS status;
682 
683  if (!gGuest.GuestInitialized || NULL == gWinGuest)
684  {
685  return;
686  }
687 
688  if (gWinGuest->KernelBuffer)
689  {
691  }
692 
693  IntDriverUninit();
694 
695  status = IntWinDrvObjUninit();
696  if (!INT_SUCCESS(status))
697  {
698  ERROR("[ERROR] IntWinDrvObjUninit failed: 0x%08x\n", status);
699  }
700 
701  IntWinHalUninit();
702 
704 
705  // Do this after the process subsystem uninits, so we can free there whatever
706  // caches that weren't fully formed (only allocated)
708 
709  if (NULL != gWinGuest->NtBuildLabString)
710  {
712  }
713 
714  if (NULL != gWinGuest->VersionString)
715  {
717  }
718 
719  if (NULL != gWinGuest->ServerVersionString)
720  {
722  }
723 
724  status = IntCr4Unprotect();
725  if (!INT_SUCCESS(status))
726  {
727  ERROR("[ERROR] IntCr4Unprotect failed: 0x%08x\n", status);
728  }
729 
731 
733 }
734 
735 
736 INTSTATUS
738  void
739  )
749 {
750  INTSTATUS status;
751 
753  {
754  status = IntWinIdtProtectAll();
755  if (!INT_SUCCESS(status))
756  {
757  ERROR("[ERROR] IntWinIdtProtectAll failed: 0x%08x\n", status);
758  return status;
759  }
760  }
761 
763  {
764  status = IntMsrSyscallProtect();
765  if (!INT_SUCCESS(status))
766  {
767  ERROR("[ERROR] IntMsrSyscallProtect failed: 0x%08x\n", status);
768  return status;
769  }
770  }
771 
773  {
774  status = IntCr4Protect();
775  if (!INT_SUCCESS(status))
776  {
777  ERROR("[ERROR] IntCr4Protect failed: 0x%08x\n", status);
778  return status;
779  }
780  }
781 
783  {
784  status = IntIdtrProtect();
785  if (!INT_SUCCESS(status))
786  {
787  ERROR("[ERROR] IntIdtrProtect failed: 0x%08x\n", status);
788  return status;
789  }
790  }
791 
793  {
794  status = IntGdtrProtect();
795  if (!INT_SUCCESS(status))
796  {
797  ERROR("[ERROR] IntGdtrProtect failed: 0x%08x\n", status);
798  return status;
799  }
800  }
801 
803  {
804  status = IntWinSudProtectSudExec();
805  if (!INT_SUCCESS(status))
806  {
807  ERROR("[ERROR] IntWinSudProtectSudExec failed: 0x%08x\n", status);
808  return status;
809  }
810  }
811 
813  {
814  status = IntWinSudProtectIntegrity();
815  if (!INT_SUCCESS(status))
816  {
817  ERROR("[ERROR] IntWinSudProtectIntegrity failed: 0x%08x\n", status);
818  return status;
819  }
820  }
821 
823  {
824  status = IntWinIntObjProtect();
825  if (!INT_SUCCESS(status))
826  {
827  ERROR("[ERROR] IntWinIntObjProtect failed: 0x%08x\n", status);
828  return status;
829  }
830  }
831 
832  // Flag the protection
834 
835  return INT_STATUS_SUCCESS;
836 }
837 
838 
839 static INTSTATUS
841  void
842  )
852 {
853  INTSTATUS status;
854  QWORD expGva;
855 
856  status = IntPeFindKernelExport("PsCreateSystemThread", &gWinGuest->PsCreateSystemThread);
857  if (!INT_SUCCESS(status))
858  {
859  ERROR("[ERROR] %s not found: 0x%08x\n", "PsCreateSystemThread", status);
860  return status;
861  }
862 
863  TRACE("[INTRO-INIT] Found API function PsCreateSystemThread @ 0x%016llx...\n", gWinGuest->PsCreateSystemThread);
864 
865  status = IntPeFindKernelExport("ExAllocatePoolWithTag", &gWinGuest->ExAllocatePoolWithTag);
866  if (!INT_SUCCESS(status))
867  {
868  ERROR("[ERROR] %s not found: 0x%08x\n", "ExAllocatePoolWithTag", status);
869  return status;
870  }
871 
872  TRACE("[INTRO-INIT] Found API function ExAllocatePoolWithTag @ 0x%016llx...\n", gWinGuest->ExAllocatePoolWithTag);
873 
874  status = IntPeFindKernelExport("ExFreePoolWithTag", &gWinGuest->ExFreePoolWithTag);
875  if (!INT_SUCCESS(status))
876  {
877  ERROR("[ERROR] %s not found: 0x%08x\n", "ExFreePoolWithTag", status);
878  return status;
879  }
880 
881  TRACE("[INTRO-INIT] Found API function ExFreePoolWithTag @ 0x%016llx...\n", gWinGuest->ExFreePoolWithTag);
882 
883  status = IntPeFindKernelExport("NtBuildNumber", &expGva);
884  if (!INT_SUCCESS(status))
885  {
886  ERROR("[ERROR] %s not found: 0x%08x\n", "NtBuildNumber", status);
887  return status;
888  }
889 
890  status = IntKernVirtMemFetchDword(expGva, &gWinGuest->NtBuildNumberValue);
891  if (!INT_SUCCESS(status))
892  {
893  ERROR("[ERROR] Failed getting NtBuildNumber value: %08x\n", status);
894  return status;
895  }
896 
897  TRACE("[INTRO-INIT] Found NtBuildNumber @ 0x%016llx with value 0x%08x...\n",
898  expGva, gWinGuest->NtBuildNumberValue);
899 
900  status = IntPeFindKernelExport("NtBuildLab", &expGva);
901  if (!INT_SUCCESS(status))
902  {
903  ERROR("[ERROR] %s not found: 0x%08x\n", "NtBuildLab", status);
904  return status;
905  }
906 
907  status = IntReadString(expGva, 16, FALSE, &gWinGuest->NtBuildLabString, NULL);
908  if (!INT_SUCCESS(status))
909  {
910  ERROR("[ERROR] Failed getting NtBuildLab value for kernel base 0x%016llx:0x%08x\n", gGuest.KernelVa, status);
911  return status;
912  }
913 
914  TRACE("[INTRO-INIT] Found NtBuildLab @ 0x%016llx with value: `%s`\n",
915  expGva, gWinGuest->NtBuildLabString);
916 
917  if (!gGuest.Guest64)
918  {
919  status = IntPeFindKernelExport("KeServiceDescriptorTable", &gWinGuest->KeServiceDescriptorTable);
920  if (!INT_SUCCESS(status))
921  {
922  ERROR("[ERROR] %s not found: 0x%08x\n", "KeServiceDescriptorTable RVA", status);
923  return status;
924  }
925 
926  status = IntKernVirtMemFetchDword(gWinGuest->KeServiceDescriptorTable, (DWORD *)&gWinGuest->Ssdt);
927  if (!INT_SUCCESS(status))
928  {
929  ERROR("[ERROR] Failed getting KeServiceDescriptorTable value: %08x\n", status);
930  return status;
931  }
932 
933  // Now get the number of services. The KeServiceDescriptorTable is a struct, and the number of services
934  // is the third DWORD inside this structure.
935  status = IntKernVirtMemFetchDword(gWinGuest->KeServiceDescriptorTable + 8, &gWinGuest->NumberOfServices);
936  if (!INT_SUCCESS(status))
937  {
938  ERROR("[ERROR] Failed getting KeServiceDescriptorTable number of services: %08x\n", status);
939  return status;
940  }
941 
942  if (gWinGuest->NumberOfServices > 1024)
943  {
944  ERROR("[ERROR] The number of services in the SSDT is higher than expected: %u\n",
945  gWinGuest->NumberOfServices);
947  }
948 
949  TRACE("[INTRO-INIT] Found KeServiceDescriptorTable @ 0x%016llx with table at 0x%016llx and %d functions...\n",
950  gWinGuest->KeServiceDescriptorTable, gWinGuest->Ssdt, gWinGuest->NumberOfServices);
951  }
952 
953  return INT_STATUS_SUCCESS;
954 }
955 
956 
957 static INTSTATUS
959  _Out_ WIN_PRODUCT_TYPE *ProductType
960  )
973 {
974 // It seems it is hardcoded on every os but we might think about moving it to cami.
975 #define WIN_SHARED_USER_DATA_OFFSET_PRODUCT 0x264
976 
977  INTSTATUS status;
978  DWORD productType;
979 
980  if (!gGuest.Guest64)
981  {
982  *ProductType = winProductTypeWinNt;
984  }
985 
987  if (!INT_SUCCESS(status))
988  {
989  return status;
990  }
991 
992  if (productType != winProductTypeWinNt &&
993  productType != winProductTypeLanManNt &&
994  productType != winProductTypeServer)
995  {
997  }
998 
999  *ProductType = productType;
1000 
1001  return INT_STATUS_SUCCESS;
1002 
1003 #undef WIN_SHARED_USER_DATA_OFFSET_PRODUCT
1004 }
1005 
1006 
1007 static INTSTATUS
1009  void
1010  )
1026 {
1027  INTSTATUS status;
1028 
1029  status = IntWinGuestResolveImports();
1030  if (!INT_SUCCESS(status))
1031  {
1032  ERROR("[ERROR] IntWinGuestResolveImports failed: 0x%08x\n", status);
1033 
1035 
1036  goto leave_and_unload;
1037  }
1038 
1040  {
1041  ERROR("[ERROR] Guest %d booted with /3GB is not supported!\n", gGuest.OSVersion);
1042 
1044 
1046 
1047  goto leave_and_unload;
1048  }
1049 
1050  // Set the OS specific fields in the guest-state structure
1051  gGuest.OSVersion = gWinGuest->NtBuildNumberValue & 0xFFFF;
1053 
1054  status = IntWinGuestFetchProductType(&gWinGuest->ProductType);
1055  if (!INT_SUCCESS(status))
1056  {
1057  WARNING("[WARNING] IntWinGuestFetchProductType failed: 0x%08x, will not be able to determine whether it is a "
1058  "server or not\n", status);
1059  gWinGuest->ProductType = winProductTypeUnknown;
1060  }
1061 
1062  status = IntWinGuestIsSupported();
1063  if (!INT_SUCCESS(status))
1064  {
1065  ERROR("[ERROR] NtBuildNumber %04x is not supported!\n", gGuest.OSVersion);
1066 
1068 
1070 
1071  goto leave_and_unload;
1072  }
1073 
1074  LOG("[INFO] Identified OS type Windows, version %d\n", gGuest.OSVersion);
1075  LOG("[INFO] Guest has KPTI %s\n", gGuest.KptiInstalled ? "Installed" : "Not installed");
1076 
1078 
1079  // Fill in the auxiliary data. These fields may not be persistent and may change during the
1080  // normal execution of the OS, so these APIs may be called as many times as necessarily
1081  // (example, one could register a callback for a given event and call these functions again
1082  // anytime that event is triggered). However, all the fields are lists of some sort, and the
1083  // head will remain valid during the normal execution of the guest.
1084 
1085  status = IntWinGuestFindKernelObjects();
1086  if (!INT_SUCCESS(status))
1087  {
1088  if (INT_STATUS_LOAD_ABORTED != status)
1089  {
1090  ERROR("[ERROR] IntWinGuestFindKernelObjects failed: 0x%08x\n", status);
1091 
1093  }
1094  else
1095  {
1096  WARNING("[WARNING] Introcore load was aborted!\n");
1097  }
1098 
1099  goto leave_and_unload;
1100  }
1101 
1102  TRACE("[INTRO-INIT] Kernel objects successfully identified!\n");
1103 
1104  status = IntWinAgentInjectTrampoline();
1105  if (!INT_SUCCESS(status))
1106  {
1107  ERROR("[ERROR] IntWinAgentInjectTrampoline failed: 0x%08x\n", status);
1108  goto leave_and_unload;
1109  }
1110 
1111  status = IntWinApiHookAll();
1112  if (!INT_SUCCESS(status))
1113  {
1114  ERROR("[ERROR] IntWinApiHookAll failed: 0x%08x\n", status);
1115  goto leave_and_unload;
1116  }
1117 
1118  status = IntSwapgsStartMitigation();
1119  if (!INT_SUCCESS(status))
1120  {
1121  ERROR("[ERROR] IntSwapgsStartMitigation failed: 0x%08x\n", status);
1122  goto leave_and_unload;
1123  }
1124 
1126  if (!INT_SUCCESS(status))
1127  {
1128  ERROR("[ERROR] IntWinDrvIterateLoadedModules failed: 0x%08x\n", status);
1129  goto leave_and_unload;
1130  }
1131 
1133  if (NULL == gGuest.KernelDriver)
1134  {
1135  ERROR("[ERROR] Failed finding kernel module!\n");
1136  goto leave_and_unload;
1137  }
1138 
1139  LOG("[INTRO-INIT] Kernel loaded @ 0x%016llx size of image = 0x%llx timedate stamp = 0x%08x\n",
1141 
1142  status = IntWinHalCreateHalData();
1143  if (!INT_SUCCESS(status))
1144  {
1145  ERROR("[ERROR] IntWinHalCreateHalData failed: 0x%08x\n", status);
1146  goto leave_and_unload;
1147  }
1148 
1150  if (!INT_SUCCESS(status))
1151  {
1152  ERROR("[ERROR] IntWinProcIterateGuestProcesses failed: 0x%08x\n", status);
1153  goto leave_and_unload;
1154  }
1155 
1156  status = IntWinGuestActivateProtection();
1157  if (!INT_SUCCESS(status))
1158  {
1159  ERROR("[ERROR] IntWinGuestActivateProtection failed: 0x%08x\n", status);
1160  goto leave_and_unload;
1161  }
1162 
1164  {
1165  status = IntVeDeployAgent();
1166  if (!INT_SUCCESS(status))
1167  {
1168  ERROR("[ERROR] IntVeDeployAgent failed: 0x%08x\n", status);
1169  }
1170  }
1171 
1172  // Make sure no threads have a RIP pointing in a modified code region.
1173  TRACE("[WINGUEST] Ensuring no thread will return into our hooks!\n");
1174 
1176  if (!INT_SUCCESS(status))
1177  {
1178  ERROR("[ERROR] IntThrSafeCheckThreads failed: 0x%08x\n", status);
1179  goto leave_and_unload;
1180  }
1181 
1183 
1185 
1186  TRACE("[WINGUEST] Introspection successfully initialized!\n");
1187 
1188  return INT_STATUS_SUCCESS;
1189 
1190 leave_and_unload:
1192  return status;
1193 }
1194 
1195 
1196 static INTSTATUS
1198  _Inout_ WIN_INIT_SWAP *Context,
1199  _In_ QWORD Cr3,
1200  _In_ QWORD VirtualAddress,
1201  _In_ QWORD PhysicalAddress,
1202  _In_reads_bytes_(DataSize) void *Data,
1203  _In_ DWORD DataSize,
1204  _In_ DWORD Flags
1205  )
1230 {
1231  PWIN_INIT_SWAP pSwp;
1232  QWORD va;
1233  INTSTATUS status;
1234 
1236  UNREFERENCED_PARAMETER(VirtualAddress);
1237  UNREFERENCED_PARAMETER(PhysicalAddress);
1238 
1239  if (Flags & SWAPMEM_FLAG_ASYNC_CALL)
1240  {
1241  IntPauseVcpus();
1242  }
1243 
1244  status = INT_STATUS_SUCCESS;
1245 
1246  pSwp = Context;
1247  va = pSwp->VirtualAddress;
1248 
1249  // Remove the context. The caller knows this may happen & won't use it after IntSwapMemReadData
1250  RemoveEntryList(&pSwp->Link);
1252 
1253  if (0 == gWinGuest->RemainingSections)
1254  {
1255  ERROR("[ERROR] Callback came after we have no more sections to read...\n");
1257  goto resume_and_exit;
1258  }
1259 
1260  memcpy(gWinGuest->KernelBuffer + va, Data, DataSize);
1261 
1262  gWinGuest->RemainingSections--;
1263 
1264  if ((0 == gWinGuest->RemainingSections) && (Flags & SWAPMEM_FLAG_ASYNC_CALL))
1265  {
1266  TRACE("[WINGUEST STATIC] Since we are called asynchronously we will finish the initialization...\n");
1267 
1268  status = IntWinGuestFinishInit();
1269  if (!INT_SUCCESS(status))
1270  {
1271  ERROR("[ERROR] IntWinGuestFinishInit failed: 0x%08x\n", status);
1272  }
1273  }
1274 
1275 resume_and_exit:
1276  if (Flags & SWAPMEM_FLAG_ASYNC_CALL)
1277  {
1278  IntResumeVcpus();
1279  }
1280 
1281  return status;
1282 }
1283 
1284 
1285 static INTSTATUS
1287  _In_ PBYTE KernelHeaders
1288  )
1309 {
1310  BOOLEAN unmapNtHeaders;
1311  DWORD secStartOffset, secCount;
1312  PIMAGE_DOS_HEADER pDosHeader;
1313  INTSTATUS status;
1314  INTRO_PE_INFO peInfo = { 0 };
1315 
1316  unmapNtHeaders = FALSE;
1317  pDosHeader = (PIMAGE_DOS_HEADER)KernelHeaders;
1318 
1319  // First thing, validate the buffer
1320  status = IntPeValidateHeader(gGuest.KernelVa, KernelHeaders, PAGE_SIZE, &peInfo, 0);
1321  if (!INT_SUCCESS(status))
1322  {
1323  ERROR("[ERROR] IntPeValidateHeader failed: 0x%08x\n", status);
1324  return status;
1325  }
1326 
1327  if (gGuest.Guest64 != peInfo.Image64Bit)
1328  {
1329  ERROR("[ERROR] Inconsistent MZPE image!\n");
1331  }
1332 
1333  // Update the size of the kernel
1334  gGuest.KernelSize = peInfo.SizeOfImage;
1335 
1336  if (peInfo.Image64Bit)
1337  {
1338  PIMAGE_NT_HEADERS64 pNth64;
1339 
1340  if ((QWORD)(DWORD)pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS64) < PAGE_SIZE)
1341  {
1342  // We are in the same page, so it's safe to use this
1343  pNth64 = (PIMAGE_NT_HEADERS64)(KernelHeaders + pDosHeader->e_lfanew);
1344  }
1345  else
1346  {
1347  status = IntVirtMemMap(gGuest.KernelVa + pDosHeader->e_lfanew, sizeof(*pNth64),
1348  gGuest.Mm.SystemCr3, 0, &pNth64);
1349  if (!INT_SUCCESS(status))
1350  {
1351  ERROR("[ERROR] Failed mapping VA 0x%016llx to host: 0x%08x\n",
1352  gGuest.KernelVa + pDosHeader->e_lfanew, status);
1353  return status;
1354  }
1355 
1356  unmapNtHeaders = TRUE;
1357  }
1358 
1359  secCount = 0xffff & pNth64->FileHeader.NumberOfSections;
1360  secStartOffset = pDosHeader->e_lfanew + 4 + sizeof(IMAGE_FILE_HEADER) +
1362 
1363  if (unmapNtHeaders)
1364  {
1365  IntVirtMemUnmap(&pNth64);
1366  }
1367  }
1368  else
1369  {
1370  PIMAGE_NT_HEADERS32 pNth32;
1371 
1372  if ((QWORD)(DWORD)pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS32) < PAGE_SIZE)
1373  {
1374  // We are in the same page, so it's safe to use this
1375  pNth32 = (PIMAGE_NT_HEADERS32)(KernelHeaders + pDosHeader->e_lfanew);
1376  }
1377  else
1378  {
1379  status = IntVirtMemMap(gGuest.KernelVa + pDosHeader->e_lfanew, sizeof(*pNth32),
1380  gGuest.Mm.SystemCr3, 0, &pNth32);
1381  if (!INT_SUCCESS(status))
1382  {
1383  ERROR("[ERROR] Failed mapping VA 0x%016llx to host: 0x%08x\n",
1384  gGuest.KernelVa + pDosHeader->e_lfanew, status);
1385  return status;
1386  }
1387 
1388  unmapNtHeaders = TRUE;
1389  }
1390 
1391  secCount = 0xffff & pNth32->FileHeader.NumberOfSections;
1392  secStartOffset = pDosHeader->e_lfanew + 4 + sizeof(IMAGE_FILE_HEADER) +
1394 
1395  if (unmapNtHeaders)
1396  {
1397  IntVirtMemUnmap(&pNth32);
1398  }
1399  }
1400 
1401  if (secStartOffset >= PAGE_SIZE)
1402  {
1403  ERROR("[ERROR] Sections get outside the first page. We don't support this yet!\n");
1404  return INT_STATUS_NOT_SUPPORTED;
1405  }
1406 
1407  if (secStartOffset + secCount * sizeof(IMAGE_SECTION_HEADER) > PAGE_SIZE)
1408  {
1409  ERROR("[ERROR] Sections get outside the first page. We don't support this yet!\n");
1410  return INT_STATUS_NOT_SUPPORTED;
1411  }
1412 
1413  if (peInfo.SizeOfImage < PAGE_SIZE)
1414  {
1415  ERROR("[ERROR] SizeOfImage too small: %d!\n", peInfo.SizeOfImage);
1417  }
1418 
1419  // Now finally initialize the lock & kernel buffer
1420  gWinGuest->KernelBuffer = HpAllocWithTag(peInfo.SizeOfImage, IC_TAG_KRNB);
1421  if (NULL == gWinGuest->KernelBuffer)
1422  {
1424  }
1425 
1426  gWinGuest->KernelBufferSize = peInfo.SizeOfImage;
1427  memcpy(gWinGuest->KernelBuffer, KernelHeaders, PAGE_SIZE);
1428 
1429  gWinGuest->RemainingSections = secCount;
1430 
1431  for (DWORD i = 0; i < secCount; i++)
1432  {
1433  DWORD secActualSize;
1434 
1435  PIMAGE_SECTION_HEADER pSec = (PIMAGE_SECTION_HEADER)(gWinGuest->KernelBuffer + secStartOffset +
1436  i * sizeof(IMAGE_SECTION_HEADER));
1437 
1438  secActualSize = ROUND_UP(pSec->Misc.VirtualSize, PAGE_SIZE);
1439 
1440  if (0 == pSec->VirtualAddress)
1441  {
1442  ERROR("[ERROR] We cannot have a section starting at 0!\n");
1443 
1444  return INT_STATUS_NOT_SUPPORTED;
1445  }
1446 
1447  if (0 == pSec->Misc.VirtualSize)
1448  {
1449  ERROR("[ERROR] We cannot have a section starting at 0!\n");
1450 
1451  return INT_STATUS_NOT_SUPPORTED;
1452  }
1453 
1454  // Make sure the section fits within the allocated buffer. We must avoid cases where the SizeOfImage or
1455  // section headers are maliciously altered.
1456  if ((pSec->VirtualAddress >= peInfo.SizeOfImage) ||
1457  (secActualSize > peInfo.SizeOfImage) ||
1458  (pSec->VirtualAddress + secActualSize > peInfo.SizeOfImage))
1459  {
1460  ERROR("[ERROR] Section %d seems corrupted: sizeOfImage = 0x%x, secstart = 0x%x, secsize = 0x%x\n",
1461  i, peInfo.SizeOfImage, pSec->VirtualAddress, pSec->Misc.VirtualSize);
1462 
1464  }
1465 
1467  (0 == memcmp(pSec->Name, "INITKDBG", sizeof("INITKDBG") - 1) ||
1468  (0 == memcmp(pSec->Name, "ERRATA", sizeof("ERRATA") - 1))))
1469  {
1470  memset(gWinGuest->KernelBuffer + pSec->VirtualAddress, 0, secActualSize);
1471 
1472  gWinGuest->RemainingSections--;
1473  }
1474  else if (pSec->Characteristics & IMAGE_SCN_MEM_NOT_PAGED)
1475  {
1476  // The section will be present, so read it now
1478  secActualSize,
1479  gWinGuest->KernelBuffer + pSec->VirtualAddress,
1480  NULL);
1481  if (!INT_SUCCESS(status))
1482  {
1483  ERROR("[ERROR] IntKernVirtMemRead failed for 0x%016llx -> 0x%016llx %s: 0x%08x\n",
1484  gGuest.KernelVa + pSec->VirtualAddress,
1485  gGuest.KernelVa + pSec->VirtualAddress + secActualSize,
1486  pSec->Name, status);
1487 
1488  return status;
1489  }
1490 
1491  gWinGuest->RemainingSections--;
1492  }
1493  else
1494  {
1495  DWORD retSize = 0;
1496 
1497  // Use the swap mechanism only if we can't directly read the memory; this avoids unnecessary
1498  // recursive function calls.
1500  secActualSize,
1501  gWinGuest->KernelBuffer + pSec->VirtualAddress,
1502  &retSize);
1503  if (!INT_SUCCESS(status))
1504  {
1505  PWIN_INIT_SWAP pSwp = NULL;
1506  void *swapHandle = NULL;
1507 
1508  pSwp = HpAllocWithTag(sizeof(*pSwp), IC_TAG_WSWP);
1509  if (NULL == pSwp)
1510  {
1512  }
1513 
1514  pSwp->VirtualAddress = pSec->VirtualAddress;
1515  pSwp->Size = secActualSize;
1516 
1517  InsertTailList(&gWinGuest->InitSwapHandles, &pSwp->Link);
1518 
1519  WARNING("Section %d / %d is not in memory, will do a swap mem read\n", i, secCount);
1520 
1521  status = IntSwapMemReadData(0,
1522  gGuest.KernelVa + pSec->VirtualAddress,
1523  secActualSize,
1525  pSwp,
1526  0,
1528  NULL,
1529  &swapHandle);
1530  if (!INT_SUCCESS(status))
1531  {
1532  ERROR("[ERROR] IntSwapMemReadData failed: 0x%08x\n", status);
1533  return status;
1534  }
1535 
1536  // The callback will be called async, save the handle in case an uninit will come
1537  if (NULL != swapHandle)
1538  {
1539  pSwp->SwapHandle = swapHandle;
1540  }
1541  }
1542  else
1543  {
1544  if (retSize != secActualSize)
1545  {
1546  ERROR("We requested %08x bytes, but got %08x!\n", secActualSize, retSize);
1548  }
1549 
1550  gWinGuest->RemainingSections--;
1551  }
1552  }
1553  }
1554 
1555  // We managed to read everything here, so continue the initialization
1556  if (0 == gWinGuest->RemainingSections)
1557  {
1558  TRACE("[WINGUEST STATIC] All sections were present in memory!\n");
1559 
1560  status = IntWinGuestFinishInit();
1561  if (!INT_SUCCESS(status))
1562  {
1563  ERROR("[ERROR] IntWinGuestFinishInit failed: 0x%08x\n", status);
1564  }
1565  }
1566 
1567  return status;
1568 }
1569 
1570 
1571 static INTSTATUS
1573  _Inout_ WIN_INIT_SWAP *Context,
1574  _In_ QWORD Cr3,
1575  _In_ QWORD VirtualAddress,
1576  _In_ QWORD PhysicalAddress,
1577  _In_reads_bytes_(DataSize) void *Data,
1578  _In_ DWORD DataSize,
1579  _In_ DWORD Flags
1580  )
1598 {
1599  INTSTATUS status;
1600  PWIN_INIT_SWAP pSwp;
1601 
1603  UNREFERENCED_PARAMETER(VirtualAddress);
1604  UNREFERENCED_PARAMETER(PhysicalAddress);
1605  UNREFERENCED_PARAMETER(Flags);
1606  UNREFERENCED_PARAMETER(DataSize);
1607 
1608  IntPauseVcpus();
1609 
1610  // Remove the context. The caller knows this may happen & won't use it after IntSwapMemReadData
1611  pSwp = Context;
1612  RemoveEntryList(&pSwp->Link);
1614 
1615  status = IntWinGuestReadKernel(Data);
1616  if (!INT_SUCCESS(status))
1617  {
1618  ERROR("[ERROR] IntWinGuestReadKernel failed: 0x%08x\n", status);
1619 
1620  // We don't care about specifics, but the introspection MUST be unloaded
1622  }
1623 
1624  IntResumeVcpus();
1625 
1626  return status;
1627 }
1628 
1629 
1630 static INTSTATUS
1632  _In_ QWORD KernelGva,
1633  _In_ BOOLEAN Guest64,
1634  _In_ BOOLEAN IsKptiInstalled,
1635  _Out_ DWORD *NtBuildNumber
1636  )
1654 {
1655  INTSTATUS status;
1656  DWORD limit, cb;
1657  DWORD smallestBuild, biggestBuild;
1658  QWORD gva, cr3;
1659  PDWORD pPage;
1660  BOOLEAN found;
1661  PDWORD pNtList;
1662 
1663  limit = 0;
1664  gva = KernelGva & PAGE_MASK;
1665  pNtList = NULL;
1666  pPage = NULL;
1667  found = FALSE;
1668  cb = 0;
1669 
1670  status = IntCamiGetWinSupportedList(IsKptiInstalled, Guest64, pNtList, &cb);
1671  if (!INT_SUCCESS(status) || 0 == cb)
1672  {
1673  ERROR("[ERROR] IntCamiGetWinSupportedList failed with count %u: 0x%08x\n", cb, status);
1674  return INT_STATUS_NOT_FOUND;
1675  }
1676 
1677  TRACE("[INFO] %d supported os versions from cami\n", cb);
1678 
1679  pNtList = HpAllocWithTag(sizeof(*pNtList) * cb, IC_TAG_CAMI);
1680  if (NULL == pNtList)
1681  {
1683  }
1684 
1685  status = IntCamiGetWinSupportedList(IsKptiInstalled, Guest64, pNtList, &cb);
1686  if (!INT_SUCCESS(status) || 0 == cb)
1687  {
1688  ERROR("[ERROR] IntCamiGetWinSupportedList failed: 0x%08x\n", status);
1689  goto cleanup_and_exit;
1690  }
1691 
1692  // pNtList is sorted.
1693  smallestBuild = pNtList[0];
1694  biggestBuild = pNtList[cb - 1];
1695 
1696  cr3 = gGuest.Mm.SystemCr3;
1697 
1698  found = FALSE;
1699  while (limit < KERNEL_SEARCH_LIMIT)
1700  {
1701  status = IntVirtMemMap(gva, PAGE_SIZE, cr3, 0, &pPage);
1702  if (!INT_SUCCESS(status))
1703  {
1704  pPage = NULL;
1705  goto _next_page;
1706  }
1707 
1708  for (DWORD i = 0; i < PAGE_SIZE / sizeof(DWORD); i++)
1709  {
1710  DWORD val = pPage[i];
1711 
1712  if ((val & 0xf0000000) != 0xf0000000 ||
1713  (val & 0xf000ffff) != val ||
1714  (val & 0xffff) > biggestBuild ||
1715  (val & 0xffff) < smallestBuild)
1716  {
1717  continue;
1718  }
1719 
1720  for (DWORD j = 0; j < cb; j++)
1721  {
1722  if (pNtList[j] == (val & 0xFFFF))
1723  {
1724  TRACE("[WINGUEST STATIC] Found an NtBuildNumber 0x%08x (%d) @ 0x%016llx\n",
1725  val, val & 0xffff, gva + i * sizeof(DWORD));
1726 
1727  *NtBuildNumber = pNtList[j];
1728  found = TRUE;
1729  goto cleanup_and_exit;
1730  }
1731  }
1732  }
1733 
1734 _next_page:
1735  if (NULL != pPage)
1736  {
1737  IntVirtMemUnmap(&pPage);
1738  }
1739 
1740  if (found)
1741  {
1742  break;
1743  }
1744 
1745  gva += PAGE_SIZE;
1746  limit += PAGE_SIZE;
1747  }
1748 
1749 cleanup_and_exit:
1750  if (NULL != pPage)
1751  {
1752  IntVirtMemUnmap(&pPage);
1753  }
1754 
1755  if (NULL != pNtList)
1756  {
1757  HpFreeAndNullWithTag(&pNtList, IC_TAG_CAMI);
1758  }
1759 
1760  if (!found)
1761  {
1762  return INT_STATUS_NOT_FOUND;
1763  }
1764 
1765  return INT_STATUS_SUCCESS;
1766 }
1767 
1768 
1769 static INTSTATUS
1771  _In_ QWORD KernelBase,
1772  _In_ QWORD KernelGva,
1773  _In_ BYTE *KernelHeaders
1774  )
1798 {
1799  IMAGE_SECTION_HEADER *pSec = NULL;
1800  const DWORD maxSecCount = 10;
1801  DWORD sectionCount = 0;
1802  INTSTATUS status;
1803  BOOLEAN found = FALSE;
1804  void *mappedSecPage = NULL;
1805 
1806  pSec = HpAllocWithTag(sizeof(*pSec) * maxSecCount, IC_TAG_IMGE);
1807  if (NULL == pSec)
1808  {
1810  }
1811 
1812  // Check if the current GVA is inside any section, as on 20h1 we have some mapped MZPEs inside kernel.
1813  status = IntPeGetSectionHeaderByRva(KernelBase, KernelHeaders, (DWORD)(KernelGva - KernelBase), &pSec[0]);
1814  if (INT_SUCCESS(status))
1815  {
1816  if ((pSec[0].Characteristics & IMAGE_SCN_MEM_DISCARDABLE) ||
1817  (pSec[0].Characteristics & IMAGE_SCN_MEM_EXECUTE) == 0 ||
1818  (pSec[0].Characteristics & IMAGE_SCN_MEM_WRITE))
1819  {
1820  WARNING("[WARNING] Gva 0x%016llx is not in a good section of 0x%016llx, discardable: %d, "
1821  "execute: %d, writable: %d",
1822  KernelGva, KernelBase, !!(pSec[0].Characteristics & IMAGE_SCN_MEM_DISCARDABLE),
1823  !!(pSec[0].Characteristics & IMAGE_SCN_MEM_EXECUTE),
1824  !!(pSec[0].Characteristics & IMAGE_SCN_MEM_WRITE));
1826  goto cleanup_and_exit;
1827  }
1828  }
1829 
1830  // The GVA is inside a good section, now verify if we can find PsNtosImageBase in ALMOSTRO or .data.
1831  status = IntPeGetSectionHeadersByName(KernelBase, KernelHeaders, "ALMOSTRO", maxSecCount - 1, gGuest.Mm.SystemCr3,
1832  pSec, &sectionCount);
1833  if (!INT_SUCCESS(status) || (0 == sectionCount))
1834  {
1835  WARNING("[WARNING] IntPeGetSectionHeadersByName failed for `ALMOSTRO`: 0x%08x, secCount = %d\n", status, sectionCount);
1836  // Overwrite the status, as ALMOSTRO should always be available, we will return INT_STATUS_INVALID_DATA_STATE;
1838  goto cleanup_and_exit;
1839  }
1840 
1841  status = IntPeGetSectionHeaderByName(KernelBase, KernelHeaders, ".data", gGuest.Mm.SystemCr3, &pSec[sectionCount]);
1842  if (!INT_SUCCESS(status))
1843  {
1844  WARNING("[WARNING] Presumed base 0x%016llx has no .data section!\n", KernelBase);
1846  goto cleanup_and_exit;
1847  }
1848 
1849  sectionCount++;
1850 
1851  for (DWORD iSec = 0; iSec < sectionCount; iSec++)
1852  {
1853  if ((pSec[iSec].VirtualAddress & PAGE_OFFSET) != 0)
1854  {
1855  WARNING("[WARNING] Base 0x%016llx has section start 0x%08x which is not page aligned!\n",
1856  KernelBase, pSec[iSec].VirtualAddress);
1858  goto cleanup_and_exit;
1859  }
1860 
1861  if (pSec[iSec].Misc.VirtualSize > 2 * ONE_MEGABYTE)
1862  {
1863  WARNING("[WARNING] Section %d has size too big: 0x%08x\n", iSec, pSec[iSec].Misc.VirtualSize);
1865  goto cleanup_and_exit;
1866  }
1867 
1868  for (QWORD page = KernelBase + pSec[iSec].VirtualAddress;
1869  page < KernelBase + pSec[iSec].VirtualAddress + pSec[iSec].Misc.VirtualSize;
1870  page += PAGE_SIZE)
1871  {
1872  DWORD sizeToMap = PAGE_SIZE;
1873 
1874  if (page == ((KernelBase + pSec[iSec].VirtualAddress + pSec[iSec].Misc.VirtualSize) & PAGE_MASK))
1875  {
1876  sizeToMap = ALIGN_DOWN(PAGE_REMAINING(pSec[iSec].Misc.VirtualSize), gGuest.WordSize);
1877  }
1878 
1879  status = IntVirtMemMap(page, sizeToMap, gGuest.Mm.SystemCr3, 0, &mappedSecPage);
1880  if (!INT_SUCCESS(status))
1881  {
1882  WARNING("[WARNING] IntVirtMemMap failed for 0x%016llx: 0x%08x. .data seems paged "
1883  "out for 0x%016llx!\n", page, status, KernelBase);
1884  goto cleanup_and_exit;
1885  }
1886 
1887  for (DWORD current = 0; current < sizeToMap; current += gGuest.WordSize)
1888  {
1889  QWORD currentptr = gGuest.Guest64 ? *(QWORD *)((size_t)mappedSecPage + current) :
1890  *(DWORD *)((size_t)mappedSecPage + current);
1891 
1892  if (currentptr == KernelBase)
1893  {
1894  TRACE("[INFO] Found PsNtosImageBase = 0x%016llx at address 0x%016llx!\n",
1895  KernelBase, page + current);
1896  found = TRUE;
1897  goto cleanup_and_exit;
1898  }
1899  }
1900 
1901  IntVirtMemUnmap(&mappedSecPage);
1902  }
1903  }
1904 
1905 cleanup_and_exit:
1906  if (NULL != mappedSecPage)
1907  {
1908  IntVirtMemUnmap(&mappedSecPage);
1909  }
1910 
1911  if (NULL != pSec)
1912  {
1914  }
1915 
1916  if (!INT_SUCCESS(status))
1917  {
1918  return status;
1919  }
1920 
1921  if (!found)
1922  {
1923  WARNING("[WARNING] PsNtosImageBase not found for 0x%016llx!\n", KernelBase);
1924  return INT_STATUS_NOT_FOUND;
1925  }
1926 
1927  return INT_STATUS_SUCCESS;
1928 }
1929 
1930 
1931 static INTSTATUS
1933  _In_ QWORD KernelGva,
1934  _Out_ QWORD *KernelBase
1935  )
1949 {
1950  INTSTATUS status;
1951  PBYTE hostPage;
1952  QWORD kernelBase, limit;
1953  BOOLEAN found;
1954 
1955  hostPage = NULL;
1956  kernelBase = KernelGva & PAGE_MASK;
1957  limit = 0;
1958  found = FALSE;
1959 
1960  // Take a walk from page 2 page, until we find the DOS headers or a non-present page (which we
1961  // assume it's the MZPE headers, since the KernelGva points inside .text)
1962  while (limit < KERNEL_SEARCH_LIMIT)
1963  {
1964  status = IntVirtMemMap(kernelBase, PAGE_SIZE, gGuest.Mm.SystemCr3, 0, &hostPage);
1965  if (!INT_SUCCESS(status))
1966  {
1967  hostPage = NULL;
1968  goto _next_page;
1969  }
1970 
1971  if ((hostPage[0] == 'M' && hostPage[1] == 'Z'))
1972  {
1973  status = IntWinGuestValidateKernel(kernelBase, KernelGva, hostPage);
1974  if (!INT_SUCCESS(status))
1975  {
1976  WARNING("[WARNING] IntWinGuestValidateKernel failed: 0x%08x\n", status);
1977  }
1978  else
1979  {
1980  found = TRUE;
1981  break;
1982  }
1983  }
1984 
1985  IntVirtMemUnmap(&hostPage);
1986 
1987 _next_page:
1988  kernelBase -= PAGE_SIZE;
1989  limit += PAGE_SIZE;
1990  }
1991 
1992  if (NULL != hostPage)
1993  {
1994  IntVirtMemUnmap(&hostPage);
1995  }
1996 
1997  if (!found && (limit == KERNEL_SEARCH_LIMIT))
1998  {
1999  ERROR("[ERROR] Could not find the kernel headers in the first %lldMB!\n",
2001  return INT_STATUS_NOT_FOUND;
2002  }
2003 
2004  *KernelBase = kernelBase;
2005 
2006  return INT_STATUS_SUCCESS;
2007 }
2008 
2009 
2010 static DWORD
2012  _In_ DWORD CpuCount
2013  )
2026 {
2027  DWORD activeCount = CpuCount;
2028 
2029  // For each CPU reported by the integrator, try to read and validate CR3.
2030  for (DWORD i = 0; i < CpuCount; i++)
2031  {
2032  INTSTATUS status;
2033  QWORD cr3, cr0;
2034 
2035  status = IntCr3Read(i, &cr3);
2036  if (!INT_SUCCESS(status))
2037  {
2038  cr3 = 0;
2039  }
2040 
2041  status = IntCr0Read(i, &cr0);
2042  if (!INT_SUCCESS(status))
2043  {
2044  cr0 = 0;
2045  }
2046 
2047  if (0 == cr3 || 0 == (cr0 & CR0_PG))
2048  {
2049  activeCount--;
2051  }
2052  }
2053 
2054  if (activeCount != CpuCount)
2055  {
2056  WARNING("[WARNING] The active cpu count (%d) is different than the actual cpu count (%d)\n",
2057  activeCount, CpuCount);
2058  }
2059 
2060  TRACE("[INTRO-INIT] Active CPU Count: %d\n", activeCount);
2061 
2062  return activeCount;
2063 }
2064 
2065 
2066 static INTSTATUS
2068  _In_ QWORD Syscall
2069  )
2089 {
2090  INTSTATUS status;
2091  QWORD kernelCr3OffsetPcr;
2092  QWORD kpcr;
2093  PBYTE pPage;
2094  BOOLEAN bFound;
2095  DWORD csType, mapSize;
2096  INSTRUX offsetInstrux;
2097 
2098  bFound = FALSE;
2099  pPage = NULL;
2101  kernelCr3OffsetPcr = kpcr = 0;
2102  mapSize = PAGE_SIZE;
2103 
2104  //
2105  // We map 4K starting from the syscall
2106  //
2107  status = IntVirtMemMap(Syscall, mapSize, 0, 0, &pPage);
2108  if (!INT_SUCCESS(status))
2109  {
2110  //
2111  // Map the remaining of the page Syscall is in. It SHOULD contain the instructions we're looking for.
2112  //
2113  mapSize -= (Syscall & PAGE_OFFSET);
2114  status = IntVirtMemMap(Syscall, mapSize, 0, 0, &pPage);
2115  if (!INT_SUCCESS(status))
2116  {
2117  ERROR("[ERROR] Failed to map Syscall page: 0x%08x\n", status);
2118  goto cleanup_and_exit;
2119  }
2120  }
2121 
2122  //
2123  // We will search for two instructions here:
2124  // 1. mov regx, q/dword ptr gs/fs:[KernelCr3OffsetInPcr]
2125  // 2. mov cr3, regx
2126  //
2127  for (DWORD i = 0; i < mapSize;)
2128  {
2129  INSTRUX instrux;
2130 
2131  CHAR nd[ND_MIN_BUF_SIZE] = { 0 };
2132 
2133  status = IntDecDecodeInstructionFromBuffer(pPage + i, mapSize - i, csType, &instrux);
2134  if (!INT_SUCCESS(status))
2135  {
2136  i++;
2137  continue;
2138  }
2139 
2140 #ifdef DEBUG
2141  NdToText(&instrux, 0, sizeof(nd), nd);
2142 #endif // DEBUG
2143 
2144  //
2145  // If we haven't found the mov regx, gs/fs:[KernelCr3OffsetInPcr] instr
2146  // or we found an instruction that uses the same register as destination
2147  //
2148  if (ND_INS_MOV == instrux.Instruction && 2 == instrux.ExpOperandsCount &&
2149  ND_OP_REG == instrux.Operands[0].Type &&
2150  ND_REG_GPR == instrux.Operands[0].Info.Register.Type)
2151  {
2152  if (!bFound ||
2153  (instrux.Operands[0].Info.Register.Reg == offsetInstrux.Operands[0].Info.Register.Reg))
2154  {
2155  //
2156  // mov regx, gs/fs:[KernelCr3OffsetInPcr] instr
2157  //
2158  if (ND_OP_MEM == instrux.Operands[1].Type && instrux.Operands[1].Info.Memory.HasSeg &&
2159  instrux.Operands[1].Info.Memory.HasDisp &&
2160  ((gGuest.Guest64 && NDR_GS == instrux.Operands[1].Info.Memory.Seg) ||
2161  (!gGuest.Guest64 && NDR_FS == instrux.Operands[1].Info.Memory.Seg)))
2162  {
2163  memcpy(&offsetInstrux, &instrux, sizeof(instrux));
2164  bFound = TRUE;
2165 
2166  TRACE("[INFO] Found a possible offset instruction: %s\n", nd);
2167  }
2168  //
2169  // This instruction overwrites the register that holds the gs/fs:[something]
2170  // so the last valid instruction we found is no longer valid
2171  //
2172  else
2173  {
2174  memzero(&offsetInstrux, sizeof(offsetInstrux));
2175  bFound = FALSE;
2176  }
2177  }
2178  }
2179  //
2180  // We found the first valid instruction, now we search for a mov cr3, regx
2181  //
2182  else if (bFound && ND_INS_MOV_CR == instrux.Instruction && 2 == instrux.ExpOperandsCount &&
2183  ND_OP_REG == instrux.Operands[0].Type && ND_OP_REG == instrux.Operands[1].Type &&
2184  ND_REG_CR == instrux.Operands[0].Info.Register.Type &&
2185  NDR_CR3 == instrux.Operands[0].Info.Register.Reg &&
2186  offsetInstrux.Operands[0].Info.Register.Reg == instrux.Operands[1].Info.Register.Reg)
2187  {
2188  kernelCr3OffsetPcr = offsetInstrux.Operands[1].Info.Memory.Disp;
2189 
2190  TRACE("[INFO] Found a valid second instruction: %s\n", nd);
2191  TRACE("[INFO] We will use the last possible offset instruction!\n");
2192 
2193  break;
2194  }
2195 
2196  i += instrux.Length;
2197  }
2198 
2199  if (0 == kernelCr3OffsetPcr)
2200  {
2201  ERROR("[ERROR] Could not find a valid instruction to get kernel cr3!");
2202  status = INT_STATUS_NOT_FOUND;
2203  goto cleanup_and_exit;
2204  }
2205 
2206  TRACE("[INTRO-INIT] Found KernelDirectoryTableBase offset in PCR at %llx\n", kernelCr3OffsetPcr);
2207 
2208  status = IntFindKernelPcr(IG_CURRENT_VCPU, &kpcr);
2209  if (!INT_SUCCESS(status))
2210  {
2211  ERROR("[ERROR] IntFindKernelPcr failed: 0x%08x\n", status);
2212  goto cleanup_and_exit;
2213  }
2214 
2215  TRACE("[INTRO-INIT] Found PCR at 0x%016llx\n", kpcr);
2216 
2217  status = IntKernVirtMemRead(kpcr + kernelCr3OffsetPcr, gGuest.WordSize, &gGuest.Mm.SystemCr3, NULL);
2218  if (!INT_SUCCESS(status))
2219  {
2220  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x\n", status);
2221  goto cleanup_and_exit;
2222  }
2223 
2224 cleanup_and_exit:
2225 
2226  if (NULL != pPage)
2227  {
2228  IntVirtMemUnmap(&pPage);
2229  }
2230 
2231  return status;
2232 }
2233 
2234 
2235 INTSTATUS
2237  void
2238  )
2248 {
2249  INTSTATUS status;
2250  QWORD pcr, idleThread, idleProcess;
2251  PBYTE pMap;
2252  DWORD mapSize;
2253  BOOLEAN bFound;
2254 
2255  pcr = idleThread = idleProcess = 0;
2256  bFound = FALSE;
2257  pMap = NULL;
2259 
2260  status = IntFindKernelPcr(IG_CURRENT_VCPU, &pcr);
2261  if (!INT_SUCCESS(status))
2262  {
2263  ERROR("[ERROR] IntFindKernelPcr failed: 0x%08x\n", status);
2264  return status;
2265  }
2266 
2267  TRACE("[INFO] KPCR [%d] @ 0x%016llx\n", gVcpu->Index, pcr);
2268 
2269  // Read the idle thread
2271  gGuest.WordSize,
2272  &idleThread,
2273  NULL);
2274  if (!INT_SUCCESS(status))
2275  {
2276  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x\n", status);
2277  return status;
2278  }
2279 
2280  TRACE("[INFO] Idle thread [%d] @ 0x%016llx\n", gVcpu->Index, idleThread);
2281 
2282  status = IntVirtMemMap(idleThread + PROCESS_SEARCH_LIMIT_THREAD_LOWER, mapSize, gGuest.Mm.SystemCr3, 0, &pMap);
2283  if (!INT_SUCCESS(status))
2284  {
2285  ERROR("[ERROR] IntVirtMemMap failed: 0x%08x\n", status);
2286  pMap = NULL;
2287  goto cleanup_and_exit;
2288  }
2289 
2290  // Iterate through the search limit in order to find the idle process.
2292  procOffset >= 0;
2293  procOffset -= gGuest.WordSize)
2294  {
2295  QWORD supposedCr3 = 0;
2296  QWORD aux = 0;
2297 
2298  idleProcess = gGuest.Guest64 ? *(PQWORD)(pMap + procOffset) : *(PDWORD)(pMap + procOffset);
2299  idleProcess = FIX_GUEST_POINTER(gGuest.Guest64, idleProcess);
2300 
2301  // The idle process isn't in the process list and is inside the kernel, not dynamically allocated.
2302  if (!IS_KERNEL_POINTER_WIN(gGuest.Guest64, idleProcess) ||
2303  (idleProcess < gGuest.KernelVa) || (idleProcess > (gGuest.KernelVa + gGuest.KernelSize)))
2304  {
2305  continue;
2306  }
2307 
2308  TRACE("[INFO] Found a potentially valid idle process at offset 0x%08x -> 0x%016llx\n",
2309  procOffset + PROCESS_SEARCH_LIMIT_THREAD_LOWER, idleProcess);
2310 
2311  // The way we'll do the check is we read the supposed cr3,
2312  // translate it's address with it, and see if it matches.
2313  // THEORETICALLY, it will only work with the cr3, it shouldn't work with junk.
2314 
2315  status = IntKernVirtMemRead(idleProcess + CR3_OFFSET_IN_KPROCESS, gGuest.WordSize, &supposedCr3, NULL);
2316  if (!INT_SUCCESS(status) || (0 == supposedCr3))
2317  {
2318  continue;
2319  }
2320 
2321  status = IntVirtMemRead(idleProcess + CR3_OFFSET_IN_KPROCESS, gGuest.WordSize, supposedCr3, &aux, NULL);
2322  if (!INT_SUCCESS(status) || (aux != supposedCr3))
2323  {
2324  continue;
2325  }
2326 
2327  TRACE("[INFO] Found a valid idle process cr3 at offset 0x%08x @ 0x%016llx -> 0x%016llx\n",
2328  CR3_OFFSET_IN_KPROCESS, idleProcess + CR3_OFFSET_IN_KPROCESS, supposedCr3);
2329 
2330  bFound = TRUE;
2331  gGuest.Mm.SystemCr3 = supposedCr3;
2332  break;
2333  }
2334 
2335 cleanup_and_exit:
2336  if (NULL != pMap)
2337  {
2338  IntVirtMemUnmap(&pMap);
2339  }
2340 
2341  return bFound ? INT_STATUS_SUCCESS : INT_STATUS_NOT_FOUND;
2342 }
2343 
2344 
2345 INTSTATUS
2347  void
2348  )
2363 {
2364  INTSTATUS status;
2365  DWORD activeCpuCount;
2366 
2367  WIN_INIT_SWAP *pSwp = NULL;
2368  void *swapHandle = NULL;
2369  QWORD msrValue = 0;
2370  QWORD idtValue = 0;
2371  QWORD kernelBase = 0;
2372  QWORD kernelBaseIdt = 0;
2373  DWORD ntBuildNumber = 0;
2374 
2375  // Uninitialize some things which may have been left here from the previous retry
2376  if (gWinGuest)
2377  {
2379 
2380  memzero(gWinGuest, sizeof(*gWinGuest));
2381  }
2382 
2383  gWinGuest = &gGuest._WindowsGuest;
2384 
2385  // Init this early because if anything fails here, we'll end up calling IntWinGuestCancelKernelRead
2386  // which will iterate the list, causing intro to crash if it's uninitialized.
2387  InitializeListHead(&gWinGuest->InitSwapHandles);
2388 
2389  // Initialize the #VE state. We do this here because only Windows guests are supported for #VE.
2390  status = IntVeInit();
2391  if (!INT_SUCCESS(status))
2392  {
2393  ERROR("[ERROR] IntVeInit failed: 0x%08x; will continue, but will not use #VE.\n", status);
2394  }
2395  else
2396  {
2397  TRACE("[INTRO-INIT] #VE initialized successfully!\n");
2398  }
2399 
2400  if (gGuest.Guest64)
2401  {
2402  QWORD gsBase = 0;
2403 
2404  status = IntGsRead(IG_CURRENT_VCPU, &gsBase);
2405  if (!INT_SUCCESS(status))
2406  {
2407  ERROR("[ERROR] IntGsRead failed: 0x%08x\n", status);
2408  return status;
2409  }
2410 
2411  TRACE("[INTRO-INIT] IA32_GS_BASE_MSR = 0x%016llx \n", gsBase);
2412  }
2413  else
2414  {
2415  QWORD fsBase = 0;
2416 
2417  status = IntFsRead(IG_CURRENT_VCPU, &fsBase);
2418  if (!INT_SUCCESS(status))
2419  {
2420  ERROR("[ERROR] IntFsRead failed: 0x%08x\n", status);
2421  return status;
2422  }
2423 
2424  TRACE("[INTRO-INIT] IA32_FS_BASE_MSR = 0x%016llx \n", fsBase);
2425  }
2426 
2427  activeCpuCount = IntWinGetActiveCpuCount(gGuest.CpuCount);
2428 
2429  if (gGuest.Guest64)
2430  {
2431  status = IntSyscallRead(IG_CURRENT_VCPU, NULL, &msrValue);
2432  }
2433  else
2434  {
2435  status = IntSysenterRead(IG_CURRENT_VCPU, NULL, &msrValue, NULL);
2436  }
2437 
2438  if (!INT_SUCCESS(status))
2439  {
2440  ERROR("[ERROR] Failed reading the syscall msr: 0x%08x\n", status);
2441  return status;
2442  }
2443  else if (!IS_KERNEL_POINTER_WIN(gGuest.Guest64, msrValue))
2444  {
2445  ERROR("[ERROR] SYSCALL MSR 0x%016llx is not valid!\n", msrValue);
2446 
2447  // We can actually try again, no need to say it's not supported
2449  }
2450 
2451  TRACE("[INTRO-INIT] Found SYSCALL handler @ 0x%016llx\n", msrValue);
2452 
2454  if (!INT_SUCCESS(status))
2455  {
2456  ERROR("[ERROR] IntCr3Read failed: 0x%08x\n", status);
2457  return status;
2458  }
2459 
2460  if (gGuest.KptiActive)
2461  {
2462  // With KPTI, we might get here using any cr3 so read the one from PCR
2463  status = IntWinGuestFindKernelCr3(msrValue);
2464  if (!INT_SUCCESS(status))
2465  {
2466  ERROR("[ERROR] IntWinGuestFindKernelCr3 failed: 0x%08x\n", status);
2467 
2469 
2470  // There is no point in retrying the init, as this will always fail if it managed to fail once.
2472 
2473  return status;
2474  }
2475  }
2476 
2477  TRACE("[INTRO-INIT] Found a valid cr3 at: 0x%016llx\n", gGuest.Mm.SystemCr3);
2478 
2479  // We have the IDT and cr3, now get the #PF interrupt handler
2480  status = IntIdtGetEntry(IG_CURRENT_VCPU, VECTOR_PF, &idtValue);
2481  if (!INT_SUCCESS(status))
2482  {
2483  ERROR("[ERROR] IntIdtGetEntry failed: %08x\n", status);
2484  return status;
2485  }
2486 
2487  if (IS_KERNEL_POINTER_WIN(gGuest.Guest64, idtValue))
2488  {
2489  TRACE("[INTRO-INIT] Found first interrupt handler @ 0x%016llx\n", idtValue);
2490  }
2491  else
2492  {
2493  WARNING("[WARNING] First interrupt handler @ 0x%016llx is not valid\n", idtValue);
2494  }
2495 
2496  // Find the kernel. We must have a kernel base from the msr value, and that must be valid, or
2497  // else the agent injection won't work
2498  status = IntWinGuestFindKernel(msrValue, &kernelBase);
2499  if (!INT_SUCCESS(status))
2500  {
2501  ERROR("[ERROR] IntWinGuestFindKernel failed: %08X\n", status);
2502  kernelBase = 0;
2503  }
2504  else
2505  {
2506  LOG("[INTRO-INIT] Found the base of the ntoskrnl.exe [SYSCALL] @ VA 0x%016llx\n", kernelBase);
2507  }
2508 
2509  // Find the kernel using the first interrupt handler as well.
2510  if (IS_KERNEL_POINTER_WIN(gGuest.Guest64, idtValue))
2511  {
2512  status = IntWinGuestFindKernel(idtValue, &kernelBaseIdt);
2513  if (!INT_SUCCESS(status))
2514  {
2515  ERROR("[ERROR] IntWinGuestFindKernel failed: 0x%08X\n", status);
2516 
2518 
2520 
2521  return status;
2522  }
2523 
2524  LOG("[INTRO-INIT] Found the base of the ntoskrnl.exe [IDT] @ VA 0x%016llx\n", kernelBaseIdt);
2525  }
2526 
2527  if (kernelBaseIdt && kernelBase != kernelBaseIdt)
2528  {
2529  WARNING("[WARNING] SYSCALL & IDT handlers point in different drivers (0x%016llx vs 0x%016llx).\n",
2530  kernelBase, kernelBaseIdt);
2531 
2532  kernelBase = kernelBaseIdt;
2533  }
2534 
2535  // Find the NtBuildNumber inside the kernel
2536  status = IntWinGuestFindBuildNumber(kernelBase, gGuest.Guest64, gGuest.KptiInstalled, &ntBuildNumber);
2537  if (!INT_SUCCESS(status))
2538  {
2539  ERROR("[ERROR] IntWinGuestFindBuildNumber failed: 0x%08X\n", status);
2540 
2542 
2544 
2545  return status;
2546  }
2547 
2548  gGuest.ActiveCpuCount = activeCpuCount;
2550  gGuest.KernelVa = kernelBase;
2551 
2552  // Set this temporarily
2553  gGuest.OSVersion = ntBuildNumber & 0xffff;
2554 
2555  // Set this temporarily until we find the real size
2557 
2558  // We need these early, for agent injection (BP page faults)
2559  gWinGuest->SyscallAddress = msrValue;
2560 
2561  IntWinAgentInit();
2562 
2563  // We have a kernel cr3 and the OS version so we can query the PCR
2564  // and find the idle process cr3.
2565  // We can place hooks on this one since it SHOULDN'T terminate
2566  status = IntWinGuestFindIdleCr3();
2567  if (!INT_SUCCESS(status))
2568  {
2569  ERROR("[ERROR] IntWinGuestFindIdleCr3 failed: 0x%08x\n", status);
2570 
2572 
2573  // There is no point in retrying the init, as this will always fail if it managed to fail once.
2575 
2576  return status;
2577  }
2578 
2579  TRACE("[INTRO-INIT] Found idle process CR3: 0x%016llx\n", gGuest.Mm.SystemCr3);
2580 
2581  // We only search for this here since we don't really need it before...
2582  status = IntWinGuestFindSelfMapIndex();
2583  if (!INT_SUCCESS(status))
2584  {
2585  ERROR("[ERROR] IntWinGuestFindSelfMapIndex failed: 0x%08x\n", status);
2586 
2588 
2589  // There is no point in retrying the init, as this will always fail if it managed to fail once.
2591 
2592  return status;
2593  }
2594 
2595  for (DWORD i = 0; i < gGuest.CpuCount; i++)
2596  {
2598  if (!INT_SUCCESS(status))
2599  {
2600  gGuest.VcpuArray[i].IdtBase = 0;
2601  gGuest.VcpuArray[i].IdtLimit = 0;
2602  }
2603  }
2604 
2605  status = IntWinGuestInit();
2606  if (!INT_SUCCESS(status))
2607  {
2608  ERROR("[ERROR] IntWinGuestInit failed: 0x%08x\n", status);
2609  return status;
2610  }
2611 
2612  // We are done here, now read the headers and go forward
2613  pSwp = HpAllocWithTag(sizeof(*pSwp), IC_TAG_WSWP);
2614  if (NULL == pSwp)
2615  {
2617  }
2618 
2619  pSwp->VirtualAddress = kernelBase;
2620  pSwp->Size = PAGE_SIZE;
2621 
2622  InsertTailList(&gWinGuest->InitSwapHandles, &pSwp->Link);
2623 
2624  status = IntSwapMemReadData(0,
2625  kernelBase,
2626  PAGE_SIZE,
2628  pSwp,
2629  0,
2631  NULL,
2632  &swapHandle);
2633  if (!INT_SUCCESS(status))
2634  {
2635  ERROR("[ERROR] Failed reading the kernel headers: 0x%08x\n", status);
2636  return status;
2637  }
2638 
2639  // The callback will be called async, save the handle in case an uninit will come
2640  if (NULL != swapHandle)
2641  {
2642  pSwp->SwapHandle = swapHandle;
2643  }
2644 
2645  return INT_STATUS_SUCCESS;
2646 }
2647 
2648 
2649 INTSTATUS
2651  _In_ DWORD FullStringSize,
2652  _In_ DWORD VersionStringSize,
2653  _Out_ CHAR *FullString,
2654  _Out_ CHAR *VersionString
2655  )
2668 {
2669  int count;
2670 
2671  if (NULL == gWinGuest->NtBuildLabString)
2672  {
2673  return INT_STATUS_NOT_READY;
2674  }
2675 
2676  if (NULL == gWinGuest->VersionString)
2677  {
2678  return INT_STATUS_NOT_READY;
2679  }
2680 
2681  if (NULL == gWinGuest->ServerVersionString)
2682  {
2683  return INT_STATUS_NOT_READY;
2684  }
2685 
2686  if (winProductTypeNotYetLoaded == gWinGuest->ProductType)
2687  {
2688  return INT_STATUS_NOT_READY;
2689  }
2690 
2691  if (strlen(gWinGuest->NtBuildLabString) >= FullStringSize)
2692  {
2694  }
2695 
2696  strcpy(FullString, gWinGuest->NtBuildLabString);
2697 
2698  if (gWinGuest->ProductType == winProductTypeServer && strlen(gWinGuest->ServerVersionString) != 0)
2699  {
2700  count = snprintf(VersionString, VersionStringSize, "%s %s", gWinGuest->ServerVersionString,
2701  strcasestr(FullString, "lts") ? "ltsb" : "");
2702  }
2703  else
2704  {
2705  const char *appendix = gWinGuest->ProductType == winProductTypeUnknown ? "(could not determine if server)" :
2706  gWinGuest->ProductType == winProductTypeServer ? "(possible server)" : "";
2707 
2708  count = snprintf(VersionString, VersionStringSize, "%s %s %s", gWinGuest->VersionString,
2709  strcasestr(FullString, "lts") ? "ltsb" : "", appendix);
2710  }
2711 
2712  if (count < 0)
2713  {
2715  }
2716 
2717  if ((DWORD)count >= VersionStringSize)
2718  {
2720  }
2721 
2722  return INT_STATUS_SUCCESS;
2723 }
#define IMAGE_SCN_MEM_EXECUTE
Definition: winpe.h:472
static INTSTATUS IntWinGuestFetchProductType(WIN_PRODUCT_TYPE *ProductType)
Obtains the Windows product type.
Definition: winguest.c:958
static INTSTATUS IntWinGuestFindSelfMapIndex(void)
Finds the self map index.
Definition: winguest.c:369
#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:1932
UINT16 NumberOfSections
Definition: winpe.h:65
struct _IMAGE_NT_HEADERS64 * PIMAGE_NT_HEADERS64
Advanced server.
Definition: winguest.h:799
QWORD PhysicalAddress
The physical address to which VirtualAddress translates to.
Definition: introcore.h:107
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:707
#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:90
#define INTRO_OPT_VE
Enable the Virtualization exception page table access pre-filtering agent (64-bit Windows only)...
Definition: intro_types.h:475
#define IC_TAG_CAMI
Live update allocations.
Definition: memtags.h:124
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:2435
QWORD MmPfnDatabase
Guest virtual address of the PFN data base.
Definition: winguest.h:837
INTSTATUS IntWinGuestActivateProtection(void)
Activates the protection for a Windows guest.
Definition: winguest.c:737
static INTSTATUS IntWinGuestFindKernelObjects(void)
Searches for kernel objects.
Definition: winguest.c:311
uint8_t BYTE
Definition: intro_types.h:47
#define CR0_PG
Definition: processor.h:40
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:815
INTSTATUS IntPeFindKernelExport(const char *Name, QWORD *ExportGva)
Find an export inside the NT kernel image.
Definition: winpe.c:1748
QWORD SystemCr3
The Cr3 used to map the kernel.
Definition: guests.h:211
#define INT_STATUS_SUCCESS
Definition: introstatus.h:54
INTSTATUS IntFsRead(DWORD CpuNumber, QWORD *FsValue)
Reads the IA32_FS_BASE guest MSR.
Definition: introcpu.c:252
#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:2270
DWORD KernelSize
The size of the kernel.
Definition: guests.h:284
#define KERNEL_SEARCH_LIMIT
The maximum size of the area of memory in which the kernel base is searched for.
Definition: winguest.c:43
INTSTATUS IntWinGuestNew(void)
Starts the initialization and protection process for a new Windows guest.
Definition: winguest.c:2346
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:110
Information not yet loaded.
Definition: winguest.h:797
The product type is unknown.
Definition: winguest.h:804
void IntWinHalUninit(void)
Frees any resources held by gHalData and removes all the HAL protections.
Definition: winhal.c:2479
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:58
#define INT_SUCCESS(Status)
Definition: introstatus.h:42
QWORD ExFreePoolWithTag
Guest virtual address of the ExFreePoolWithTag kernel function.
Definition: winguest.h:814
INTSTATUS IntWinSudProtectIntegrity(void)
Initializes the SharedUserData integrity protection.
Definition: winsud.c:1099
LIST_ENTRY Link
Link inside the WINDOWS_GUEST.InitSwapHandles list.
Definition: winguest.h:867
A critical structure was not found inside the guest kernel.
Definition: intro_types.h:2441
static INTSTATUS IntWinGuestFindBuildNumber(QWORD KernelGva, BOOLEAN Guest64, BOOLEAN IsKptiInstalled, DWORD *NtBuildNumber)
Finds the NtBuildNumber kernel variable.
Definition: winguest.c:1631
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:297
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:833
int32_t INT32
Definition: intro_types.h:44
static INTSTATUS IntWinGuestFindKernelCr3(QWORD Syscall)
Searches for the kernel Cr3.
Definition: winguest.c:2067
#define INT_STATUS_NOT_NEEDED_HINT
Definition: introstatus.h:317
#define ERROR(fmt,...)
Definition: glue.h:62
#define ONE_MEGABYTE
Definition: introdefs.h:90
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:942
#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:1429
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:2437
INTSTATUS IntMsrSyscallProtect(void)
Enable protection for all SYSCALL and SYSENTER MSRs.
DWORD OSVersion
Os version.
Definition: guests.h:281
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:819
#define WIN_SHARED_USER_DATA_OFFSET_PRODUCT
INTSTATUS IntWinIntObjProtect(void)
Protects the interrupt objects which are present in the KPRCB&#39;s InterruptObject array.
Definition: winintobj.c:473
QWORD IntroActiveEventId
The event ID on which introcore became active.
Definition: guests.h:381
QWORD ExAllocatePoolWithTag
Guest virtual address of the ExAllocatePoolWithTag kernel function.
Definition: winguest.h:813
#define TRFLG_NONE
No special options.
Definition: introcore.h:82
#define INTRO_OPT_PROT_KM_SUD_INTEGRITY
Enable integrity checks over various SharedUserData fields, as well as the zero-filled zone after the...
Definition: intro_types.h:528
PCHAR ServerVersionString
A NULL terminated string containing Windows server version information.
Definition: winguest.h:831
void IntWinGuestUninit(void)
Uninits a Windows guest.
Definition: winguest.c:671
WINDOWS_GUEST _WindowsGuest
Linux specific information. Valid when OSType is introGuestWindows.
Definition: guests.h:420
PVCPU_STATE VcpuArray
Array of the VCPUs assigned to this guest. The index in this array matches the VCPU number...
Definition: guests.h:372
BOOLEAN IntWinGuestIsIncreasedUserVa(void)
Check if the guest has an increased user address space.
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:537
#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:840
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:412
INTRO_GUEST_TYPE OSType
The type of the guest.
Definition: guests.h:278
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:305
#define WIN_SHARED_USER_DATA_PTR
The address where the SharedUserData is mapped in the Windows kernel.
Definition: winsud.h:11
static DWORD IntWinGetActiveCpuCount(DWORD CpuCount)
Gets the number of active CPUs used by the guest.
Definition: winguest.c:2011
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
uint32_t * PDWORD
Definition: intro_types.h:49
INTSTATUS IntGdtrProtect(void)
Enable GDTR protection.
BOOLEAN KptiActive
True if KPTI is enabled on this guest, False if it is not.
Definition: guests.h:291
QWORD PsCreateSystemThread
Guest virtual address of the PsCreateSystemThread kernel function.
Definition: winguest.h:812
#define INTRO_OPT_PROT_KM_MSR_SYSCALL
Definition: intro_types.h:427
Exposes the functions used to provide Windows Threads related support.
INTSTATUS IntWinSudProtectSudExec(void)
Protects SharedUserData against executions by establishing an EPT hook on it.
Definition: winsud.c:661
DWORD MappingsCount
The number of entries inside the MappingsTrace and MappingsEntries arrays.
Definition: introcore.h:123
A kernel export was not found.
Definition: intro_types.h:2440
The object was detected after it was created.
Definition: winguest.h:150
#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:871
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:55
static INTSTATUS IntWinGuestFinishInit(void)
Finalizes the Windows initialization once the entire kernel is read.
Definition: winguest.c:1008
INTSTATUS IntFindKernelPcr(DWORD CpuNumber, QWORD *Pcr)
Finds the address of the Windows kernel _KPCR.
Definition: introcpu.c:1116
QWORD PsActiveProcessHead
Guest virtual address of the PsActiveProcessHead kernel variable.
Definition: winguest.h:835
QWORD PsLoadedModuleList
Guest virtual address of the PsLoadedModuleList kernel variable.
Definition: winguest.h:836
#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:2438
#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:69
WIN_PRODUCT_TYPE
The type of the Windows OS.
Definition: winguest.h:795
uint8_t * PBYTE
Definition: intro_types.h:47
static BOOLEAN RemoveEntryList(LIST_ENTRY *Entry)
Definition: introlists.h:87
#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:290
unsigned long long QWORD
Definition: intro_types.h:53
QWORD Current
The currently used options.
Definition: guests.h:236
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:1572
WINDOWS_GUEST * gWinGuest
Global variable holding the state of a Windows guest.
Definition: winguest.c:37
INTSTATUS IntVeInit(void)
Initialize the VE system.
Definition: vecore.c:2493
#define INTRO_OPT_PROT_KM_IDTR
Enable interrupt descriptor-table registers protection.
Definition: intro_types.h:429
QWORD IdtBase
Original IDT base.
Definition: guests.h:110
#define INTRO_OPT_PROT_KM_INTERRUPT_OBJ
Enable protection against modifications of interrupt objects from KPRCB&#39;s InterruptObject.
Definition: intro_types.h:531
#define TRUE
Definition: intro_types.h:30
QWORD Ssdt
Guest virtual address of the SSDT structure inside the kernel.
Definition: winguest.h:818
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:293
TIMER_FRIENDLY void IntDumpBuffer(const void *Buffer, QWORD Gva, DWORD Length, DWORD RowLength, DWORD ElementLength, BOOLEAN LogHeader, BOOLEAN DumpAscii)
This function dumps a given buffer in a user friendly format.
Definition: dumper.c:48
#define HpFreeAndNullWithTag(Add, Tag)
Definition: glue.h:517
#define TRACE(fmt,...)
Definition: glue.h:58
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:431
#define INT_STATUS_INVALID_DATA_STATE
Definition: introstatus.h:183
#define INT_STATUS_INVALID_INTERNAL_STATE
Definition: introstatus.h:272
QWORD KernelVa
The guest virtual address at which the kernel image.
Definition: guests.h:283
INTSTATUS IntWinProcIterateGuestProcesses(PFUNC_IterateListCallback Callback, QWORD Aux)
Iterates the in-guest process list and calls Callback for each entry.
Definition: winprocesshp.c:501
INT32 e_lfanew
Definition: winpe.h:58
union _IMAGE_SECTION_HEADER::@214 Misc
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:367
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
32-bit paging with PAE
Definition: introcore.h:70
QWORD VirtualAddress
The guest virtual address that will be read.
Definition: winguest.h:870
#define SWAPMEM_FLAG_ASYNC_CALL
Definition: swapmem.h:12
void IntWinGuestCancelKernelRead(void)
Cancels the kernel read.
Definition: winguest.c:607
#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:1197
DWORD SelfMapIndex
The self map index.
Definition: guests.h:220
4-level paging
Definition: introcore.h:71
static void InitializeListHead(LIST_ENTRY *ListHead)
Definition: introlists.h:69
DWORD CpuCount
The number of logical CPUs.
Definition: guests.h:279
#define PAGE_SIZE
Definition: common.h:70
INTSTATUS IntWinApiHookAll(void)
Iterates through all hookable APIs and sets requested hooks.
Definition: winapi.c:229
#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:2650
#define WIN_KM_FIELD(Structure, Field)
Macro used to access kernel mode fields inside the WIN_OPAQUE_FIELDS structure.
Definition: winguest.h:740
uint32_t DWORD
Definition: intro_types.h:49
UINT32 VirtualSize
Definition: winpe.h:83
DWORD KernelBufferSize
The size of the KernelBuffer.
Definition: winguest.h:852
#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:3940
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:827
INTSTATUS IntSyscallRead(DWORD CpuNumber, QWORD *SysStar, QWORD *SysLstar)
Queries the IA32_STAR, and IA32_LSTAR guest MSRs.
Definition: introcpu.c:635
#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:61
Holds information about a Windows guest.
Definition: winguest.h:810
#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:227
INTSTATUS IntWinGuestInit(void)
Initializes a new Windows guest.
Definition: winguest.c:641
INTSTATUS IntWinProcAdd(QWORD Eprocess, QWORD Aux)
Adds a new process to the Introcore list of processes.
Definition: winprocesshp.c:396
MM Mm
Guest memory information, such as paging mode, system Cr3 value, etc.
Definition: guests.h:374
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:258
QWORD KeServiceDescriptorTable
Guest virtual address of the KeServiceDescriptorTable variable.
Definition: winguest.h:817
GUEST_STATE gGuest
The current guest state.
Definition: guests.c:50
INTSTATUS IntWinIdtProtectAll(void)
Activates the IDT protection for all the guest CPUs.
Definition: winidt.c:559
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
LIST_HEAD InitSwapHandles
A list of swap handles used to read KernelBuffer.
Definition: winguest.h:855
DWORD RemainingSections
The number of kernel sections not yet read into KernelBuffer.
Definition: winguest.h:854
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 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:292
IMAGE_FILE_HEADER FileHeader
Definition: winpe.h:227
The initialization swap handle.
Definition: winguest.h:865
PAGING_MODE Mode
The paging mode used by the guest.
Definition: guests.h:221
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
Workstation.
Definition: winguest.h:798
PCHAR NtBuildLabString
Definition: winguest.h:823
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:851
KERNEL_DRIVER * KernelDriver
Points to the driver object that describes the kernel image.
Definition: guests.h:385
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
#define INTRO_OPT_PROT_KM_SUD_EXEC
Enable protection against executions on SharedUserData.
Definition: intro_types.h:515
VCPU_STATE * gVcpu
The state of the current VCPU.
Definition: guests.c:59
UINT8 Name[IMAGE_SIZEOF_SHORT_NAME]
Definition: winpe.h:79
DWORD NtBuildNumberValue
The value of the NtBuildNumber kernel variable.
Definition: winguest.h:816
64-bit selector.
Definition: glueiface.h:188
INTSTATUS IntWinGuestFindIdleCr3(void)
Searches the Cr3 used by the idle process.
Definition: winguest.c:2236
DWORD ActiveCpuCount
The number of CPUs actually used by the guest.
Definition: guests.h:280
#define INTRO_OPT_PROT_KM_CR4
Enable CR4.SMEP and CR4.SMAP protection.
Definition: intro_types.h:426
DWORD SizeOfImage
Size of the image.
Definition: winpe.h:599
#define INT_STATUS_NOT_READY
Definition: introstatus.h:308
BOOLEAN Image64Bit
True if the image is 64 bit.
Definition: winpe.h:596
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:328
static INTSTATUS IntWinGuestReadKernel(PBYTE KernelHeaders)
Reads the whole kernel image in memory, including swapped-out sections.
Definition: winguest.c:1286
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
void * SwapHandle
The actual swap handle returned by IntSwapMemRead.
Definition: winguest.h:868
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 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:1770
INTSTATUS IntHandleBreakpoint(void *GuestHandle, QWORD GuestPhysicalAddress, DWORD CpuNumber)
Handle guest breakpoints.
Definition: callbacks.c:2734
INTRO_PROT_OPTIONS CoreOptions
The activation and protection options for this guest.
Definition: guests.h:271
UINT16 SizeOfOptionalHeader
Definition: winpe.h:69
INTSTATUS IntCamiGetWinSupportedList(BOOLEAN KptiInstalled, BOOLEAN Guest64, DWORD *NtBuildNumberList, DWORD *Count)
Return a list of supported Windows NtBuildNumbers.
#define INTRO_OPT_PROT_KM_GDTR
Enable global descriptor-table registers protection.
Definition: intro_types.h:440
static INTSTATUS IntWinGuestFindKernelObjectsInternal(void)
Finds all the objects of interest from the Windows kernel.
Definition: winguest.c:73
#define INT_STATUS_INVALID_DATA_SIZE
Definition: introstatus.h:142
#define FALSE
Definition: intro_types.h:34
#define ALIGN_DOWN(x, a)
Definition: introdefs.h:165
#define INT_STATUS_INSUFFICIENT_RESOURCES
Definition: introstatus.h:281