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 
24  _In_ QWORD Eprocess
25  )
33 {
34  INTSTATUS status;
35  WIN_PROCESS_OBJECT target;
36  RBNODE *result;
37 
38  target.EprocessAddress = Eprocess;
39 
40  status = RbLookupNode(&gWinProcTreeEprocess, &target.NodeEproc, &result);
41  if (INT_SUCCESS(status) && result)
42  {
43  return CONTAINING_RECORD(result, WIN_PROCESS_OBJECT, NodeEproc);
44  }
45  else
46  {
47  return NULL;
48  }
49 }
50 
51 
54  _In_ DWORD Pid
55  )
63 {
64  LIST_ENTRY *list;
65 
66  list = gWinProcesses.Flink;
67  while (list != &gWinProcesses)
68  {
70 
71  if (pProc->Pid == Pid)
72  {
73  return pProc;
74  }
75 
76  list = list->Flink;
77  }
78 
79  return NULL;
80 }
81 
82 
85  _In_ CHAR const *Name,
86  _In_ BOOLEAN MustBeSystem
87  )
96 {
97  if (NULL == Name)
98  {
99  return NULL;
100  }
101 
102  for (LIST_ENTRY *entry = gWinProcesses.Flink; entry != &gWinProcesses; entry = entry->Flink)
103  {
105 
106  if (MustBeSystem && !pProc->SystemProcess)
107  {
108  continue;
109  }
110 
111  if (!strcasecmp(pProc->Name, Name))
112  {
113  return pProc;
114  }
115  }
116 
117  return NULL;
118 }
119 
120 
123  _In_ QWORD Cr3
124  )
132 {
133  INTSTATUS status;
134  WIN_PROCESS_OBJECT target;
135  RBNODE* result;
136 
137  target.Cr3 = Cr3;
138 
139  status = RbLookupNode(&gWinProcTreeCr3, &target.NodeCr3, &result);
140  if (INT_SUCCESS(status) && result)
141  {
142  return CONTAINING_RECORD(result, WIN_PROCESS_OBJECT, NodeCr3);
143  }
144  else
145  {
146  return NULL;
147  }
148 }
149 
150 
153  _In_ QWORD Cr3
154  )
165 {
166  INTSTATUS status;
167  WIN_PROCESS_OBJECT target;
168  RBNODE* result;
169 
170  target.UserCr3 = Cr3;
171 
172  status = RbLookupNode(&gWinProcTreeUserCr3, &target.NodeUserCr3, &result);
173  if (INT_SUCCESS(status) && result)
174  {
175  return CONTAINING_RECORD(result, WIN_PROCESS_OBJECT, NodeUserCr3);
176  }
177  else
178  {
179  return NULL;
180  }
181 }
182 
183 
184 INTSTATUS
186  _In_ QWORD Gva
187  )
207 {
208  INTSTATUS status;
209  QWORD flink, blink, prevFlink, nextBlink, pid, type;
210  CHAR imageName[IMAGE_BASE_NAME_LEN];
211 
212 #define IS_INVALID_PTR(ptr) !IS_KERNEL_POINTER_WIN(gGuest.Guest64, (ptr)) || \
213  (gGuest.Guest64 && 0xffffffff00000000 == ((ptr) & 0xffffffff00000000)) || \
214  (!gGuest.Guest64 && 0xffffffff == (ptr)) \
215 
216  // System EPROC is not in kernel
217  if (gGuest.KernelVa != 0 &&
218  gGuest.KernelSize != 0 &&
219  Gva >= gGuest.KernelVa &&
220  Gva <= gGuest.KernelVa + gGuest.KernelSize)
221  {
223  }
224 
225  flink = 0;
226  blink = 0;
227  prevFlink = 0;
228  nextBlink = 0;
229  pid = 0;
230  type = 0;
231 
232  status = IntKernVirtMemFetchWordSize(Gva, &flink);
233  if (!INT_SUCCESS(status))
234  {
235  return status;
236  }
237 
238  if (IS_INVALID_PTR(flink))
239  {
241  }
242 
243  status = IntKernVirtMemFetchWordSize(Gva + gGuest.WordSize, &blink);
244  if (!INT_SUCCESS(status))
245  {
246  return status;
247  }
248 
249  if (IS_INVALID_PTR(flink))
250  {
252  }
253 
254  // Make sure flink->blink points to us.
255  status = IntKernVirtMemFetchWordSize(flink + gGuest.WordSize, &nextBlink);
256  if (!INT_SUCCESS(status))
257  {
258  return status;
259  }
260 
261  if (nextBlink != Gva)
262  {
264  }
265 
266  // Make sure blink->flink points to us.
267  status = IntKernVirtMemFetchWordSize(blink, &prevFlink);
268  if (!INT_SUCCESS(status))
269  {
270  return status;
271  }
272 
273  if (prevFlink != Gva)
274  {
276  }
277 
278  status = IntKernVirtMemFetchWordSize(Gva - WIN_KM_FIELD(Process, ListEntry) + WIN_KM_FIELD(Process, Id), &pid);
279  if (!INT_SUCCESS(status))
280  {
281  return status;
282  }
283 
284  if (pid != 4)
285  {
287  }
288 
289  status = IntKernVirtMemFetchWordSize(Gva - WIN_KM_FIELD(Process, ListEntry), &type);
290  if (!INT_SUCCESS(status))
291  {
292  return status;
293  }
294 
295  if ((BYTE)type != 3)
296  {
298  }
299 
300  // Process name must be 'System'
301  status = IntKernVirtMemRead(Gva - WIN_KM_FIELD(Process, ListEntry) +
302  WIN_KM_FIELD(Process, Name),
303  sizeof(imageName),
304  imageName,
305  NULL);
306  if (!INT_SUCCESS(status))
307  {
308  return status;
309  }
310 
311  imageName[sizeof(imageName) - 1] = 0;
312 
313  if (0 != strcasecmp(imageName, "system"))
314  {
316  }
317 
318  return INT_STATUS_SUCCESS;
319 }
320 
321 
322 INTSTATUS
324  _In_ QWORD Eprocess,
325  _In_ QWORD Aux
326  )
344 {
345  INTSTATUS status;
346  QWORD cr3, parentEproc;
347  DWORD pid;
348  DWORD flags;
349  WIN_PROCESS_OBJECT *pProcObj, *pParent;
350  BYTE *pEproc = NULL;
351 
353 
354  status = IntWinProcMapEprocess(Eprocess, &pEproc);
355  if (NULL == pEproc || !INT_SUCCESS(status))
356  {
357  ERROR("[ERROR] IntWinProcMapEprocess failed: 0x%08x\n", status);
358  return status;
359  }
360 
361  pid = *(DWORD *)(pEproc + WIN_KM_FIELD(Process, Id));
362 
363  if (gGuest.Guest64)
364  {
365  cr3 = *(QWORD *)(pEproc + WIN_KM_FIELD(Process, Cr3));
366  }
367  else
368  {
369  cr3 = *(DWORD *)(pEproc + WIN_KM_FIELD(Process, Cr3));
370  }
371 
372  flags = *(DWORD *)(pEproc + WIN_KM_FIELD(Process, Flags));
373  if (0 != (flags & (
374  WIN_KM_FIELD(EprocessFlags, Delete)
375  | WIN_KM_FIELD(EprocessFlags, Exiting)
376  | WIN_KM_FIELD(EprocessFlags, VmDeleted))))
377  {
378  LOG("[EPROCESS] Skipping process %llx which is being marked as deleted (flags = 0x%x)...\n", Eprocess, flags);
380  goto cleanup_and_exit;
381  }
382  else if (0 == (flags & WIN_KM_FIELD(EprocessFlags, HasAddrSpace)))
383  {
384  LOG("[EPROCESS] Skipping process %llx which does not have an address space (flags = 0x%x)...\n",
385  Eprocess, flags);
387  goto cleanup_and_exit;
388  }
389 
390  if (NULL != IntWinProcFindObjectByEprocess(Eprocess))
391  {
392  LOG("[EPROCESS] The list seems to have a circular loop on process 0x%016llx, will break iteration...\n",
393  Eprocess);
395  goto cleanup_and_exit;
396  }
397 
398  // Find the associated parent process.
399  pParent = IntWinProcFindObjectByPid(*(DWORD *)(pEproc + WIN_KM_FIELD(Process, ParentPid)));
400  if (NULL != pParent)
401  {
402  parentEproc = pParent->EprocessAddress;
403  }
404  else
405  {
406  parentEproc = 0;
407  }
408 
409  status = IntWinProcCreateProcessObject(&pProcObj, Eprocess, pEproc, parentEproc, 0, cr3, pid, TRUE);
410  if (!INT_SUCCESS(status))
411  {
412  ERROR("[ERROR] IntWinProcCreateProcessObject failed for EPROC 0x%016llx with flags 0x%x: 0x%08x\n",
413  Eprocess, flags, status);
414  goto cleanup_and_exit;
415  }
416 
417 cleanup_and_exit:
418  if (NULL != pEproc)
419  {
420  IntVirtMemUnmap(&pEproc);
421  }
422 
423  return status;
424 }
425 
426 
427 INTSTATUS
430  _In_ QWORD Aux
431  )
446 {
447 #define PROCESSES_MAX_COUNT 65535
448  INTSTATUS status;
449  QWORD currentProcess = 0, count = 0, nextProcess = 0;
450 
451  if (Callback == NULL)
452  {
454  }
455 
456  currentProcess = gWinGuest->PsActiveProcessHead;
457  if (currentProcess == 0)
458  {
460  }
461 
462  status = IntKernVirtMemRead(currentProcess, gGuest.WordSize, &currentProcess, NULL);
463  if (!INT_SUCCESS(status))
464  {
465  ERROR("[ERROR] Failed getting the Flink value of EPROCESS @ 0x%016llx: 0x%08x\n", currentProcess, status);
466  return status;
467  }
468 
469  status = IntKernVirtMemRead(currentProcess, gGuest.WordSize, &nextProcess, NULL);
470  if (!INT_SUCCESS(status))
471  {
472  ERROR("[ERROR] Failed getting the Flink value of EPROCESS @ 0x%016llx: 0x%08x\n", currentProcess, status);
473  return status;
474  }
475 
476  status = INT_STATUS_SUCCESS;
477  while ((currentProcess != gWinGuest->PsActiveProcessHead) && (count++ < PROCESSES_MAX_COUNT))
478  {
479  status = Callback((currentProcess - WIN_KM_FIELD(Process, ListEntry)), Aux);
480  if (INT_STATUS_BREAK_ITERATION == status)
481  {
482  status = INT_STATUS_SUCCESS;
483  break;
484  }
485  if (INT_STATUS_NOT_NEEDED_HINT == status)
486  {
487  // The process was not actually added in our list, so decrease the count so that we don't
488  // miss the actually non-deleted processes.
489  count--;
490  }
491 
492  status = IntKernVirtMemRead(currentProcess, gGuest.WordSize, &currentProcess, NULL);
493  if (!INT_SUCCESS(status))
494  {
495  ERROR("[ERROR] Failed getting the Flink value of EPROCESS @ 0x%016llx: 0x%08x\n", currentProcess, status);
496  break;
497  }
498 
499  // Consider the nextProcess variable as the hare in the tortoise and hare algorithm. We should do f(f(hare)),
500  // so read the next eprocess of the next eprocess, keeping the notion that the "nextProcess" moves through
501  // the list at a speed doubled of the "currentProcess"'s speed.
502  status = IntKernVirtMemRead(nextProcess, gGuest.WordSize, &nextProcess, NULL);
503  if (!INT_SUCCESS(status))
504  {
505  ERROR("[ERROR] Failed getting the Flink value of EPROCESS @ 0x%016llx: 0x%08x\n", nextProcess, status);
506  break;
507  }
508 
509  status = IntKernVirtMemRead(nextProcess, gGuest.WordSize, &nextProcess, NULL);
510  if (!INT_SUCCESS(status))
511  {
512  ERROR("[ERROR] Failed getting the Flink value of EPROCESS @ 0x%016llx: 0x%08x\n", nextProcess, status);
513  break;
514  }
515 
516  // The hare and the tortoise are in the same position - a cycle was detected in the list! Verify if the cycle
517  // is not the list head (since the linked lists will always be cyclical with respect to the list head). Note
518  // that we don't actually need to find the first repetition in the cycle, nor the period of the cycle, we only
519  // need to find whether it is a cycle, so the condition that the hare and the tortoise are in the same position
520  // is enough.
521  if (nextProcess == currentProcess && currentProcess != gWinGuest->PsActiveProcessHead)
522  {
523  ERROR("[ERROR] The guest linked list seem to be cyclical, a cycle has been detected on 0x%016llx\n",
524  currentProcess);
526  break;
527  }
528  }
529 
530  if (count >= PROCESSES_MAX_COUNT)
531  {
532  status = INT_STATUS_OUT_OF_RANGE;
533  }
534 
535  return status;
536 
537 #undef PROCESSES_MAX_COUNT
538 }
539 
540 
541 INTSTATUS
543  _In_ QWORD Eprocess,
545  )
558 {
559  if (0 == Eprocess)
560  {
562  }
563 
564  memset(Name, 0, IMAGE_BASE_NAME_LEN);
565 
566  _Analysis_assume_(Name[IMAGE_BASE_NAME_LEN - 1] = NULL);
567  return IntKernVirtMemRead(Eprocess + WIN_KM_FIELD(Process, Name),
569  Name,
570  NULL);
571 }
572 
573 
574 INTSTATUS
576  _In_ QWORD Eprocess,
578  )
589 {
590  LIST_ENTRY *list;
591 
592  list = gWinProcesses.Flink;
593  while (list != &gWinProcesses)
594  {
596  list = list->Flink;
597 
598  if (pProc->EprocessAddress == Eprocess)
599  {
600  memcpy(Name, pProc->Name, IMAGE_BASE_NAME_LEN);
601  _Analysis_assume_(Name[IMAGE_BASE_NAME_LEN - 1] = NULL);
602 
603  return INT_STATUS_SUCCESS;
604  }
605  }
606 
607  return INT_STATUS_NOT_FOUND;
608 }
609 
610 
611 BOOLEAN
613  void
614  )
623 {
624  INTSTATUS status;
625  size_t totalHeapSize, freeHeapSize;
626 
627  // Before actually protecting the process, make sure enough heap is available. If there's less than 30%, we will NOT
628  // protect anymore processes!!!
629  status = IntQueryHeapSize(&totalHeapSize, &freeHeapSize);
630  if (!INT_SUCCESS(status))
631  {
632  ERROR("[ERROR] IntQueryHeapSize failed: 0x%08x, will assume 'infinite' heap.\n", status);
633  totalHeapSize = 0xFFFFFFFFFFFFFFFF;
634  freeHeapSize = 0xFFFFFFFFFFFFFFFF;
635  }
636 
637  TRACE("[INFO] Heap stats: total size: %zu bytes, free: %zu bytes\n", totalHeapSize, freeHeapSize);
638 
639  // Make sure more than minimum of MIN_HEAP_SIZE_PERCENT of heap is available.
640  if (freeHeapSize < (totalHeapSize * MIN_HEAP_SIZE_PERCENT) / 100)
641  {
642  return FALSE;
643  }
644 
645  return TRUE;
646 }
647 
648 
651  _Inout_ RBNODE *Node
652  )
656 {
658 }
659 
660 
663  _In_ RBNODE const *Left,
664  _In_ RBNODE const *Right)
665 {
666  WIN_PROCESS_OBJECT const *p1 = CONTAINING_RECORD(Left, WIN_PROCESS_OBJECT, NodeCr3);
667  WIN_PROCESS_OBJECT const *p2 = CONTAINING_RECORD(Right, WIN_PROCESS_OBJECT, NodeCr3);
668  QWORD cr3p1, cr3p2;
669 
670  cr3p1 = p1->Cr3;
671  cr3p2 = p2->Cr3;
672 
673  if (cr3p1 < cr3p2)
674  {
675  return -1;
676  }
677  else if (cr3p1 > cr3p2)
678  {
679  return 1;
680  }
681  else
682  {
683  return 0;
684  }
685 }
686 
687 
690  _In_ RBNODE const *Left,
691  _In_ RBNODE const *Right)
692 {
694  PWIN_PROCESS_OBJECT p2 = CONTAINING_RECORD(Right, WIN_PROCESS_OBJECT, NodeUserCr3);
695  QWORD cr3p1, cr3p2;
696 
697  cr3p1 = p1->UserCr3;
698  cr3p2 = p2->UserCr3;
699 
700  if (cr3p1 < cr3p2)
701  {
702  return -1;
703  }
704  else if (cr3p1 > cr3p2)
705  {
706  return 1;
707  }
708  else
709  {
710  return 0;
711  }
712 }
713 
714 
717  _In_ RBNODE const *Left,
718  _In_ RBNODE const *Right)
719 {
722 
723  if (p1->EprocessAddress < p2->EprocessAddress)
724  {
725  return -1;
726  }
727  else if (p1->EprocessAddress > p2->EprocessAddress)
728  {
729  return 1;
730  }
731  else
732  {
733  return 0;
734  }
735 }
736 
737 
738 INTSTATUS
740  _Out_writes_bytes_(Length) PCHAR CommandLine,
741  _In_ DWORD Length
742  )
755 {
756  PCHAR cmd = CommandLine;
757 
758  PLIST_ENTRY list = gWinProcesses.Flink;
759  while (list != &gWinProcesses)
760  {
762  INT32 len;
763 
764  list = list->Flink;
765 
766  if (!pProc->IsAgent)
767  {
768  continue;
769  }
770 
771  len = snprintf(cmd, Length, "%s %u ", pProc->Name, pProc->Pid);
772  Length -= len;
773  cmd += len;
774 
775  if ((int)Length < 0)
776  {
778  }
779  }
780 
781  return INT_STATUS_SUCCESS;
782 }
783 
784 
785 void
787  void
788  )
792 {
793  INTSTATUS status;
794 
795  LIST_ENTRY *list = gWinProcesses.Flink;
796  while (list != &gWinProcesses)
797  {
798  PWIN_PROCESS_OBJECT pProc;
799  CHAR parentName[IMAGE_BASE_NAME_LEN];
800  CHAR realParentName[IMAGE_BASE_NAME_LEN];
801 
802  memset(parentName, 0, sizeof(parentName));
803  memset(realParentName, 0, sizeof(realParentName));
804 
805  pProc = CONTAINING_RECORD(list, WIN_PROCESS_OBJECT, Link);
806 
807  list = list->Flink;
808 
809  if (0 != pProc->ParentEprocess)
810  {
811  status = IntWinProcGetNameFromInternalEprocess(pProc->ParentEprocess, parentName);
812  if (INT_STATUS_NOT_FOUND == status)
813  {
814  strlcpy(parentName, "<TERMINATED>", sizeof(parentName));
815  }
816  }
817 
818  if (0 != pProc->RealParentEprocess)
819  {
820  status = IntWinProcGetNameFromInternalEprocess(pProc->RealParentEprocess, realParentName);
821  if (INT_STATUS_NOT_FOUND == status)
822  {
823  strlcpy(parentName, "<TERMINATED>", sizeof(parentName));
824  }
825  }
826 
827  NLOG(" EPROCESS: 0x%016llx, CR3: 0x%016llx, PID: %d, Token: 0x%016llx, WOW64: %d, Name: %s, "
828  "Parent: 0x%016llx/%s, Real parent: 0x%016llx/%s, Prot Mask: 0x%08x, System: %d\n",
829  pProc->EprocessAddress,
830  pProc->Cr3,
831  pProc->Pid,
832  pProc->OriginalTokenPtr,
833  pProc->Wow64Process,
834  pProc->Name,
835  pProc->ParentEprocess,
836  parentName,
837  pProc->RealParentEprocess,
838  realParentName,
839  pProc->ProtectionMask,
840  pProc->SystemProcess);
841 
842  if (NULL != pProc->Subsystemx64)
843  {
844  LIST_ENTRY *list2;
845 
846  NLOG(" 64 bit subsystem:\n");
847 
848  // Dump loaded modules
849  list2 = pProc->Subsystemx64->ProcessModules.Flink;
850  while (list2 != &pProc->Subsystemx64->ProcessModules)
851  {
853 
854  list2 = list2->Flink;
855 
856  NLOG(" MODULE : 0x%016llx, Size: 0x%08x, Protected: %d, Hooked: %d, Unpack Protected: %d, "
857  "Main Module: %d Path: \"%s\", hash: 0x%08x\n",
858  pMod->VirtualBase, pMod->Size, pMod->ShouldProtHooks,
859  pMod->IsProtected, pMod->ShouldProtUnpack, pMod->IsMainModule,
860  utf16_for_log(pMod->Path->Path), pMod->Path->NameHash);
861  }
862  }
863 
864 
865  if (NULL != pProc->Subsystemx86)
866  {
867  LIST_ENTRY *list2;
868 
869  NLOG(" 32 bit subsystem:\n");
870 
871  // Dump loaded modules.
872  list2 = pProc->Subsystemx86->ProcessModules.Flink;
873  while (list2 != &pProc->Subsystemx86->ProcessModules)
874  {
876 
877  list2 = list2->Flink;
878 
879  NLOG(" MODULE : 0x%016llx, Size: 0x%08x, Protected: %d, Hooked: %d, "
880  "Unpack Protected: %d, Main Module: %d Path: \"%s\"\n",
881  pMod->VirtualBase, pMod->Size, pMod->ShouldProtHooks,
882  pMod->IsProtected, pMod->ShouldProtUnpack, pMod->IsMainModule, utf16_for_log(pMod->Path->Path));
883  }
884  }
885 
886  NLOG(" VAD tree:\n");
887 
888  // Dump the VAD tree.
890 
891  NLOG(" VAS monitor:\n");
892 
893  IntVasDump(pProc->Cr3);
894  }
895 }
896 
897 
898 void
900  _In_opt_ const char *ProcessName
901  )
908 {
909  LIST_ENTRY *list = gWinProcesses.Flink;
910  while (list != &gWinProcesses)
911  {
913  list = list->Flink;
914 
915  if (NULL != ProcessName && 0 != strcasecmp(ProcessName, pProc->Name))
916  {
917  continue;
918  }
919 
920  NLOG("Process %s:%d @ 0x%016llx\n", pProc->Name, pProc->Pid, pProc->EprocessAddress);
922  }
923 }
924 
925 
926 void
928  void
929  )
933 {
934  if ((0 == WIN_KM_FIELD(Process, MitigationFlags)) || 0 == (WIN_KM_FIELD(Process, MitigationFlags2)))
935  {
936  LOG("Mitigation Flags are not available! 0x%08x 0x%08x\n",
937  WIN_KM_FIELD(Process, MitigationFlags),
938  WIN_KM_FIELD(Process, MitigationFlags2));
939 
940  return;
941  }
942 
943  LIST_ENTRY *list = gWinProcesses.Flink;
944  while (list != &gWinProcesses)
945  {
947  INTSTATUS status;
948  WIN_MITIGATION_FLAGS flags = { 0 };
949  WIN_MITIGATION_FLAGS2 flags2 = { 0 };
950  static const PCHAR flagsText[] =
951  {
952  "ControlFlowGuardEnabled",
953  "ControlFlowGuardExportSuppressionEnabled",
954  "ControlFlowGuardStrict",
955  "DisallowStrippedImages",
956  "ForceRelocateImages",
957  "HighEntropyASLREnabled",
958  "StackRandomizationDisabled",
959  "ExtensionPointDisable",
960  "DisableDynamicCode",
961  "DisableDynamicCodeAllowOptOut",
962  "DisableDynamicCodeAllowRemoteDowngrade",
963  "AuditDisableDynamicCode",
964  "DisallowWin32kSystemCalls",
965  "AuditDisallowWin32kSystemCalls",
966  "EnableFilteredWin32kAPIs",
967  "AuditFilteredWin32kAPIs",
968  "DisableNonSystemFonts",
969  "AuditNonSystemFontLoading",
970  "PreferSystem32Images",
971  "ProhibitRemoteImageMap",
972  "AuditProhibitRemoteImageMap",
973  "ProhibitLowILImageMap",
974  "AuditProhibitLowILImageMap",
975  "SignatureMitigationOptIn",
976  "AuditBlockNonMicrosoftBinaries",
977  "AuditBlockNonMicrosoftBinariesAllowStore",
978  "LoaderIntegrityContinuityEnabled",
979  "AuditLoaderIntegrityContinuity",
980  "EnableModuleTamperingProtection",
981  "EnableModuleTamperingProtectionNoInherit",
982  };
983  static const PCHAR flags2Text[] =
984  {
985  "EnableExportAddressFilter",
986  "AuditExportAddressFilter",
987  "EnableExportAddressFilterPlus",
988  "AuditExportAddressFilterPlus",
989  "EnableRopStackPivot",
990  "AuditRopStackPivot",
991  "EnableRopCallerCheck",
992  "AuditRopCallerCheck",
993  "EnableRopSimExec",
994  "AuditRopSimExec",
995  "EnableImportAddressFilter",
996  "AuditImportAddressFilter",
997  };
998 
999  list = list->Flink;
1000 
1001  status = IntKernVirtMemRead(pProc->EprocessAddress + WIN_KM_FIELD(Process, MitigationFlags),
1002  sizeof(flags),
1003  &flags,
1004  NULL);
1005  if (!INT_SUCCESS(status))
1006  {
1007  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x\n", status);
1008  continue;
1009  }
1010 
1011  status = IntKernVirtMemRead(pProc->EprocessAddress + WIN_KM_FIELD(Process, MitigationFlags2),
1012  sizeof(flags2),
1013  &flags2,
1014  NULL);
1015  if (!INT_SUCCESS(status))
1016  {
1017  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x\n", status);
1018  continue;
1019  }
1020 
1021  LOG("Dumping Flags for `%s` @ 0x%016llx\n", pProc->Name, pProc->EprocessAddress);
1022  NLOG("+ Mitigation Flags: 0x%08x\n", flags.Flags);
1023  for (DWORD i = 0; i < 32; i++)
1024  {
1025  if (0 == (flags.Flags & (1 << i)))
1026  {
1027  continue;
1028  }
1029 
1030  if (i >= ARRAYSIZE(flagsText))
1031  {
1032  NLOG("\t Bit %02d is set -> UNKNOWN\n", i);
1033  }
1034  else
1035  {
1036  NLOG("\t Bit %02d is set -> %s\n", i, flagsText[i]);
1037  }
1038  }
1039  NLOG("\n");
1040  NLOG("+ Mitigation Flags 2: 0x%08x\n", flags2.Flags);
1041  for (DWORD i = 0; i < 32; i++)
1042  {
1043  if (0 == (flags2.Flags & (1 << i)))
1044  {
1045  continue;
1046  }
1047 
1048  if (i >= ARRAYSIZE(flags2Text))
1049  {
1050  NLOG("\t Bit %02d is set -> UNKNOWN\n", i);
1051  }
1052  else
1053  {
1054  NLOG("\t Bit %02d is set -> %s\n", i, flags2Text[i]);
1055  }
1056  }
1057 
1058  NLOG("\n\n");
1059  }
1060 }
1061 
1062 
1063 INTSTATUS
1065  _In_ QWORD Eprocess,
1066  _Outptr_ void **Ptr
1067  )
1079 {
1080  // must be initialized because compiler assumes gGuest.WordSize might be 0
1082  static DWORD sizeofNeededEprocess = 0;
1083 
1084  if (0 == Eprocess)
1085  {
1087  }
1088 
1089  if (NULL == Ptr)
1090  {
1092  }
1093 
1094  // While sizeof(EPROCESS) < PAGE_SIZE - (aprox.) 0x20, the entire structure will be allocated in the same
1095  // page so we can map just the remaining page. However, an attacker might craft an EPROCESS structure at
1096  // page boundary. This ensures that all of our accesses inside the mapped EPROCESS are safe but it does
1097  // not fully detect EPROCESS structures that span two pages because the size we check is the maximum field
1098  // offset + guest word size, not the actual size of EPROCESS!
1099  if (0 == sizeofNeededEprocess)
1100  {
1101  for (WIN_KM_FIELD_PROCESS field = 0; field < winKmFieldProcessEnd; ++field)
1102  {
1103  if (sizeofNeededEprocess < gWinGuest->OsSpecificFields.Km.Process[field])
1104  {
1105  sizeofNeededEprocess = gWinGuest->OsSpecificFields.Km.Process[field];
1106  }
1107  }
1108  // now sizeofNeededEprocess is just the highest field offset, add word size
1109  sizeofNeededEprocess += gGuest.WordSize;
1110  }
1111 
1112  // It seems that `sizeofNeededEprocess += gGuest.WordSize;` causes some problems on certain OSes, for
1113  // example on a 2k12R2, sizeof(EPROCESS) is 0x700. The last field is the ExitStatus DWORD (at 0x6FC).
1114  // We will want to read 0x704, but if it is cross-page and the second page isn't present, we will fail.
1115  // Thus, until we have all the fields sizes in CAMI, we will try to decrement at most gGuest.WordSize / 2 times
1116  // and retry the mapping. If it succeeds, then that is our real size.
1117  for (BYTE i = 0; i <= gGuest.WordSize / 2; i++)
1118  {
1119  status = IntVirtMemMap(Eprocess, sizeofNeededEprocess, gGuest.Mm.SystemCr3, 0, Ptr);
1120  if (INT_SUCCESS(status))
1121  {
1122  break;
1123  }
1124 
1125  sizeofNeededEprocess--;
1126  }
1127 
1128  if (!INT_SUCCESS(status))
1129  {
1130  ERROR("[ERROR] Failed to map EPROCESS at address 0x%016llx, IntVirtMemMap failed: 0x%08x\n", Eprocess, status);
1131  }
1132 
1133  return status;
1134 }
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:300
_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:35
#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:90
QWORD SystemCr3
The Cr3 used to map the kernel.
Definition: guests.h:207
#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:280
int IntWinProcRbTreeNodeCompareUserCr3(RBNODE const *Left, RBNODE const *Right)
Definition: winprocesshp.c:689
struct _LIST_ENTRY * Flink
Definition: introlists.h:20
BOOLEAN IntWinVadDump(VAD const *Vad, void *Context)
Prints a VAD structure.
Definition: winvad.c:1698
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:1614
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
#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:53
#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:185
int INTSTATUS
The status data type.
Definition: introstatus.h:24
The end of the fields.
Definition: winguest.h:295
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:14
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:542
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:650
QWORD ParentEprocess
The EPROCESS of the parent process.
Definition: winprocess.h:89
#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:667
#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:123
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:84
QWORD Cr3
Process PDBR. Includes PCID.
Definition: winprocess.h:96
#define _Inout_
Definition: intro_sal.h:20
QWORD PsActiveProcessHead
Guest virtual address of the PsActiveProcessHead kernel variable.
Definition: winguest.h:821
PWIN_PROCESS_OBJECT IntWinProcFindObjectByEprocess(QWORD Eprocess)
Finds a process by the address of its _EPROCESS structure.
Definition: winprocesshp.c:23
RBNODE NodeEproc
Entry within gWinProcTreeEprocess (RB Tree).
Definition: winprocess.h:86
int IntWinProcRbTreeNodeCompareCr3(RBNODE const *Left, RBNODE const *Right)
Definition: winprocesshp.c:662
BOOLEAN Guest64
True if this is a 64-bit guest, False if it is a 32-bit guest.
Definition: guests.h:286
unsigned long long QWORD
Definition: intro_types.h:53
CHAR Name[IMAGE_BASE_NAME_LEN]
Process base name.
Definition: winprocess.h:106
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:97
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:428
INTSTATUS IntWinProcGetNameFromInternalEprocess(QWORD Eprocess, CHAR *Name)
Get a process name from the internal Introcore buffers.
Definition: winprocesshp.c:575
DWORD ProtectionMask
Protection mask: tells us what level of protection will be activated for this process.
Definition: winprocess.h:196
WIN_OPAQUE_FIELDS OsSpecificFields
OS-dependent and specific information (variables, offsets, etc).
Definition: winguest.h:843
#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:279
BYTE WordSize
Guest word size. Will be 4 for 32-bit guests and 8 for 64-bit guests.
Definition: guests.h:363
void IntWinProcDump(void)
Prints information about all the processes in the system.
Definition: winprocesshp.c:786
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:152
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:1494
DWORD Pid
Process ID (the one used by Windows).
Definition: winprocess.h:98
enum _WIN_KM_FIELD_PROCESS WIN_KM_FIELD_PROCESS
Indexes in the WIN_OPAQUE_FIELDS.Km.Process array, containing offsets inside the _EPROCESS structure...
#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:2009
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:726
uint32_t DWORD
Definition: intro_types.h:49
QWORD OriginalTokenPtr
Original Token pointer inside EPROCESS (should never change).
Definition: winprocess.h:241
INTSTATUS IntWinProcAdd(QWORD Eprocess, QWORD Aux)
Adds a new process to the Introcore list of processes.
Definition: winprocesshp.c:323
PWIN_PROCESS_OBJECT IntWinProcFindObjectByCr3(QWORD Cr3)
Finds a process by its kernel CR3.
Definition: winprocesshp.c:122
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:612
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:739
LIST_HEAD ProcessModules
List of process modules.
Definition: winprocess.h:68
void IntWinProcDumpVads(const char *ProcessName)
Prints information about the VADs loaded in a process.
Definition: winprocesshp.c:899
__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:85
MM Mm
Guest memory information, such as paging mode, system Cr3 value, etc.
Definition: guests.h:370
QWORD EprocessAddress
This will be the address of the ActiveProcess field.
Definition: winprocess.h:88
DWORD SystemProcess
TRUE if this is a system process.
Definition: winprocess.h:134
GUEST_STATE gGuest
The current guest state.
Definition: guests.c:48
#define _Function_class_(expr)
Definition: intro_sal.h:40
int IntWinProcRbTreeNodeCompareEproc(RBNODE const *Left, RBNODE const *Right)
Definition: winprocesshp.c:716
void IntWinProcDumpEgFlags(void)
Prints the mitigation flags of a process.
Definition: winprocesshp.c:927
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
#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:129
#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:296
RBNODE NodeCr3
Entry within gWinProcTreeCr3 (RB Tree).
Definition: winprocess.h:84
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
struct _WIN_OPAQUE_FIELDS::@207 Km
Kernel mode information.
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:81
Mitigation flags.
Definition: wddefs.h:1570
WCHAR * Path
The string which represents the user-mode module path.
Definition: winumpath.h:17