Bitdefender Hypervisor Memory Introspection
winprocesshp.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 Bitdefender
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 #include "winprocesshp.h"
6 #include "guests.h"
7 #include "vasmonitor.h"
8 #include "winummodule.h"
9 
20 
21 void
31 {
32  InitializeListHead(&gWinProcesses);
33 
34  RbPreinit(&gWinProcTreeCr3);
36 
37  RbPreinit(&gWinProcTreeUserCr3);
39 
40  RbPreinit(&gWinProcTreeEprocess);
42 }
43 
44 
45 void
47  _In_ WIN_PROCESS_OBJECT *Process
48  )
57 {
58  InsertTailList(&gWinProcesses, &Process->Link);
59 
60  // Note: we cannot have errors here, because:
61  // 1. we supply valid arguments (so no STATUS_INVALID_PARAMETER can occur)
62  // 2. at the beginning of the function, we check for duplicates by searching for both the CR3 and EPROCESS (so
63  // we cannot have STATUS_KEY_ALREADY_PRESENT either).
64  RbInsertNode(&gWinProcTreeCr3, &Process->NodeCr3);
65 
66  RbInsertNode(&gWinProcTreeUserCr3, &Process->NodeUserCr3);
67 
68  RbInsertNode(&gWinProcTreeEprocess, &Process->NodeEproc);
69 }
70 
71 
72 void
74  _In_ WIN_PROCESS_OBJECT *Process
75  )
84 {
85  RemoveEntryList(&Process->Link);
86 
87  RbDeleteNode(&gWinProcTreeCr3, &Process->NodeCr3);
88 
89  RbDeleteNode(&gWinProcTreeUserCr3, &Process->NodeUserCr3);
90 
91  RbDeleteNode(&gWinProcTreeEprocess, &Process->NodeEproc);
92 }
93 
94 
97  _In_ QWORD Eprocess
98  )
106 {
107  INTSTATUS status;
108  WIN_PROCESS_OBJECT target;
109  RBNODE *result;
110 
111  target.EprocessAddress = Eprocess;
112 
113  status = RbLookupNode(&gWinProcTreeEprocess, &target.NodeEproc, &result);
114  if (INT_SUCCESS(status) && result)
115  {
116  return CONTAINING_RECORD(result, WIN_PROCESS_OBJECT, NodeEproc);
117  }
118  else
119  {
120  return NULL;
121  }
122 }
123 
124 
127  _In_ DWORD Pid
128  )
136 {
137  LIST_ENTRY *list;
138 
139  list = gWinProcesses.Flink;
140  while (list != &gWinProcesses)
141  {
143 
144  if (pProc->Pid == Pid)
145  {
146  return pProc;
147  }
148 
149  list = list->Flink;
150  }
151 
152  return NULL;
153 }
154 
155 
158  _In_ CHAR const *Name,
159  _In_ BOOLEAN MustBeSystem
160  )
169 {
170  if (NULL == Name)
171  {
172  return NULL;
173  }
174 
175  for (LIST_ENTRY *entry = gWinProcesses.Flink; entry != &gWinProcesses; entry = entry->Flink)
176  {
178 
179  if (MustBeSystem && !pProc->SystemProcess)
180  {
181  continue;
182  }
183 
184  if (!strcasecmp(pProc->Name, Name))
185  {
186  return pProc;
187  }
188  }
189 
190  return NULL;
191 }
192 
193 
196  _In_ QWORD Cr3
197  )
205 {
206  INTSTATUS status;
207  WIN_PROCESS_OBJECT target;
208  RBNODE* result;
209 
210  target.Cr3 = Cr3;
211 
212  status = RbLookupNode(&gWinProcTreeCr3, &target.NodeCr3, &result);
213  if (INT_SUCCESS(status) && result)
214  {
215  return CONTAINING_RECORD(result, WIN_PROCESS_OBJECT, NodeCr3);
216  }
217  else
218  {
219  return NULL;
220  }
221 }
222 
223 
226  _In_ QWORD Cr3
227  )
238 {
239  INTSTATUS status;
240  WIN_PROCESS_OBJECT target;
241  RBNODE* result;
242 
243  target.UserCr3 = Cr3;
244 
245  status = RbLookupNode(&gWinProcTreeUserCr3, &target.NodeUserCr3, &result);
246  if (INT_SUCCESS(status) && result)
247  {
248  return CONTAINING_RECORD(result, WIN_PROCESS_OBJECT, NodeUserCr3);
249  }
250  else
251  {
252  return NULL;
253  }
254 }
255 
256 
257 INTSTATUS
259  _In_ QWORD Gva
260  )
280 {
281  INTSTATUS status;
282  QWORD flink, blink, prevFlink, nextBlink, pid, type;
283  CHAR imageName[IMAGE_BASE_NAME_LEN];
284 
285 #define IS_INVALID_PTR(ptr) !IS_KERNEL_POINTER_WIN(gGuest.Guest64, (ptr)) || \
286  (gGuest.Guest64 && 0xffffffff00000000 == ((ptr) & 0xffffffff00000000)) || \
287  (!gGuest.Guest64 && 0xffffffff == (ptr)) \
288 
289  // System EPROC is not in kernel
290  if (gGuest.KernelVa != 0 &&
291  gGuest.KernelSize != 0 &&
292  Gva >= gGuest.KernelVa &&
293  Gva <= gGuest.KernelVa + gGuest.KernelSize)
294  {
296  }
297 
298  flink = 0;
299  blink = 0;
300  prevFlink = 0;
301  nextBlink = 0;
302  pid = 0;
303  type = 0;
304 
305  status = IntKernVirtMemFetchWordSize(Gva, &flink);
306  if (!INT_SUCCESS(status))
307  {
308  return status;
309  }
310 
311  if (IS_INVALID_PTR(flink))
312  {
314  }
315 
316  status = IntKernVirtMemFetchWordSize(Gva + gGuest.WordSize, &blink);
317  if (!INT_SUCCESS(status))
318  {
319  return status;
320  }
321 
322  if (IS_INVALID_PTR(blink))
323  {
325  }
326 
327  // Make sure flink->blink points to us.
328  status = IntKernVirtMemFetchWordSize(flink + gGuest.WordSize, &nextBlink);
329  if (!INT_SUCCESS(status))
330  {
331  return status;
332  }
333 
334  if (nextBlink != Gva)
335  {
337  }
338 
339  // Make sure blink->flink points to us.
340  status = IntKernVirtMemFetchWordSize(blink, &prevFlink);
341  if (!INT_SUCCESS(status))
342  {
343  return status;
344  }
345 
346  if (prevFlink != Gva)
347  {
349  }
350 
351  status = IntKernVirtMemFetchWordSize(Gva - WIN_KM_FIELD(Process, ListEntry) + WIN_KM_FIELD(Process, Id), &pid);
352  if (!INT_SUCCESS(status))
353  {
354  return status;
355  }
356 
357  if (pid != 4)
358  {
360  }
361 
362  status = IntKernVirtMemFetchWordSize(Gva - WIN_KM_FIELD(Process, ListEntry), &type);
363  if (!INT_SUCCESS(status))
364  {
365  return status;
366  }
367 
368  if ((BYTE)type != 3)
369  {
371  }
372 
373  // Process name must be 'System'
374  status = IntKernVirtMemRead(Gva - WIN_KM_FIELD(Process, ListEntry) +
375  WIN_KM_FIELD(Process, Name),
376  sizeof(imageName),
377  imageName,
378  NULL);
379  if (!INT_SUCCESS(status))
380  {
381  return status;
382  }
383 
384  imageName[sizeof(imageName) - 1] = 0;
385 
386  if (0 != strcasecmp(imageName, "system"))
387  {
389  }
390 
391  return INT_STATUS_SUCCESS;
392 }
393 
394 
395 INTSTATUS
397  _In_ QWORD Eprocess,
398  _In_ QWORD Aux
399  )
417 {
418  INTSTATUS status;
419  QWORD cr3, parentEproc;
420  DWORD pid;
421  DWORD flags;
422  WIN_PROCESS_OBJECT *pProcObj, *pParent;
423  BYTE *pEproc = NULL;
424 
426 
427  status = IntWinProcMapEprocess(Eprocess, &pEproc);
428  if (NULL == pEproc || !INT_SUCCESS(status))
429  {
430  ERROR("[ERROR] IntWinProcMapEprocess failed: 0x%08x\n", status);
431  return status;
432  }
433 
434  pid = *(DWORD *)(pEproc + WIN_KM_FIELD(Process, Id));
435 
436  if (gGuest.Guest64)
437  {
438  cr3 = *(QWORD *)(pEproc + WIN_KM_FIELD(Process, Cr3));
439  }
440  else
441  {
442  cr3 = *(DWORD *)(pEproc + WIN_KM_FIELD(Process, Cr3));
443  }
444 
445  flags = *(DWORD *)(pEproc + WIN_KM_FIELD(Process, Flags));
446  if (0 != (flags & (
447  WIN_KM_FIELD(EprocessFlags, Delete)
448  | WIN_KM_FIELD(EprocessFlags, Exiting)
449  | WIN_KM_FIELD(EprocessFlags, VmDeleted))))
450  {
451  LOG("[EPROCESS] Skipping process %llx which is being marked as deleted (flags = 0x%x)...\n", Eprocess, flags);
453  goto cleanup_and_exit;
454  }
455  else if (0 == (flags & WIN_KM_FIELD(EprocessFlags, HasAddrSpace)))
456  {
457  LOG("[EPROCESS] Skipping process %llx which does not have an address space (flags = 0x%x)...\n",
458  Eprocess, flags);
460  goto cleanup_and_exit;
461  }
462 
463  if (NULL != IntWinProcFindObjectByEprocess(Eprocess))
464  {
465  LOG("[EPROCESS] The list seems to have a circular loop on process 0x%016llx, will break iteration...\n",
466  Eprocess);
468  goto cleanup_and_exit;
469  }
470 
471  // Find the associated parent process.
472  pParent = IntWinProcFindObjectByPid(*(DWORD *)(pEproc + WIN_KM_FIELD(Process, ParentPid)));
473  if (NULL != pParent)
474  {
475  parentEproc = pParent->EprocessAddress;
476  }
477  else
478  {
479  parentEproc = 0;
480  }
481 
482  status = IntWinProcCreateProcessObject(&pProcObj, Eprocess, pEproc, parentEproc, 0, cr3, pid, TRUE);
483  if (!INT_SUCCESS(status))
484  {
485  ERROR("[ERROR] IntWinProcCreateProcessObject failed for EPROC 0x%016llx with flags 0x%x: 0x%08x\n",
486  Eprocess, flags, status);
487  goto cleanup_and_exit;
488  }
489 
490 cleanup_and_exit:
491  if (NULL != pEproc)
492  {
493  IntVirtMemUnmap(&pEproc);
494  }
495 
496  return status;
497 }
498 
499 
500 INTSTATUS
503  _In_ QWORD Aux
504  )
519 {
520 #define PROCESSES_MAX_COUNT 65535
521  INTSTATUS status;
522  QWORD currentProcess = 0, count = 0, nextProcess = 0;
523 
524  if (Callback == NULL)
525  {
527  }
528 
529  currentProcess = gWinGuest->PsActiveProcessHead;
530  if (currentProcess == 0)
531  {
533  }
534 
535  status = IntKernVirtMemRead(currentProcess, gGuest.WordSize, &currentProcess, NULL);
536  if (!INT_SUCCESS(status))
537  {
538  ERROR("[ERROR] Failed getting the Flink value of EPROCESS @ 0x%016llx: 0x%08x\n", currentProcess, status);
539  return status;
540  }
541 
542  status = IntKernVirtMemRead(currentProcess, gGuest.WordSize, &nextProcess, NULL);
543  if (!INT_SUCCESS(status))
544  {
545  ERROR("[ERROR] Failed getting the Flink value of EPROCESS @ 0x%016llx: 0x%08x\n", currentProcess, status);
546  return status;
547  }
548 
549  status = INT_STATUS_SUCCESS;
550  while ((currentProcess != gWinGuest->PsActiveProcessHead) && (count++ < PROCESSES_MAX_COUNT))
551  {
552  status = Callback((currentProcess - WIN_KM_FIELD(Process, ListEntry)), Aux);
553  if (INT_STATUS_BREAK_ITERATION == status)
554  {
555  status = INT_STATUS_SUCCESS;
556  break;
557  }
558  if (INT_STATUS_NOT_NEEDED_HINT == status)
559  {
560  // The process was not actually added in our list, so decrease the count so that we don't
561  // miss the actually non-deleted processes.
562  count--;
563  }
564 
565  status = IntKernVirtMemRead(currentProcess, gGuest.WordSize, &currentProcess, NULL);
566  if (!INT_SUCCESS(status))
567  {
568  ERROR("[ERROR] Failed getting the Flink value of EPROCESS @ 0x%016llx: 0x%08x\n", currentProcess, status);
569  break;
570  }
571 
572  // Consider the nextProcess variable as the hare in the tortoise and hare algorithm. We should do f(f(hare)),
573  // so read the next eprocess of the next eprocess, keeping the notion that the "nextProcess" moves through
574  // the list at a speed doubled of the "currentProcess"'s speed.
575  status = IntKernVirtMemRead(nextProcess, gGuest.WordSize, &nextProcess, NULL);
576  if (!INT_SUCCESS(status))
577  {
578  ERROR("[ERROR] Failed getting the Flink value of EPROCESS @ 0x%016llx: 0x%08x\n", nextProcess, status);
579  break;
580  }
581 
582  status = IntKernVirtMemRead(nextProcess, gGuest.WordSize, &nextProcess, NULL);
583  if (!INT_SUCCESS(status))
584  {
585  ERROR("[ERROR] Failed getting the Flink value of EPROCESS @ 0x%016llx: 0x%08x\n", nextProcess, status);
586  break;
587  }
588 
589  // The hare and the tortoise are in the same position - a cycle was detected in the list! Verify if the cycle
590  // is not the list head (since the linked lists will always be cyclical with respect to the list head). Note
591  // that we don't actually need to find the first repetition in the cycle, nor the period of the cycle, we only
592  // need to find whether it is a cycle, so the condition that the hare and the tortoise are in the same position
593  // is enough.
594  if (nextProcess == currentProcess && currentProcess != gWinGuest->PsActiveProcessHead)
595  {
596  ERROR("[ERROR] The guest linked list seem to be cyclical, a cycle has been detected on 0x%016llx\n",
597  currentProcess);
599  break;
600  }
601  }
602 
603  if (count >= PROCESSES_MAX_COUNT)
604  {
605  status = INT_STATUS_OUT_OF_RANGE;
606  }
607 
608  return status;
609 
610 #undef PROCESSES_MAX_COUNT
611 }
612 
613 
614 INTSTATUS
616  _In_ QWORD Eprocess,
618  )
631 {
632  if (0 == Eprocess)
633  {
635  }
636 
637  memset(Name, 0, IMAGE_BASE_NAME_LEN);
638 
639  _Analysis_assume_(Name[IMAGE_BASE_NAME_LEN - 1] = NULL);
640  return IntKernVirtMemRead(Eprocess + WIN_KM_FIELD(Process, Name),
642  Name,
643  NULL);
644 }
645 
646 
647 INTSTATUS
649  _In_ QWORD Eprocess,
651  )
662 {
663  LIST_ENTRY *list;
664 
665  list = gWinProcesses.Flink;
666  while (list != &gWinProcesses)
667  {
669  list = list->Flink;
670 
671  if (pProc->EprocessAddress == Eprocess)
672  {
673  memcpy(Name, pProc->Name, IMAGE_BASE_NAME_LEN);
674  _Analysis_assume_(Name[IMAGE_BASE_NAME_LEN - 1] = NULL);
675 
676  return INT_STATUS_SUCCESS;
677  }
678  }
679 
680  return INT_STATUS_NOT_FOUND;
681 }
682 
683 
684 BOOLEAN
686  void
687  )
696 {
697  INTSTATUS status;
698  size_t totalHeapSize, freeHeapSize;
699 
700  // Before actually protecting the process, make sure enough heap is available. If there's less than 30%, we will NOT
701  // protect anymore processes!!!
702  status = IntQueryHeapSize(&totalHeapSize, &freeHeapSize);
703  if (!INT_SUCCESS(status))
704  {
705  ERROR("[ERROR] IntQueryHeapSize failed: 0x%08x, will assume 'infinite' heap.\n", status);
706  totalHeapSize = 0xFFFFFFFFFFFFFFFF;
707  freeHeapSize = 0xFFFFFFFFFFFFFFFF;
708  }
709 
710  TRACE("[INFO] Heap stats: total size: %zu bytes, free: %zu bytes\n", totalHeapSize, freeHeapSize);
711 
712  // Make sure more than minimum of MIN_HEAP_SIZE_PERCENT of heap is available.
713  if (freeHeapSize < (totalHeapSize * MIN_HEAP_SIZE_PERCENT) / 100)
714  {
715  return FALSE;
716  }
717 
718  return TRUE;
719 }
720 
721 
724  _Inout_ RBNODE *Node
725  )
729 {
731 }
732 
733 
736  _In_ RBNODE const *Left,
737  _In_ RBNODE const *Right)
738 {
739  WIN_PROCESS_OBJECT const *p1 = CONTAINING_RECORD(Left, WIN_PROCESS_OBJECT, NodeCr3);
740  WIN_PROCESS_OBJECT const *p2 = CONTAINING_RECORD(Right, WIN_PROCESS_OBJECT, NodeCr3);
741  QWORD cr3p1, cr3p2;
742 
743  cr3p1 = p1->Cr3;
744  cr3p2 = p2->Cr3;
745 
746  if (cr3p1 < cr3p2)
747  {
748  return -1;
749  }
750  else if (cr3p1 > cr3p2)
751  {
752  return 1;
753  }
754  else
755  {
756  return 0;
757  }
758 }
759 
760 
763  _In_ RBNODE const *Left,
764  _In_ RBNODE const *Right)
765 {
767  PWIN_PROCESS_OBJECT p2 = CONTAINING_RECORD(Right, WIN_PROCESS_OBJECT, NodeUserCr3);
768  QWORD cr3p1, cr3p2;
769 
770  cr3p1 = p1->UserCr3;
771  cr3p2 = p2->UserCr3;
772 
773  if (cr3p1 < cr3p2)
774  {
775  return -1;
776  }
777  else if (cr3p1 > cr3p2)
778  {
779  return 1;
780  }
781  else
782  {
783  return 0;
784  }
785 }
786 
787 
790  _In_ RBNODE const *Left,
791  _In_ RBNODE const *Right)
792 {
795 
796  if (p1->EprocessAddress < p2->EprocessAddress)
797  {
798  return -1;
799  }
800  else if (p1->EprocessAddress > p2->EprocessAddress)
801  {
802  return 1;
803  }
804  else
805  {
806  return 0;
807  }
808 }
809 
810 
811 INTSTATUS
813  _Out_writes_bytes_(Length) PCHAR CommandLine,
814  _In_ DWORD Length
815  )
828 {
829  PCHAR cmd = CommandLine;
830 
831  PLIST_ENTRY list = gWinProcesses.Flink;
832  while (list != &gWinProcesses)
833  {
835  INT32 len;
836 
837  list = list->Flink;
838 
839  if (!pProc->IsAgent)
840  {
841  continue;
842  }
843 
844  len = snprintf(cmd, Length, "%s %u ", pProc->Name, pProc->Pid);
845  if (len < 0)
846  {
848  }
849 
850  if ((DWORD)len >= Length)
851  {
853  }
854 
855  Length -= len;
856  cmd += len;
857  }
858 
859  return INT_STATUS_SUCCESS;
860 }
861 
862 
863 void
865  void
866  )
870 {
871  INTSTATUS status;
872 
873  LIST_ENTRY *list = gWinProcesses.Flink;
874  while (list != &gWinProcesses)
875  {
876  PWIN_PROCESS_OBJECT pProc;
877  CHAR parentName[IMAGE_BASE_NAME_LEN];
878  CHAR realParentName[IMAGE_BASE_NAME_LEN];
879 
880  memset(parentName, 0, sizeof(parentName));
881  memset(realParentName, 0, sizeof(realParentName));
882 
883  pProc = CONTAINING_RECORD(list, WIN_PROCESS_OBJECT, Link);
884 
885  list = list->Flink;
886 
887  if (0 != pProc->ParentEprocess)
888  {
889  status = IntWinProcGetNameFromInternalEprocess(pProc->ParentEprocess, parentName);
890  if (INT_STATUS_NOT_FOUND == status)
891  {
892  strlcpy(parentName, "<TERMINATED>", sizeof(parentName));
893  }
894  }
895 
896  if (0 != pProc->RealParentEprocess)
897  {
898  status = IntWinProcGetNameFromInternalEprocess(pProc->RealParentEprocess, realParentName);
899  if (INT_STATUS_NOT_FOUND == status)
900  {
901  strlcpy(parentName, "<TERMINATED>", sizeof(parentName));
902  }
903  }
904 
905  NLOG(" EPROCESS: 0x%016llx, CR3: 0x%016llx, PID: %d, Token: 0x%016llx, WOW64: %d, Name: %s, "
906  "Parent: 0x%016llx/%s, Real parent: 0x%016llx/%s, Prot Mask: 0x%08x, System: %d\n",
907  pProc->EprocessAddress,
908  pProc->Cr3,
909  pProc->Pid,
910  pProc->OriginalTokenPtr,
911  pProc->Wow64Process,
912  pProc->Name,
913  pProc->ParentEprocess,
914  parentName,
915  pProc->RealParentEprocess,
916  realParentName,
917  pProc->ProtectionMask,
918  pProc->SystemProcess);
919 
920  if (NULL != pProc->Subsystemx64)
921  {
922  LIST_ENTRY *list2;
923 
924  NLOG(" 64 bit subsystem:\n");
925 
926  // Dump loaded modules
927  list2 = pProc->Subsystemx64->ProcessModules.Flink;
928  while (list2 != &pProc->Subsystemx64->ProcessModules)
929  {
931 
932  list2 = list2->Flink;
933 
934  NLOG(" MODULE : 0x%016llx, Size: 0x%08x, Protected: %d, Hooked: %d, Unpack Protected: %d, "
935  "Main Module: %d Path: \"%s\", hash: 0x%08x\n",
936  pMod->VirtualBase, pMod->Size, pMod->ShouldProtHooks,
937  pMod->IsProtected, pMod->ShouldProtUnpack, pMod->IsMainModule,
938  utf16_for_log(pMod->Path->Path), pMod->Path->NameHash);
939  }
940  }
941 
942 
943  if (NULL != pProc->Subsystemx86)
944  {
945  LIST_ENTRY *list2;
946 
947  NLOG(" 32 bit subsystem:\n");
948 
949  // Dump loaded modules.
950  list2 = pProc->Subsystemx86->ProcessModules.Flink;
951  while (list2 != &pProc->Subsystemx86->ProcessModules)
952  {
954 
955  list2 = list2->Flink;
956 
957  NLOG(" MODULE : 0x%016llx, Size: 0x%08x, Protected: %d, Hooked: %d, "
958  "Unpack Protected: %d, Main Module: %d Path: \"%s\"\n",
959  pMod->VirtualBase, pMod->Size, pMod->ShouldProtHooks,
960  pMod->IsProtected, pMod->ShouldProtUnpack, pMod->IsMainModule, utf16_for_log(pMod->Path->Path));
961  }
962  }
963 
964  NLOG(" VAD tree:\n");
965 
966  // Dump the VAD tree.
968 
969  NLOG(" VAS monitor:\n");
970 
971  IntVasDump(pProc->Cr3);
972  }
973 }
974 
975 
976 void
978  _In_opt_ const char *ProcessName
979  )
986 {
987  LIST_ENTRY *list = gWinProcesses.Flink;
988  while (list != &gWinProcesses)
989  {
991  list = list->Flink;
992 
993  if (NULL != ProcessName && 0 != strcasecmp(ProcessName, pProc->Name))
994  {
995  continue;
996  }
997 
998  NLOG("Process %s:%d @ 0x%016llx\n", pProc->Name, pProc->Pid, pProc->EprocessAddress);
1000  }
1001 }
1002 
1003 
1004 void
1006  void
1007  )
1011 {
1012  if ((0 == WIN_KM_FIELD(Process, MitigationFlags)) || 0 == (WIN_KM_FIELD(Process, MitigationFlags2)))
1013  {
1014  LOG("Mitigation Flags are not available! 0x%08x 0x%08x\n",
1015  WIN_KM_FIELD(Process, MitigationFlags),
1016  WIN_KM_FIELD(Process, MitigationFlags2));
1017 
1018  return;
1019  }
1020 
1021  LIST_ENTRY *list = gWinProcesses.Flink;
1022  while (list != &gWinProcesses)
1023  {
1025  INTSTATUS status;
1026  WIN_MITIGATION_FLAGS flags = { 0 };
1027  WIN_MITIGATION_FLAGS2 flags2 = { 0 };
1028  static const PCHAR flagsText[] =
1029  {
1030  "ControlFlowGuardEnabled",
1031  "ControlFlowGuardExportSuppressionEnabled",
1032  "ControlFlowGuardStrict",
1033  "DisallowStrippedImages",
1034  "ForceRelocateImages",
1035  "HighEntropyASLREnabled",
1036  "StackRandomizationDisabled",
1037  "ExtensionPointDisable",
1038  "DisableDynamicCode",
1039  "DisableDynamicCodeAllowOptOut",
1040  "DisableDynamicCodeAllowRemoteDowngrade",
1041  "AuditDisableDynamicCode",
1042  "DisallowWin32kSystemCalls",
1043  "AuditDisallowWin32kSystemCalls",
1044  "EnableFilteredWin32kAPIs",
1045  "AuditFilteredWin32kAPIs",
1046  "DisableNonSystemFonts",
1047  "AuditNonSystemFontLoading",
1048  "PreferSystem32Images",
1049  "ProhibitRemoteImageMap",
1050  "AuditProhibitRemoteImageMap",
1051  "ProhibitLowILImageMap",
1052  "AuditProhibitLowILImageMap",
1053  "SignatureMitigationOptIn",
1054  "AuditBlockNonMicrosoftBinaries",
1055  "AuditBlockNonMicrosoftBinariesAllowStore",
1056  "LoaderIntegrityContinuityEnabled",
1057  "AuditLoaderIntegrityContinuity",
1058  "EnableModuleTamperingProtection",
1059  "EnableModuleTamperingProtectionNoInherit",
1060  };
1061  static const PCHAR flags2Text[] =
1062  {
1063  "EnableExportAddressFilter",
1064  "AuditExportAddressFilter",
1065  "EnableExportAddressFilterPlus",
1066  "AuditExportAddressFilterPlus",
1067  "EnableRopStackPivot",
1068  "AuditRopStackPivot",
1069  "EnableRopCallerCheck",
1070  "AuditRopCallerCheck",
1071  "EnableRopSimExec",
1072  "AuditRopSimExec",
1073  "EnableImportAddressFilter",
1074  "AuditImportAddressFilter",
1075  };
1076 
1077  list = list->Flink;
1078 
1079  status = IntKernVirtMemRead(pProc->EprocessAddress + WIN_KM_FIELD(Process, MitigationFlags),
1080  sizeof(flags),
1081  &flags,
1082  NULL);
1083  if (!INT_SUCCESS(status))
1084  {
1085  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x\n", status);
1086  continue;
1087  }
1088 
1089  status = IntKernVirtMemRead(pProc->EprocessAddress + WIN_KM_FIELD(Process, MitigationFlags2),
1090  sizeof(flags2),
1091  &flags2,
1092  NULL);
1093  if (!INT_SUCCESS(status))
1094  {
1095  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x\n", status);
1096  continue;
1097  }
1098 
1099  LOG("Dumping Flags for `%s` @ 0x%016llx\n", pProc->Name, pProc->EprocessAddress);
1100  NLOG("+ Mitigation Flags: 0x%08x\n", flags.Flags);
1101  for (DWORD i = 0; i < 32; i++)
1102  {
1103  if (0 == (flags.Flags & (1 << i)))
1104  {
1105  continue;
1106  }
1107 
1108  if (i >= ARRAYSIZE(flagsText))
1109  {
1110  NLOG("\t Bit %02d is set -> UNKNOWN\n", i);
1111  }
1112  else
1113  {
1114  NLOG("\t Bit %02d is set -> %s\n", i, flagsText[i]);
1115  }
1116  }
1117  NLOG("\n");
1118  NLOG("+ Mitigation Flags 2: 0x%08x\n", flags2.Flags);
1119  for (DWORD i = 0; i < 32; i++)
1120  {
1121  if (0 == (flags2.Flags & (1 << i)))
1122  {
1123  continue;
1124  }
1125 
1126  if (i >= ARRAYSIZE(flags2Text))
1127  {
1128  NLOG("\t Bit %02d is set -> UNKNOWN\n", i);
1129  }
1130  else
1131  {
1132  NLOG("\t Bit %02d is set -> %s\n", i, flags2Text[i]);
1133  }
1134  }
1135 
1136  NLOG("\n\n");
1137  }
1138 }
1139 
1140 
1141 INTSTATUS
1143  _In_ QWORD Eprocess,
1144  _Outptr_ void **Ptr
1145  )
1157 {
1158  // must be initialized because compiler assumes gGuest.WordSize might be 0
1160  static DWORD sizeofNeededEprocess = 0;
1161 
1162  if (0 == Eprocess)
1163  {
1165  }
1166 
1167  if (NULL == Ptr)
1168  {
1170  }
1171 
1172  // While sizeof(EPROCESS) < PAGE_SIZE - (aprox.) 0x20, the entire structure will be allocated in the same
1173  // page so we can map just the remaining page. However, an attacker might craft an EPROCESS structure at
1174  // page boundary. This ensures that all of our accesses inside the mapped EPROCESS are safe but it does
1175  // not fully detect EPROCESS structures that span two pages because the size we check is the maximum field
1176  // offset + guest word size, not the actual size of EPROCESS!
1177  if (0 == sizeofNeededEprocess)
1178  {
1179  for (WIN_KM_FIELD_PROCESS field = 0; field < winKmFieldProcessEnd; ++field)
1180  {
1181  if (sizeofNeededEprocess < gWinGuest->OsSpecificFields.Km.Process[field])
1182  {
1183  sizeofNeededEprocess = gWinGuest->OsSpecificFields.Km.Process[field];
1184  }
1185  }
1186  // now sizeofNeededEprocess is just the highest field offset, add word size
1187  sizeofNeededEprocess += gGuest.WordSize;
1188  }
1189 
1190  // It seems that `sizeofNeededEprocess += gGuest.WordSize;` causes some problems on certain OSes, for
1191  // example on a 2k12R2, sizeof(EPROCESS) is 0x700. The last field is the ExitStatus DWORD (at 0x6FC).
1192  // We will want to read 0x704, but if it is cross-page and the second page isn't present, we will fail.
1193  // Thus, until we have all the fields sizes in CAMI, we will try to decrement at most gGuest.WordSize / 2 times
1194  // and retry the mapping. If it succeeds, then that is our real size.
1195  for (BYTE i = 0; i <= gGuest.WordSize / 2; i++)
1196  {
1197  status = IntVirtMemMap(Eprocess, sizeofNeededEprocess, gGuest.Mm.SystemCr3, 0, Ptr);
1198  if (INT_SUCCESS(status))
1199  {
1200  break;
1201  }
1202 
1203  sizeofNeededEprocess--;
1204  }
1205 
1206  if (!INT_SUCCESS(status))
1207  {
1208  ERROR("[ERROR] Failed to map EPROCESS at address 0x%016llx, IntVirtMemMap failed: 0x%08x\n", Eprocess, status);
1209  }
1210 
1211  return status;
1212 }
WINUM_PATH * Path
Module path.
Definition: winummodule.h:62
#define IS_INVALID_PTR(ptr)
#define _In_opt_
Definition: intro_sal.h:16
PWIN_PROCESS_SUBSYSTEM Subsystemx64
The x64 subsystem. Note that a 32 bit process on a 64 bit OS may have both subsystems valid...
Definition: winprocess.h:312
_Bool BOOLEAN
Definition: intro_types.h:58
#define CONTAINING_RECORD(List, Type, Member)
Definition: introlists.h:36
INTSTATUS IntVirtMemUnmap(void **HostPtr)
Unmaps a memory range previously mapped with IntVirtMemMap.
Definition: introcore.c:2234
uint8_t BYTE
Definition: intro_types.h:47
WINDOWS_GUEST * gWinGuest
Global variable holding the state of a Windows guest.
Definition: winguest.c:37
struct _WIN_OPAQUE_FIELDS::@212 Km
Kernel mode information.
#define _In_
Definition: intro_sal.h:21
RBTREE gWinProcTreeEprocess
Tree of all the processes inside the guest, using the _EPROCESS address as the key.
Definition: winprocesshp.c:18
QWORD RealParentEprocess
The active EPROCESS at the moment of creation.
Definition: winprocess.h:92
QWORD SystemCr3
The Cr3 used to map the kernel.
Definition: guests.h:211
#define INT_STATUS_SUCCESS
Definition: introstatus.h:54
#define INT_STATUS_OUT_OF_RANGE
Definition: introstatus.h:275
DWORD ShouldProtHooks
TRUE if the module should be protected against hooks.
Definition: winummodule.h:42
#define _Out_writes_bytes_(expr)
Definition: intro_sal.h:38
DWORD KernelSize
The size of the kernel.
Definition: guests.h:284
void IntWinProcLstRemoveProcess(WIN_PROCESS_OBJECT *Process)
Removes a WIN_PROCESS_OBJECT structure from the process lists and trees.
Definition: winprocesshp.c:73
int IntWinProcRbTreeNodeCompareUserCr3(RBNODE const *Left, RBNODE const *Right)
Definition: winprocesshp.c:762
struct _LIST_ENTRY * Flink
Definition: introlists.h:20
BOOLEAN IntWinVadDump(VAD const *Vad, void *Context)
Prints a VAD structure.
Definition: winvad.c:1706
RBTREE gWinProcTreeCr3
Tree of all the processes inside the guest, using the kernel CR3 as the key.
Definition: winprocesshp.c:13
void FUNC_RbTreeNodeFree(RBNODE *Node)
Definition: rbtree.h:47
#define INT_SUCCESS(Status)
Definition: introstatus.h:42
#define MIN_HEAP_SIZE_PERCENT
Minimum amount of free heap needed in order to activate process protection.
Definition: introcore.h:58
Mitigation flags.
Definition: wddefs.h:1759
Definition: rbtree.h:34
#define ARRAYSIZE(A)
Definition: introdefs.h:101
int32_t INT32
Definition: intro_types.h:44
INTSTATUS RbLookupNode(RBTREE *Tree, RBNODE *NodeToSearch, RBNODE **NodeFound)
Definition: rbtree.c:517
INTSTATUS IntVasDump(QWORD Cr3)
Dump the monitored tables for the indicated Cr3.
Definition: vasmonitor.c:1077
void IntWinProcLstInsertProcess(WIN_PROCESS_OBJECT *Process)
Inserts a WIN_PROCESS_OBJECT structure into the process lists and trees.
Definition: winprocesshp.c:46
#define INT_STATUS_NOT_NEEDED_HINT
Definition: introstatus.h:317
PWIN_PROCESS_OBJECT IntWinProcFindObjectByPid(DWORD Pid)
Finds a process by its ID.
Definition: winprocesshp.c:126
#define ERROR(fmt,...)
Definition: glue.h:62
#define _Outptr_
Definition: intro_sal.h:19
INTSTATUS IntKernVirtMemFetchWordSize(QWORD GuestVirtualAddress, void *Data)
Reads a guest pointer from the guest kernel memory.
Definition: introcore.c:847
INTSTATUS IntWinProcIsPsActiveProcessHead(QWORD Gva)
Checks if a guest memory area is the list head of the process list (PsActiveProcessHead) ...
Definition: winprocesshp.c:258
int INTSTATUS
The status data type.
Definition: introstatus.h:24
The end of the fields.
Definition: winguest.h:298
INTSTATUS RbInit(RBTREE *Tree, PFUNC_RbTreeNodeFree NodeFree, PFUNC_RbTreeNodeCompare NodeCompare)
Definition: rbtree.c:386
int FUNC_RbTreeNodeCompare(RBNODE *Left, RBNODE *Right)
Definition: rbtree.h:59
#define INT_STATUS_NOT_FOUND
Definition: introstatus.h:284
DWORD IsProtected
TRUE if the module is actually hooked.
Definition: winummodule.h:46
#define IMAGE_BASE_NAME_LEN
The maximum length of a process name.
Definition: winguest.h:15
INTSTATUS IntQueryHeapSize(size_t *TotalHeapSize, size_t *FreeHeapSize)
Definition: glue.c:1112
INTSTATUS IntWinProcGetNameFromEprocess(QWORD Eprocess, CHAR *Name)
Reads a process name from the guest memory.
Definition: winprocesshp.c:615
QWORD VirtualBase
Guest virtual address of the loaded module.
Definition: winummodule.h:34
void IntWinProcRbTreeNodeFree(RBNODE *Node)
The NodeFree routine for the process RBTREE structures.
Definition: winprocesshp.c:723
QWORD ParentEprocess
The EPROCESS of the parent process.
Definition: winprocess.h:91
#define LOG(fmt,...)
Definition: glue.h:61
DWORD Process[winKmFieldProcessEnd]
Information about the _EPROCESS structure. Indexed with values from WIN_KM_FIELD_PROCESS.
Definition: winguest.h:681
#define INT_STATUS_BREAK_ITERATION
Can be used by iteration callbacks to break the iteration early.
Definition: introstatus.h:374
DWORD Wow64Process
TRUE if this is a 32 bit process on a 64 bit OS.
Definition: winprocess.h:125
LIST_HEAD gWinProcesses
The list of all the processes inside the guest.
Definition: winprocesshp.c:11
PWIN_PROCESS_OBJECT IntWinProcFindObjectByName(CHAR const *Name, BOOLEAN MustBeSystem)
Finds a process by name.
Definition: winprocesshp.c:157
QWORD Cr3
Process PDBR. Includes PCID.
Definition: winprocess.h:98
#define _Inout_
Definition: intro_sal.h:20
QWORD PsActiveProcessHead
Guest virtual address of the PsActiveProcessHead kernel variable.
Definition: winguest.h:835
PWIN_PROCESS_OBJECT IntWinProcFindObjectByEprocess(QWORD Eprocess)
Finds a process by the address of its _EPROCESS structure.
Definition: winprocesshp.c:96
RBNODE NodeEproc
Entry within gWinProcTreeEprocess (RB Tree).
Definition: winprocess.h:88
int IntWinProcRbTreeNodeCompareCr3(RBNODE const *Left, RBNODE const *Right)
Definition: winprocesshp.c:735
static BOOLEAN RemoveEntryList(LIST_ENTRY *Entry)
Definition: introlists.h:87
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
CHAR Name[IMAGE_BASE_NAME_LEN]
Process base name.
Definition: winprocess.h:108
DWORD ShouldProtUnpack
TRUE if the module should be protected against unpack.
Definition: winummodule.h:43
QWORD UserCr3
Process user PDBR. Includes PCID.
Definition: winprocess.h:99
INTSTATUS IntWinProcMapEprocess(QWORD Eprocess, void **Ptr)
Maps a _EPROCESS structure.
#define TRUE
Definition: intro_types.h:30
INTSTATUS(* PFUNC_IterateListCallback)(QWORD Node, QWORD Aux)
Definition: introtypes.h:71
INTSTATUS IntWinProcIterateGuestProcesses(PFUNC_IterateListCallback Callback, QWORD Aux)
Iterates the in-guest process list and calls Callback for each entry.
Definition: winprocesshp.c:501
void IntWinProcLstUnsafeReInit(void)
Reinitializes the Windows process lists and trees, without doing any cleanup.
Definition: winprocesshp.c:22
INTSTATUS IntWinProcGetNameFromInternalEprocess(QWORD Eprocess, CHAR *Name)
Get a process name from the internal Introcore buffers.
Definition: winprocesshp.c:648
DWORD ProtectionMask
Protection mask: tells us what level of protection will be activated for this process.
Definition: winprocess.h:200
WIN_OPAQUE_FIELDS OsSpecificFields
OS-dependent and specific information (variables, offsets, etc).
Definition: winguest.h:857
#define TRACE(fmt,...)
Definition: glue.h:58
#define INT_STATUS_INVALID_DATA_STATE
Definition: introstatus.h:183
QWORD KernelVa
The guest virtual address at which the kernel image.
Definition: guests.h:283
BYTE WordSize
Guest word size. Will be 4 for 32-bit guests and 8 for 64-bit guests.
Definition: guests.h:367
static void InsertTailList(LIST_ENTRY *ListHead, LIST_ENTRY *Entry)
Definition: introlists.h:135
void IntWinProcDump(void)
Prints information about all the processes in the system.
Definition: winprocesshp.c:864
size_t strlcpy(char *dst, const char *src, size_t dest_size)
Definition: introcrt.c:1093
char * PCHAR
Definition: intro_types.h:56
PWIN_PROCESS_OBJECT IntWinProcFindObjectByUserCr3(QWORD Cr3)
Finds a process by its user CR3.
Definition: winprocesshp.c:225
INTSTATUS IntWinProcCreateProcessObject(WIN_PROCESS_OBJECT **Process, QWORD EprocessAddress, PBYTE EprocessBuffer, QWORD ParentEprocess, QWORD RealParentEprocess, QWORD Cr3, DWORD Pid, BOOLEAN StaticScan)
Allocates a WIN_PROCESS_OBJECT structure for the given process.
Definition: winprocess.c:1544
DWORD Pid
Process ID (the one used by Windows).
Definition: winprocess.h:100
static void InitializeListHead(LIST_ENTRY *ListHead)
Definition: introlists.h:69
enum _WIN_KM_FIELD_PROCESS WIN_KM_FIELD_PROCESS
Indexes in the WIN_OPAQUE_FIELDS.Km.Process array, containing offsets inside the _EPROCESS structure...
void RbPreinit(RBTREE *Tree)
Definition: rbtree.c:377
#define UNREFERENCED_PARAMETER(P)
Definition: introdefs.h:29
INTSTATUS IntWinVadWalkTree(PWIN_PROCESS_OBJECT Process, PFUNC_RbTreeWalkCallback Callback)
Walks the VAD tree of a process.
Definition: winvad.c:2025
DWORD NameHash
The CRC32 hash of the name. Used for fast matching.
Definition: winumpath.h:23
#define INT_STATUS_DATA_BUFFER_TOO_SMALL
Definition: introstatus.h:194
#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
#define INT_STATUS_INVALID_DATA_VALUE
Definition: introstatus.h:136
QWORD OriginalTokenPtr
Original Token pointer inside EPROCESS (should never change).
Definition: winprocess.h:247
INTSTATUS IntWinProcAdd(QWORD Eprocess, QWORD Aux)
Adds a new process to the Introcore list of processes.
Definition: winprocesshp.c:396
PWIN_PROCESS_OBJECT IntWinProcFindObjectByCr3(QWORD Cr3)
Finds a process by its kernel CR3.
Definition: winprocesshp.c:195
FUNC_RbTreeWalkCallback * PFUNC_RbTreeWalkCallback
Definition: rbtree.h:79
BOOLEAN IntWinProcIsEnoughHeapAvailable(void)
Checks if enough heap is available in order to protect a new process.
Definition: winprocesshp.c:685
Definition: rbtree.h:84
#define INT_STATUS_INVALID_OBJECT_TYPE
Definition: introstatus.h:145
INTSTATUS IntWinProcGetAgentsAsCli(PCHAR CommandLine, DWORD Length)
Returns the name and ID for all the processes injected as agents inside the guest.
Definition: winprocesshp.c:812
LIST_HEAD ProcessModules
List of process modules.
Definition: winprocess.h:70
void IntWinProcDumpVads(const char *ProcessName)
Prints information about the VADs loaded in a process.
Definition: winprocesshp.c:977
__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
RBNODE NodeUserCr3
Entry within gWinProcTreeUserCr3 (RB Tree).
Definition: winprocess.h:87
MM Mm
Guest memory information, such as paging mode, system Cr3 value, etc.
Definition: guests.h:374
QWORD EprocessAddress
This will be the address of the ActiveProcess field.
Definition: winprocess.h:90
DWORD SystemProcess
TRUE if this is a system process.
Definition: winprocess.h:136
GUEST_STATE gGuest
The current guest state.
Definition: guests.c:50
#define _Function_class_(expr)
Definition: intro_sal.h:40
int IntWinProcRbTreeNodeCompareEproc(RBNODE const *Left, RBNODE const *Right)
Definition: winprocesshp.c:789
void IntWinProcDumpEgFlags(void)
Prints the mitigation flags of a process.
void RbDeleteNode(RBTREE *Tree, RBNODE *Node)
Definition: rbtree.c:710
INTSTATUS IntKernVirtMemRead(QWORD KernelGva, DWORD Length, void *Buffer, DWORD *RetLength)
Reads data from a guest kernel virtual memory range.
Definition: introcore.c:674
#define NLOG(fmt,...)
Definition: glue.h:43
#define LIST_HEAD_INIT(Name)
Definition: introlists.h:39
#define _Out_writes_z_(expr)
Definition: intro_sal.h:37
INTSTATUS RbInsertNode(RBTREE *Tree, RBNODE *Node)
Definition: rbtree.c:606
#define INT_STATUS_NOT_INITIALIZED_HINT
Definition: introstatus.h:320
char * utf16_for_log(const WCHAR *WString)
Converts a UTF-16 to a UTF-8 string to be used inside logging macros.
Definition: introcore.c:2845
#define INT_STATUS_INVALID_PARAMETER_1
Definition: introstatus.h:62
DWORD IsAgent
TRUE if this is an injected agent.
Definition: winprocess.h:131
#define INT_STATUS_UNINITIALIZED_STATUS_VALUE
Definition: introstatus.h:278
#define RB_TREE_INIT(Name, Free, Compare)
Initializes a RBTREE structure.
Definition: introcore.h:39
char CHAR
Definition: intro_types.h:56
PWIN_PROCESS_SUBSYSTEM Subsystemx86
The x86 subsystem. Note that a 32 bit process on a 64 bit OS may have both subsystems valid...
Definition: winprocess.h:308
RBNODE NodeCr3
Entry within gWinProcTreeCr3 (RB Tree).
Definition: winprocess.h:86
DWORD IsMainModule
TRUE if this is the main module.
Definition: winummodule.h:47
#define INT_STATUS_INVALID_PARAMETER_2
Definition: introstatus.h:65
DWORD Size
Virtual size of the module.
Definition: winummodule.h:35
#define PROCESSES_MAX_COUNT
#define _Analysis_assume_(expr)
Definition: intro_sal.h:46
RBTREE gWinProcTreeUserCr3
Tree of all the processes inside the guest, using the user-mode CR3 as the key/.
Definition: winprocesshp.c:15
#define FALSE
Definition: intro_types.h:34
This structure describes a running process inside the guest.
Definition: winprocess.h:83
Mitigation flags.
Definition: wddefs.h:1715
WCHAR * Path
The string which represents the user-mode module path.
Definition: winumpath.h:17