Bitdefender Hypervisor Memory Introspection
winobj.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 Bitdefender
3  * SPDX-License-Identifier: Apache-2.0
4  */
176 
177 #include "winobj.h"
178 #include "guests.h"
179 #include "swapmem.h"
180 #include "windrvobj.h"
181 #include "winpe.h"
182 
186 #define WIN_POOL_TAG_DIRECTORY 0x65726944 // Dire
187 
191 #define WIN_POOL_TAG_DIRECTORY_7 0xe5726944
192 
196 #define WIN_POOL_TAG_OBJECT 0x546a624f
197 
201 #define WIN_POOL_TAG_OBJECT_7 0xd46a624f
202 
208 typedef enum
209 {
215 } IM_FLG;
216 
217 
218 // Optional Header sizes
219 #define HEADER_SIZE_CREATOR_INFO64 0x20
220 #define HEADER_SIZE_CREATOR_INFO32 0x10
221 #define HEADER_SIZE_CREATOR_INFO(is64) (is64) ? HEADER_SIZE_CREATOR_INFO64 : HEADER_SIZE_CREATOR_INFO32
222 
223 #define HEADER_SIZE_NAME_INFO64 0x20
224 #define HEADER_SIZE_NAME_INFO32 0x10
225 #define HEADER_SIZE_NAME_INFO(is64) (is64) ? HEADER_SIZE_NAME_INFO64 : HEADER_SIZE_NAME_INFO32
226 
227 #define HEADER_SIZE_HANDLE_INFO64 0x10
228 #define HEADER_SIZE_HANDLE_INFO32 0x08
229 #define HEADER_SIZE_HANDLE_INFO(is64) (is64) ? HEADER_SIZE_HANDLE_INFO64 : HEADER_SIZE_HANDLE_INFO32
230 
231 #define HEADER_SIZE_QUOTA_INFO64 0x20
232 #define HEADER_SIZE_QUOTA_INFO32 0x10
233 #define HEADER_SIZE_QUOTA_INFO(is64) (is64) ? HEADER_SIZE_QUOTA_INFO64 : HEADER_SIZE_QUOTA_INFO32
234 
235 #define HEADER_SIZE_PROC_INFO64 0x10
236 #define HEADER_SIZE_PROC_INFO32 0x08
237 #define HEADER_SIZE_PROC_INFO(is64) (is64) ? HEADER_SIZE_PROC_INFO64 : HEADER_SIZE_PROC_INFO32
238 
239 
241 #define ROOT_DIR_POOL_HEADER_OFF64 0x60
242 #define ROOT_DIR_POOL_HEADER_OFF32 0x30
244 
245 
246 // Known type index values
247 #define TYPE_IDX_TYPE 2
248 
249 
250 #define OBJECT_DIR_ENTRY_COUNT 37
251 
252 
256 typedef struct _ROOT_SEARCH_CTX
257 {
259  void *SwapHandle;
263 
267 typedef struct _WINOBJ_SWAPCTX
268 {
270 
271  void *SwapHandle;
275 
276 #define ROOT_HINT_PTR_COUNT 3
277 
278 typedef struct _ROOT_HINT
282 {
286 
294 
296 static DWORD gRootCount = 0;
297 
300 
306 
308 static DWORD gFoundDrivers = 0;
309 
313 static BOOLEAN gStop = FALSE;
314 
319 
320 
321 static void
323  void
324  )
336 {
337  if (!gStop)
338  {
339  if ((0 == gDirEntriesToCheck) && IsListEmpty(&gSwapHandles) &&
341  {
342  ERROR("[ERROR] Finished parsing the root directory, but not all drivers were found. "
343  "`Driver` @ 0x%016llx `FileSystem` @ 0x%016llx\n",
345 
347 
349  gStop = TRUE;
350  }
351 
353  {
354  if (0 == gPendingDrivers)
355  {
356  LOG("[WINOBJ] Search over. `Driver` @ 0x%016llx `FileSystem` @ 0x%016llx. Pending drivers = 0\n",
358  gStop = TRUE;
360  }
361  else
362  {
363  LOG("[WINOBJ] Search not over. `Driver` @ 0x%016llx `FileSystem` @ 0x%016llx. Pending drivers = %u\n",
365  }
366  }
367  }
368 }
369 
370 
371 static BOOLEAN
373  void)
374 {
375  for (DWORD i = 0; i < gRootCount; i++)
376  {
377  if (gPossibleRootGvas[i].Waiting)
378  {
379  return FALSE;
380  }
381  }
382 
383  return TRUE;
384 }
385 
386 
387 static void
389  void
390  )
394 {
395  for (DWORD i = 0; i < gRootCount; i++)
396  {
397  if (NULL != gPossibleRootGvas[i].SwapHandle)
398  {
399  INTSTATUS status = IntSwapMemRemoveTransaction(gPossibleRootGvas[i].SwapHandle);
400  if (!INT_SUCCESS(status))
401  {
402  WARNING("[WARNING] IntSwapMemRemoveTransaction failed for 0x%016llx (Handle %p): 0x%08x\n",
403  gPossibleRootGvas[i].RootGva, gPossibleRootGvas[i].SwapHandle, status);
404  }
405 
406  gPossibleRootGvas[i].SwapHandle = NULL;
407  }
408 
409  gPossibleRootGvas[i].Waiting = FALSE;
410  }
411 
412  gRootCount = 0;
413 }
414 
415 
416 static INTSTATUS
418  _In_ QWORD ObjectGva,
419  _Out_ QWORD *BufferGva,
420  _Out_opt_ WORD *Length,
421  _Out_opt_ QWORD *ParentDirGva
422  )
437 {
438  INTSTATUS status;
439  QWORD infoMaskGva;
440  DWORD sizeToSubtract = 0;
441  BYTE infoMask = 0;
442  DWORD creatorInfoSize;
443 
444  // If the Name Info Header is present we have the following layout: Name Info, Creator Info (optional), Object
445  // Header, Object;
446  // We need to go back at least sizeof(OBJECT_HEADER_NAME_INFO) + sizeof(OBJECT_HEADER),
447  // but the Body field overlaps the object
448  if (gGuest.Guest64)
449  {
450  sizeToSubtract += OFFSET_OF(OBJECT_HEADER64, Body) + HEADER_SIZE_NAME_INFO64;
451  infoMaskGva = ObjectGva - OFFSET_OF(OBJECT_HEADER64, Body) + OFFSET_OF(OBJECT_HEADER64, InfoMask);
452  creatorInfoSize = HEADER_SIZE_CREATOR_INFO64;
453  }
454  else
455  {
456  sizeToSubtract += OFFSET_OF(OBJECT_HEADER32, Body) + HEADER_SIZE_NAME_INFO32;
457  infoMaskGva = ObjectGva - OFFSET_OF(OBJECT_HEADER32, Body) + OFFSET_OF(OBJECT_HEADER32, InfoMask);
458  creatorInfoSize = HEADER_SIZE_CREATOR_INFO32;
459  }
460 
461  status = IntKernVirtMemRead(infoMaskGva, sizeof(infoMask), &infoMask, NULL);
462  if (!INT_SUCCESS(status))
463  {
464  ERROR("[ERROR] IntKernVirtMemRead failed for 0x%016llx: 0x%08x\n", infoMaskGva, status);
465  return status;
466  }
467 
468  if (0 == (IM_FLG_NAME_INFO & infoMask))
469  {
470  return INT_STATUS_NOT_FOUND;
471  }
472 
473  // Add the creator info, if it exists
474  if (0 != (IM_FLG_CREATOR_INFO & infoMask))
475  {
476  sizeToSubtract += creatorInfoSize;
477  }
478 
479  if (gGuest.Guest64)
480  {
481  OBJECT_NAME64 objNameInfo = {0};
482 
483  status = IntKernVirtMemRead(ObjectGva - sizeToSubtract, sizeof(objNameInfo), &objNameInfo, NULL);
484  if (!INT_SUCCESS(status))
485  {
486  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x\n", status);
487  return status;
488  }
489 
490  *BufferGva = objNameInfo.Name.Buffer;
491  if (NULL != Length)
492  {
493  *Length = objNameInfo.Name.Length;
494  }
495 
496  if (NULL != ParentDirGva)
497  {
498  *ParentDirGva = objNameInfo.Directory;
499  }
500  }
501  else
502  {
503  OBJECT_NAME32 objNameInfo = {0};
504 
505  status = IntKernVirtMemRead(ObjectGva - sizeToSubtract, sizeof(objNameInfo), &objNameInfo, NULL);
506  if (!INT_SUCCESS(status))
507  {
508  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x\n", status);
509  return status;
510  }
511 
512  *BufferGva = objNameInfo.Name.Buffer;
513  if (NULL != Length)
514  {
515  *Length = objNameInfo.Name.Length;
516  }
517 
518  if (NULL != ParentDirGva)
519  {
520  *ParentDirGva = objNameInfo.Directory;
521  }
522  }
523 
524  return INT_STATUS_SUCCESS;
525 }
526 
527 
528 static INTSTATUS
530  _In_ WINOBJ_SWAPCTX *Context,
531  _In_ QWORD Cr3,
532  _In_ QWORD Gva,
533  _In_ QWORD Gpa,
534  _In_ void *Data,
535  _In_ DWORD DataSize,
536  _In_ DWORD Flags
537  )
568 {
569  QWORD next = 0;
570  QWORD drvObjGva = 0;
571  DWORD sizeToRead;
572  INTSTATUS status;
573  PWINOBJ_SWAPCTX pSwapCtx = Context;
574  SIZE_T iterationCount = 0;
575 
579  UNREFERENCED_PARAMETER(DataSize);
580 
581  // Remove this swap handle from the list
582  RemoveEntryList(&pSwapCtx->Link);
584 
585  if (gGuest.Guest64)
586  {
587  next = ((OBJECT_DIRECTORY_ENTRY64 *)Data)->Chain;
588  drvObjGva = ((OBJECT_DIRECTORY_ENTRY64 *)Data)->Object;
589  sizeToRead = sizeof(OBJECT_DIRECTORY_ENTRY64);
590  }
591  else
592  {
593  next = ((OBJECT_DIRECTORY_ENTRY32 *)Data)->Chain;
594  drvObjGva = ((OBJECT_DIRECTORY_ENTRY32 *)Data)->Object;
595  sizeToRead = sizeof(OBJECT_DIRECTORY_ENTRY32);
596  }
597 
598  if (IntWinDrvObjIsValidDriverObject(drvObjGva))
599  {
600  PWIN_DRIVER_OBJECT pDrvObj = NULL;
601 
602  status = IntWinDrvObjCreateFromAddress(drvObjGva, TRUE, &pDrvObj);
603  if (!INT_SUCCESS(status))
604  {
605  ERROR("[ERROR] IntWinDrvObjCreateDriverObject failed for 0x%016llx: 0x%08x\n", drvObjGva, status);
606  }
607  else
608  {
609  gFoundDrivers++;
610  }
611  }
612 
613  while (IS_KERNEL_POINTER_WIN(gGuest.Guest64, next))
614  {
615  union
616  {
619  } entry = { 0 };
620 
621  iterationCount++;
622 
623  status = IntVirtMemRead(next, sizeToRead, 0, &entry, NULL);
625  {
626  // The page is not present, fallback to swap mem read.
627  WINOBJ_SWAPCTX *pNextCtx;
628  void *swapHandle = NULL;
629 
630  pNextCtx = HpAllocWithTag(sizeof(*pNextCtx), IC_TAG_WINOBJ_SWAP);
631  if (NULL == pNextCtx)
632  {
634  }
635 
636  pNextCtx->Id = __LINE__;
637  pNextCtx->ObjectGva = next;
638  InsertTailList(&gSwapHandles, &pNextCtx->Link);
639 
640  status = IntSwapMemReadData(0,
641  next,
642  sizeToRead,
644  pNextCtx,
645  0,
647  NULL,
648  &swapHandle);
649  if (!INT_SUCCESS(status))
650  {
651  ERROR("[ERROR] IntSwapMemReadData failed: 0x%08x\n", status);
652  RemoveEntryList(&pNextCtx->Link);
654  }
655  else if (NULL != swapHandle)
656  {
657  gPendingDrivers++;
658  pNextCtx->SwapHandle = swapHandle;
659  }
660 
661  break;
662  }
663  else if (!INT_SUCCESS(status))
664  {
665  ERROR("[ERROR] IntVirtMemRead failed: 0x%08x\n", status);
666  break;
667  }
668 
669  // If we get here, the page is present.
670  if (gGuest.Guest64)
671  {
672  next = entry.as64.Chain;
673  drvObjGva = entry.as64.Object;
674  }
675  else
676  {
677  next = entry.as32.Chain;
678  drvObjGva = entry.as32.Object;
679  }
680 
681  if (IntWinDrvObjIsValidDriverObject(drvObjGva))
682  {
683  PWIN_DRIVER_OBJECT pDrvObj = NULL;
684 
685  status = IntWinDrvObjCreateFromAddress(drvObjGva, TRUE, &pDrvObj);
686  if (!INT_SUCCESS(status))
687  {
688  ERROR("[ERROR] IntWinDrvObjCreateDriverObject failed for 0x%016llx: 0x%08x\n", drvObjGva, status);
689  }
690  else
691  {
692  gFoundDrivers++;
693  }
694  }
695 
696  if (iterationCount >= 1024)
697  {
698  CRITICAL("[WARNING] Maximum iteration count reached. Will stop the list iteration at 0x%016llx\n", next);
699  break;
700  }
701  }
702 
703  // If this was an async call, check if it was the last and if we managed to find what we were searching for; if
704  // the call is synchronous the caller will do this check
705  if (0 != (SWAPMEM_FLAG_ASYNC_CALL & Flags))
706  {
707  gPendingDrivers--;
709  }
710 
711  return INT_STATUS_SUCCESS;
712 }
713 
714 
715 static INTSTATUS
717  _In_ WINOBJ_SWAPCTX *Context,
718  _In_ QWORD Cr3,
719  _In_ QWORD Gva,
720  _In_ QWORD Gpa,
721  _In_ void *Data,
722  _In_ DWORD DataSize,
723  _In_ DWORD Flags
724  )
759 {
760  PWINOBJ_SWAPCTX pCtx = Context;
761  QWORD objectGva = pCtx->ObjectGva;
762  INTSTATUS status = INT_STATUS_SUCCESS;
763 
767 
768  RemoveEntryList(&pCtx->Link);
770 
771  // Note: this is a guest read string, so we're not sure that it's NULL terminated. Use wstrncasecmp_len instead
772  // of wstrcasecmp.
773  if (0 == wstrncasecmp_len(Data, u"Driver", DataSize / 2, CWSTRLEN(u"Driver")))
774  {
775  LOG("[NAMESPACE] Found `Driver` directory @ 0x%016llx\n", objectGva);
776  gWinGuest->DriverDirectory = objectGva;
777  }
778  else if (0 == wstrncasecmp_len(Data, u"FileSystem", DataSize / 2, CWSTRLEN((u"FileSystem"))))
779  {
780  LOG("[NAMESPACE] Found `FileSystem` directory @ 0x%016llx\n", objectGva);
781  gWinGuest->FileSystemDirectory = objectGva;
782  }
783  else
784  {
786  }
787 
788  if (gGuest.Guest64)
789  {
790  QWORD entries[OBJECT_DIR_ENTRY_COUNT] = {0};
791 
792  status = IntKernVirtMemRead(objectGva, sizeof(entries), entries, NULL);
793  if (!INT_SUCCESS(status))
794  {
795  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x\n", status);
796  return status;
797  }
798 
799  for (DWORD i = 0; i < OBJECT_DIR_ENTRY_COUNT; i++)
800  {
801  if (IS_KERNEL_POINTER_WIN(gGuest.Guest64, entries[i]))
802  {
803  WINOBJ_SWAPCTX *pNextCtx;
804  void *swapHandle = NULL;
805 
806  pNextCtx = HpAllocWithTag(sizeof(*pNextCtx), IC_TAG_WINOBJ_SWAP);
807  if (NULL == pNextCtx)
808  {
809  continue;
810  }
811 
812  pNextCtx->Id = __LINE__;
813  pNextCtx->ObjectGva = entries[i];
814  InsertTailList(&gSwapHandles, &pNextCtx->Link);
815 
816  status = IntSwapMemReadData(0,
817  entries[i],
818  sizeof(OBJECT_DIRECTORY_ENTRY64),
820  pNextCtx,
821  0,
823  NULL,
824  &swapHandle);
825  if (!INT_SUCCESS(status))
826  {
827  ERROR("[ERROR] IntSwapMemReadData failed: 0x%08x\n", status);
828  RemoveEntryList(&pNextCtx->Link);
830  }
831  else if (NULL != swapHandle)
832  {
833  gPendingDrivers++;
834  pNextCtx->SwapHandle = swapHandle;
835  }
836  }
837  }
838  }
839  else
840  {
841  DWORD entries[OBJECT_DIR_ENTRY_COUNT] = {0};
842 
843  status = IntKernVirtMemRead(objectGva, sizeof(entries), entries, NULL);
844  if (!INT_SUCCESS(status))
845  {
846  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x\n", status);
847  return status;
848  }
849 
850  for (DWORD i = 0; i < OBJECT_DIR_ENTRY_COUNT; i++)
851  {
852  if (IS_KERNEL_POINTER_WIN(gGuest.Guest64, entries[i]))
853  {
854  WINOBJ_SWAPCTX *pNextCtx;
855  void *swapHandle = NULL;
856 
857  pNextCtx = HpAllocWithTag(sizeof(*pNextCtx), IC_TAG_WINOBJ_SWAP);
858  if (NULL == pNextCtx)
859  {
860  continue;
861  }
862 
863  pNextCtx->Id = __LINE__;
864  pNextCtx->ObjectGva = entries[i];
865  InsertTailList(&gSwapHandles, &pNextCtx->Link);
866 
867  status = IntSwapMemReadData(0,
868  entries[i],
869  sizeof(OBJECT_DIRECTORY_ENTRY32),
871  pNextCtx,
872  0,
874  NULL,
875  &swapHandle);
876  if (!INT_SUCCESS(status))
877  {
878  ERROR("[ERROR] IntSwapMemReadData failed: 0x%08x\n", status);
879  RemoveEntryList(&pNextCtx->Link);
881  }
882  else if (NULL != swapHandle)
883  {
884  gPendingDrivers++;
885  pNextCtx->SwapHandle = swapHandle;
886  }
887  }
888  }
889  }
890 
891  // If this was an async call, check if it was the last and if we managed to find what we were searching for; if
892  // the call is synchronous the caller will do this check
893  if (0 != (SWAPMEM_FLAG_ASYNC_CALL & Flags))
894  {
896  }
897 
898  return INT_STATUS_SUCCESS;
899 }
900 
901 
902 static INTSTATUS
904  _In_ WINOBJ_SWAPCTX *Context,
905  _In_ QWORD Cr3,
906  _In_ QWORD Gva,
907  _In_ QWORD Gpa,
908  _In_ void *Data,
909  _In_ DWORD DataSize,
910  _In_ DWORD Flags
911  )
935 {
936  QWORD objectGva;
937  INTSTATUS status = INT_STATUS_SUCCESS;
938  QWORD parentDirGva = 0;
939  QWORD nameBufferGva = 0;
940  WORD nameLength = 0;
941  PWINOBJ_SWAPCTX pDrvDirCtx;
942  PWINOBJ_SWAPCTX pCurrentCtx = Context;
943  void *swapHandle;
944 
949  UNREFERENCED_PARAMETER(DataSize);
950 
951  objectGva = pCurrentCtx->ObjectGva;
952 
953  RemoveEntryList(&pCurrentCtx->Link);
955 
956  status = IntWinObjGetObjectNameInfo(objectGva, &nameBufferGva, &nameLength, &parentDirGva);
957  if (!INT_SUCCESS(status))
958  {
959  ERROR("[ERROR] IntWinObjGetObjectNameInfo failed for 0x%016llx: 0x%08x\n", objectGva, status);
960  status = INT_STATUS_SUCCESS;
961  goto cleanup_and_exit;
962  }
963 
964  // Sanity check
965  if ((!IS_KERNEL_POINTER_WIN(gGuest.Guest64, nameBufferGva)) || nameLength >= 32)
966  {
967  status = INT_STATUS_SUCCESS;
968  goto cleanup_and_exit;
969  }
970 
971  pDrvDirCtx = HpAllocWithTag(sizeof(*pDrvDirCtx), IC_TAG_WINOBJ_SWAP);
972  if (NULL == pDrvDirCtx)
973  {
975  goto cleanup_and_exit;
976  }
977 
978  pDrvDirCtx->Id = __LINE__;
979  pDrvDirCtx->ObjectGva = objectGva;
980  InsertTailList(&gSwapHandles, &pDrvDirCtx->Link);
981 
982  status = IntSwapMemReadData(0,
983  nameBufferGva,
984  nameLength,
986  pDrvDirCtx,
987  0,
989  NULL,
990  &swapHandle);
991  if (!INT_SUCCESS(status))
992  {
993  ERROR("[ERROR] IntSwapMemReadData failed: 0x%08x\n", status);
994  RemoveEntryList(&pDrvDirCtx->Link);
996  }
997  else if (NULL != swapHandle)
998  {
999  pDrvDirCtx->SwapHandle = swapHandle;
1000  }
1001 
1002 cleanup_and_exit:
1003  // If this was an async call, check if it was the last and if we managed to find what we were searching for; if
1004  // the call is synchronous the caller will do this check
1005  if (0 != (SWAPMEM_FLAG_ASYNC_CALL & Flags))
1006  {
1008  }
1009 
1010  return status;
1011 }
1012 
1013 
1014 static INTSTATUS
1016  _In_ WINOBJ_SWAPCTX *Context,
1017  _In_ QWORD Cr3,
1018  _In_ QWORD Gva,
1019  _In_ QWORD Gpa,
1020  _In_ void *Data,
1021  _In_ DWORD DataSize,
1022  _In_ DWORD Flags
1023  )
1047 
1048 {
1049  QWORD next;
1050  QWORD objectGva;
1051  DWORD sizeToRead;
1052  INTSTATUS status = INT_STATUS_SUCCESS;
1053  PWINOBJ_SWAPCTX pDrvDirCtx = NULL;
1054  PWINOBJ_SWAPCTX pCurrentCtx = Context;
1055  void *swapHandle;
1056 
1060  UNREFERENCED_PARAMETER(DataSize);
1061  UNREFERENCED_PARAMETER(Flags);
1062 
1063  RemoveEntryList(&pCurrentCtx->Link);
1065 
1066  if (gGuest.Guest64)
1067  {
1069 
1070  next = pEntry->Chain;
1071  objectGva = pEntry->Object;
1072  sizeToRead = sizeof(OBJECT_DIRECTORY_ENTRY64);
1073  }
1074  else
1075  {
1077 
1078  next = pEntry->Chain;
1079  objectGva = pEntry->Object;
1080  sizeToRead = sizeof(OBJECT_DIRECTORY_ENTRY32);
1081  }
1082 
1083  // Don't bother if this is not a kernel pointer
1084  if (!IS_KERNEL_POINTER_WIN(gGuest.Guest64, objectGva))
1085  {
1086  goto next_entry;
1087  }
1088 
1089  pDrvDirCtx = HpAllocWithTag(sizeof(*pDrvDirCtx), IC_TAG_WINOBJ_SWAP);
1090  if (NULL == pDrvDirCtx)
1091  {
1093  goto next_entry;
1094  }
1095 
1096  pDrvDirCtx->Id = __LINE__;
1097  pDrvDirCtx->ObjectGva = objectGva;
1098  InsertTailList(&gSwapHandles, &pDrvDirCtx->Link);
1099 
1100  status = IntSwapMemReadData(0,
1101  objectGva,
1102  1, // The size doesn't really matter, we just want to make the page present
1104  pDrvDirCtx,
1105  0,
1107  NULL,
1108  &swapHandle);
1109  if (!INT_SUCCESS(status))
1110  {
1111  ERROR("[ERROR] IntSwapMemReadData failed: 0x%08x\n", status);
1112  RemoveEntryList(&pDrvDirCtx->Link);
1114  }
1115  else if (NULL != swapHandle)
1116  {
1117  pDrvDirCtx->SwapHandle = swapHandle;
1118  }
1119 
1120 next_entry:
1121 
1123  {
1124  WINOBJ_SWAPCTX *pDirEntryCtx;
1125  swapHandle = NULL;
1126 
1127  pDirEntryCtx = HpAllocWithTag(sizeof(*pDirEntryCtx), IC_TAG_WINOBJ_SWAP);
1128  if (NULL == pDirEntryCtx)
1129  {
1131  }
1132 
1133  pDirEntryCtx->Id = __LINE__;
1134  pDirEntryCtx->ObjectGva = next;
1135  InsertTailList(&gSwapHandles, &pDirEntryCtx->Link);
1136 
1137  status = IntSwapMemReadData(0,
1138  next,
1139  sizeToRead,
1141  pDirEntryCtx,
1142  0,
1144  NULL,
1145  &swapHandle);
1146  if (!INT_SUCCESS(status))
1147  {
1148  ERROR("[ERROR] IntSwapMemReadData failed: 0x%08x\n", status);
1149  RemoveEntryList(&pDirEntryCtx->Link);
1150  HpFreeAndNullWithTag(&pDirEntryCtx, IC_TAG_WINOBJ_SWAP);
1151 
1152  // We failed to inject the #PF, there is nothing else we can do other than consider this bucket "done"
1155  }
1156  else if (NULL != swapHandle)
1157  {
1158  pDirEntryCtx->SwapHandle = swapHandle;
1159  }
1160  }
1161  else
1162  {
1163  // This is the last entry in this bucket, mark it as done
1166  }
1167 
1168  return status;
1169 }
1170 
1171 
1172 static INTSTATUS
1174  _In_ ROOT_SEARCH_CTX *Context,
1175  _In_ QWORD Cr3,
1176  _In_ QWORD Gva,
1177  _In_ QWORD Gpa,
1178  _In_ void *Data,
1179  _In_ DWORD DataSize,
1180  _In_ DWORD Flags
1181  )
1203 {
1204  DWORD tag = *(DWORD *)Data;
1205  PROOT_SEARCH_CTX pCtx = Context;
1206  QWORD objGva = pCtx->RootGva;
1207  INTSTATUS status;
1208  QWORD parentDirGva = 0;
1209  QWORD nameBufferGva = 0;
1210  WORD nameLength = 0;
1211 
1214  UNREFERENCED_PARAMETER(DataSize);
1215  UNREFERENCED_PARAMETER(Flags);
1216 
1217  pCtx->SwapHandle = NULL;
1218  pCtx->Waiting = FALSE;
1219 
1220  if ((gGuest.OSVersion < 9200 && WIN_POOL_TAG_DIRECTORY_7 != tag) ||
1221  (gGuest.OSVersion >= 9200 && WIN_POOL_TAG_DIRECTORY != tag))
1222  {
1223  TRACE("[NAMESPACE] Skipping tag 0x%08x @ 0x%016llx for object 0x%016llx!\n",
1224  tag, Gva, objGva);
1225  status = INT_STATUS_NOT_NEEDED_HINT;
1226  goto _check_state_and_exit;
1227  }
1228 
1229  LOG("[NAMESPACE] Found tag 0x%08x @ 0x%016llx for object 0x%016llx!\n", tag, Gva, objGva);
1230 
1231  // We know this page is in memory because we get here only if it already was in memory, or we injected a #PF
1232  // to bring it in memory
1233  status = IntWinObjGetObjectNameInfo(objGva, &nameBufferGva, &nameLength, &parentDirGva);
1234  if (!INT_SUCCESS(status))
1235  {
1236  ERROR("[ERROR] IntWinObjGetObjectNameInfo failed for 0x%016llx: 0x%08x\n", objGva, status);
1237  goto _check_state_and_exit;
1238  }
1239 
1240  if (0 != parentDirGva)
1241  {
1242  TRACE("[NAMESPACE] Skipping object 0x%016llx because it's parent directory is not NULL\n", objGva);
1243  status = INT_STATUS_NOT_NEEDED_HINT;
1244  goto _check_state_and_exit;
1245  }
1246 
1247  if (2 != nameLength || !IS_KERNEL_POINTER_WIN(gGuest.Guest64, nameBufferGva))
1248  {
1249  status = INT_STATUS_NOT_NEEDED_HINT;
1250  goto _check_state_and_exit;
1251  }
1252 
1253  LOG("[NAMESPACE] Found Root Directory (`\\`) @ 0x%016llx!\n", objGva);
1255 
1256  // Root found, cancel all other transactions
1258 
1259  if (gGuest.Guest64)
1260  {
1261  QWORD entries[OBJECT_DIR_ENTRY_COUNT] = {0};
1262 
1263  status = IntKernVirtMemRead(objGva, sizeof(entries), entries, NULL);
1264  if (!INT_SUCCESS(status))
1265  {
1266  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x\n", status);
1267  goto _check_state_and_exit;
1268  }
1269 
1270  for (DWORD i = 0; i < OBJECT_DIR_ENTRY_COUNT; i++)
1271  {
1272  if (IS_KERNEL_POINTER_WIN(gGuest.Guest64, entries[i]))
1273  {
1274  WINOBJ_SWAPCTX *pDirEntryCtx;
1275  void *swapHandle = NULL;
1276 
1277  pDirEntryCtx = HpAllocWithTag(sizeof(*pDirEntryCtx), IC_TAG_WINOBJ_SWAP);
1278  if (NULL == pDirEntryCtx)
1279  {
1280  continue;
1281  }
1282 
1283  pDirEntryCtx->Id = __LINE__;
1284  pDirEntryCtx->ObjectGva = entries[i];
1285  InsertTailList(&gSwapHandles, &pDirEntryCtx->Link);
1286 
1287  status = IntSwapMemReadData(0,
1288  entries[i],
1289  sizeof(OBJECT_DIRECTORY_ENTRY64),
1291  pDirEntryCtx,
1292  0,
1294  NULL,
1295  &swapHandle);
1296  if (!INT_SUCCESS(status))
1297  {
1298  ERROR("[ERROR] IntSwapMemReadData failed: 0x%08x\n", status);
1299  RemoveEntryList(&pDirEntryCtx->Link);
1300  HpFreeAndNullWithTag(&pDirEntryCtx, IC_TAG_WINOBJ_SWAP);
1301 
1304  }
1305  else if (NULL != swapHandle)
1306  {
1307  pDirEntryCtx->SwapHandle = swapHandle;
1308  }
1309  }
1310  else
1311  {
1312  // This entry is empty, so there's nothing to check here
1315  }
1316  }
1317  }
1318  else
1319  {
1320  DWORD entries[OBJECT_DIR_ENTRY_COUNT] = {0};
1321 
1322  status = IntKernVirtMemRead(objGva, sizeof(entries), entries, NULL);
1323  if (!INT_SUCCESS(status))
1324  {
1325  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x\n", status);
1326  goto _check_state_and_exit;
1327  }
1328 
1329  for (DWORD i = 0; i < OBJECT_DIR_ENTRY_COUNT; i++)
1330  {
1331  if (IS_KERNEL_POINTER_WIN(gGuest.Guest64, entries[i]))
1332  {
1333  WINOBJ_SWAPCTX *pDirEntryCtx;
1334  void *swapHandle = NULL;
1335 
1336  pDirEntryCtx = HpAllocWithTag(sizeof(*pDirEntryCtx), IC_TAG_WINOBJ_SWAP);
1337  if (NULL == pDirEntryCtx)
1338  {
1340  continue;
1341  }
1342 
1343  pDirEntryCtx->Id = __LINE__;
1344  pDirEntryCtx->ObjectGva = entries[i];
1345  InsertTailList(&gSwapHandles, &pDirEntryCtx->Link);
1346 
1347  status = IntSwapMemReadData(0,
1348  entries[i],
1349  sizeof(OBJECT_DIRECTORY_ENTRY32),
1351  pDirEntryCtx,
1352  0,
1354  NULL,
1355  &swapHandle);
1356  if (!INT_SUCCESS(status))
1357  {
1358  ERROR("[ERROR] IntSwapMemReadData failed: 0x%08x\n", status);
1359  RemoveEntryList(&pDirEntryCtx->Link);
1360  HpFreeAndNullWithTag(&pDirEntryCtx, IC_TAG_WINOBJ_SWAP);
1361 
1364  }
1365  else if (NULL != swapHandle)
1366  {
1367  pDirEntryCtx->SwapHandle = swapHandle;
1368  }
1369  }
1370  else
1371  {
1372  // This entry is empty, so there's nothing to check here
1375  }
1376  }
1377  }
1378 
1379 _check_state_and_exit:
1381  {
1382  ERROR("[ERROR] Could not find ObpRootDirectoryObject!\n");
1383 
1386  }
1387  else
1388  {
1390  }
1391 
1392  return status;
1393 }
1394 
1395 
1396 INTSTATUS
1398  _In_ PROOT_HINT Hint,
1399  _Out_ QWORD *PossibleRoot
1400  )
1418 {
1419  INTSTATUS status = INT_STATUS_SUCCESS;
1420  WORD foundTypes = 0;
1421  const WORD expectedTypes = 1;
1422  QWORD root = 0;
1423  DWORD delta;
1424 
1425  if (NULL == Hint)
1426  {
1428  }
1429 
1430  if (NULL == PossibleRoot)
1431  {
1433  }
1434 
1436  {
1437  *PossibleRoot = gWinGuest->ObpRootDirectoryObject;
1439  }
1440 
1442 
1443  // ObpRootDirectoryObject is before, after or between two type objects from non-paged pool.
1444  // The actual layout is not the same on every OS version.
1445 
1446  for (DWORD i = 0; i < ROOT_HINT_PTR_COUNT; i++)
1447  {
1448  // We allow NULL pointers around the root
1449  if (0 == Hint->Pointers[i])
1450  {
1451  continue;
1452  }
1453 
1454  status = IntWinObjIsTypeObject(Hint->Pointers[i]);
1455  if (!INT_SUCCESS(status))
1456  {
1457  if ((0 == root) &&
1458  // the root and it's headers must be in the same page
1459  ((Hint->Pointers[i] & PAGE_MASK) == ((Hint->Pointers[i] - delta) & PAGE_MASK)))
1460  {
1461  root = Hint->Pointers[i];
1462  }
1463  else
1464  {
1465  *PossibleRoot = 0;
1467  }
1468  }
1469  else
1470  {
1471  foundTypes++;
1472  }
1473  }
1474 
1475  if ((foundTypes < expectedTypes) || !IS_KERNEL_POINTER_WIN(gGuest.Guest64, root))
1476  {
1478  }
1479 
1480  status = INT_STATUS_SUCCESS;
1481 
1482  for (size_t i = 0; i < ROOT_HINT_PTR_COUNT; i++)
1483  {
1484  if (root == Hint->Pointers[i])
1485  {
1486  LOG("[NAMESPACE] Found possible root @ 0x%016llx = 0x%016llx\n",
1487  Hint->FoundAt + i * gGuest.WordSize, Hint->Pointers[i]);
1488  }
1489  else
1490  {
1491  LOG("[NAMESPACE] Found type @ 0x%016llx = 0x%016llx\n",
1492  Hint->FoundAt + i * gGuest.WordSize, Hint->Pointers[i]);
1493  }
1494  }
1495 
1496  *PossibleRoot = root;
1497 
1498  return status;
1499 }
1500 
1501 
1502 INTSTATUS
1504  _In_ QWORD Gva
1505  )
1529 {
1530  POOL_HEADER poolHeader = {0};
1531  INTSTATUS status;
1532  DWORD poolTag;
1533  DWORD poolType;
1534 
1536  {
1537  return INT_STATUS_NOT_FOUND;
1538  }
1539 
1540  // It must be readable as it is in non-paged pool
1541  if (gGuest.Guest64)
1542  {
1543  OBJECT_TYPE64 typeObj = {0};
1544 
1545  status = IntKernVirtMemRead(Gva, sizeof(typeObj), &typeObj, NULL);
1546  if (!INT_SUCCESS(status))
1547  {
1548  return INT_STATUS_NOT_FOUND;
1549  }
1550 
1553  {
1554  return INT_STATUS_NOT_FOUND;
1555  }
1556 
1557  if (typeObj.Name.Length > typeObj.Name.MaximumLength)
1558  {
1559  return INT_STATUS_NOT_FOUND;
1560  }
1561 
1562  if ((!IS_KERNEL_POINTER_WIN(gGuest.Guest64, typeObj.TypeList.Blink)) ||
1564  {
1565  return INT_STATUS_NOT_FOUND;
1566  }
1567  }
1568  else
1569  {
1570  OBJECT_TYPE32 typeObj = {0};
1571 
1572  status = IntKernVirtMemRead(Gva, sizeof(typeObj), &typeObj, NULL);
1573  if (!INT_SUCCESS(status))
1574  {
1575  return INT_STATUS_NOT_FOUND;
1576  }
1577 
1580  {
1581  return INT_STATUS_NOT_FOUND;
1582  }
1583 
1584  if (typeObj.Name.Length > typeObj.Name.MaximumLength)
1585  {
1586  return INT_STATUS_NOT_FOUND;
1587  }
1588 
1589  if ((!IS_KERNEL_POINTER_WIN(gGuest.Guest64, typeObj.TypeList.Blink)) ||
1591  {
1592  return INT_STATUS_NOT_FOUND;
1593  }
1594  }
1595 
1596  status = IntWinObjGetPoolHeaderForObject(Gva, &poolHeader);
1597  if (!INT_SUCCESS(status))
1598  {
1599  TRACE("[INFO] IntWinObjGetPoolHeaderForObject failed for 0x%016llx: 0x%08x\n", Gva, status);
1600  return INT_STATUS_NOT_FOUND;
1601  }
1602 
1603  poolTag = gGuest.Guest64 ? poolHeader.Header64.PoolTag : poolHeader.Header32.PoolTag;
1604 
1605  if ((gGuest.OSVersion < 9200 && WIN_POOL_TAG_OBJECT_7 != poolTag) ||
1606  (gGuest.OSVersion >= 9200 && WIN_POOL_TAG_OBJECT != poolTag))
1607  {
1608  return INT_STATUS_NOT_FOUND;
1609  }
1610 
1611  poolType = gGuest.Guest64 ? poolHeader.Header64.PoolType : poolHeader.Header32.PoolType;
1612 
1613  {
1614  BOOLEAN oldMatch = FALSE;
1615  BOOLEAN newMatch = FALSE;
1616 
1617  if (0 != (BIT(NonPagedPool) & poolType) &&
1618  DontUseThisType != poolType &&
1619  DontUseThisTypeSession != poolType)
1620  {
1621  oldMatch = TRUE;
1622  }
1623 
1624  // An _OBJECT_TYPE is allocated with the NonPagedPoolMustSucceed pool type.
1625  if (poolType != NonPagedPoolMustSucceed)
1626  {
1627  newMatch = FALSE;
1628  }
1629 
1630  if (oldMatch != newMatch)
1631  {
1632  CRITICAL("[CRITICAL] [ERROR] old (%u) != new (%u) pool type = 0x%08x GLA = 0x%016llx\n",
1633  oldMatch, newMatch, poolType, Gva);
1634  }
1635  }
1636 
1637  // An _OBJECT_TYPE is allocated with the NonPagedPoolMustSucceed pool type.
1638  if (poolType != NonPagedPoolMustSucceed)
1639  {
1640  return INT_STATUS_NOT_FOUND;
1641  }
1642 
1643  return INT_STATUS_SUCCESS;
1644 }
1645 
1646 
1647 INTSTATUS
1649  _In_ QWORD ObjectGva,
1650  _Out_ POOL_HEADER *PoolHeader
1651  )
1668 {
1669  INTSTATUS status;
1670  DWORD delta;
1671 
1672  if (!IS_KERNEL_POINTER_WIN(gGuest.Guest64, ObjectGva))
1673  {
1675  }
1676 
1677  if (NULL == PoolHeader)
1678  {
1680  }
1681 
1682  if (gGuest.Guest64)
1683  {
1686  }
1687  else
1688  {
1691  }
1692 
1693  if (!IS_KERNEL_POINTER_WIN(gGuest.Guest64, ObjectGva - delta))
1694  {
1695  return INT_STATUS_NOT_FOUND;
1696  }
1697 
1698  status = IntKernVirtMemRead(ObjectGva - delta,
1699  gGuest.Guest64 ? sizeof(POOL_HEADER64) : sizeof(POOL_HEADER32),
1700  PoolHeader, NULL);
1701  if (!INT_SUCCESS(status))
1702  {
1703  TRACE("[INFO] IntKernVirtMemRead failed for 0x%016llx: 0x%08x\n", ObjectGva - delta, status);
1704  }
1705 
1706  return status;
1707 }
1708 
1709 
1710 static void
1712  void
1713  )
1717 {
1718  memset(gPossibleRootGvas, 0, sizeof(gPossibleRootGvas));
1719  gRootCount = 0;
1721  gStop = FALSE;
1722  InitializeListHead(&gSwapHandles);
1723 }
1724 
1725 
1726 static INTSTATUS
1728  void
1729  )
1742 {
1743  //
1744  // This could be merged into IntWinGuestFindDriversNamespace and handle everything in one place, but that will make
1745  // everything harder to read and more error-prone
1746  //
1747 
1748  IMAGE_SECTION_HEADER sec = {0};
1749  INTSTATUS status;
1750  DWORD pageCount;
1751 
1753 
1754  status = IntPeGetSectionHeadersByName(gGuest.KernelVa, NULL, ".data", 1, gGuest.Mm.SystemCr3, &sec, NULL);
1755  if (!INT_SUCCESS(status))
1756  {
1757  ERROR("[ERROR] IntPeGetSectionHeadersByName failed for `.data`: 0x%08x\n", status);
1758  return status;
1759  }
1760 
1761  pageCount = ROUND_UP(sec.Misc.VirtualSize, PAGE_SIZE) / PAGE_SIZE;
1762  for (size_t i = 0; i < pageCount; i++)
1763  {
1764  QWORD targetGva = gGuest.KernelVa + sec.VirtualAddress + i * PAGE_SIZE;
1765  PBYTE page = NULL;
1766  DWORD ptrCount;
1767 
1768  status = IntVirtMemMap(targetGva, PAGE_SIZE, 0, 0, &page);
1769  if (!INT_SUCCESS(status))
1770  {
1771  ERROR("[ERROR] IntVirtMemMap failed for 0x%016llx: 0x%08x\n", targetGva, status);
1772  continue;
1773  }
1774 
1775  if (i == pageCount - 1)
1776  {
1777  ptrCount = (sec.Misc.VirtualSize & PAGE_OFFSET) / gGuest.WordSize;
1778  }
1779  else
1780  {
1781  ptrCount = PAGE_SIZE / gGuest.WordSize;
1782  }
1783 
1784  for (DWORD j = 0; j < ptrCount; j++)
1785  {
1786  ROOT_HINT hint = {0};
1787  QWORD root = 0;
1788 
1789  hint.FoundAt = targetGva;
1790 
1791  // Make sure we don't try to read pointers from two pages when we mapped only one
1792  if (j + ROOT_HINT_PTR_COUNT < ptrCount)
1793  {
1794  for (DWORD k = 0; k < ROOT_HINT_PTR_COUNT; k++)
1795  {
1796  hint.Pointers[k] = gGuest.Guest64 ? ((QWORD *)page)[j + k] : ((DWORD *)page)[j + k];
1797  }
1798  }
1799  // There's no point in doing this for the last page
1800  else if (i < ptrCount - 1)
1801  {
1802  if (gGuest.Guest64)
1803  {
1804  status = IntKernVirtMemRead(hint.FoundAt, sizeof(hint.Pointers), hint.Pointers, NULL);
1805  }
1806  else
1807  {
1808  DWORD temp[ROOT_HINT_PTR_COUNT] = {0};
1809 
1810  status = IntKernVirtMemRead(hint.FoundAt, sizeof(temp), temp, NULL);
1811  for (DWORD k = 0; k < ROOT_HINT_PTR_COUNT; k++)
1812  {
1813  hint.Pointers[k] = temp[k];
1814  }
1815  }
1816  if (!INT_SUCCESS(status))
1817  {
1818  ERROR("[ERROR] IntKernVirtMemRead failed for 0x%016llx: 0x%08x\n", hint.FoundAt, status);
1819  goto _continue;
1820  }
1821  }
1822 
1823  status = IntWinObjFindRootDirectory(&hint, &root);
1824  if (!INT_SUCCESS(status) && INT_STATUS_NOT_FOUND != status)
1825  {
1826  ERROR("[ERROR] IntWinObjFindRootDirectory for 0x%016llx: 0x%08x\n", hint.FoundAt, status);
1827  }
1828  else if (INT_STATUS_SUCCESS == status)
1829  {
1830  if (gRootCount < ARRAYSIZE(gPossibleRootGvas))
1831  {
1832  gPossibleRootGvas[gRootCount].RootGva = root;
1833  gPossibleRootGvas[gRootCount].Waiting = TRUE;
1834  gPossibleRootGvas[gRootCount].SwapHandle = NULL;
1835  gRootCount++;
1836  }
1837  else
1838  {
1839  break;
1840  }
1841  }
1842 
1843 _continue:
1844  targetGva += gGuest.WordSize;
1845  }
1846 
1847  IntVirtMemUnmap(&page);
1848  }
1849 
1850  return INT_STATUS_SUCCESS;
1851 }
1852 
1853 
1854 INTSTATUS
1856  void
1857  )
1929 {
1930  IMAGE_SECTION_HEADER sec = {0};
1931  INTSTATUS status;
1932  DWORD ptrCount;
1933  BOOLEAN useBuffer = TRUE;
1934 
1936  {
1937  WARNING("[WARNING] A find for drivers namespace is already in progress... "
1938  "root = %u, pending = %u, found = %u\n",
1939  gRootCount,
1941  gFoundDrivers);
1942 
1944  }
1945 
1947 
1948 _retry:
1949  if (!useBuffer)
1950  {
1951  TRACE("[NAMESPACE] Kernel buffer not present, will fetch objects directly from memory!\n");
1952 
1954  if (!INT_SUCCESS(status))
1955  {
1956  ERROR("[ERROR] IntWinGuestFindDriversNamespaceNoBuffer failed: 0x%08x\n", status);
1957  }
1958 
1959  // failure or not, we need to go through this check
1960  goto _check_roots;
1961  }
1962 
1964  gGuest.Mm.SystemCr3, &sec, NULL);
1965  if (!INT_SUCCESS(status))
1966  {
1967  ERROR("[ERROR] IntPeGetSectionHeadersByName failed for `.data`: 0x%08x\n", status);
1968  goto _check_roots;
1969  }
1970 
1971  ptrCount = sec.Misc.VirtualSize / gGuest.WordSize - ROOT_HINT_PTR_COUNT;
1972 
1973  for (DWORD i = 0; i < ptrCount; i++)
1974  {
1975  DWORD bufferOffset = sec.VirtualAddress + i * gGuest.WordSize;
1976  void *target;
1977  ROOT_HINT hint;
1978  QWORD root = 0;
1979 
1980  if (bufferOffset > gWinGuest->KernelBufferSize)
1981  {
1982  ERROR("[CRITICAL ERROR] RVA 0x%08x is outside the kernel buffer (size = 0x%08x)\n",
1983  bufferOffset, gWinGuest->KernelBufferSize);
1984  break;
1985  }
1986 
1987  target = gWinGuest->KernelBuffer + bufferOffset;
1988  hint.FoundAt = gGuest.KernelVa + bufferOffset;
1989 
1990  for (DWORD k = 0; k < ROOT_HINT_PTR_COUNT; k++)
1991  {
1992  hint.Pointers[k] = gGuest.Guest64 ? ((QWORD *)target)[k] : ((DWORD *)target)[k];
1993  }
1994 
1995  status = IntWinObjFindRootDirectory(&hint, &root);
1996  if (!INT_SUCCESS(status) && INT_STATUS_NOT_FOUND != status)
1997  {
1998  ERROR("[ERROR] IntWinObjFindRootDirectory for 0x%016llx: 0x%08x\n", hint.FoundAt, status);
1999  }
2000  else if (INT_STATUS_SUCCESS == status)
2001  {
2002  if (gRootCount < ARRAYSIZE(gPossibleRootGvas))
2003  {
2004  gPossibleRootGvas[gRootCount].RootGva = root;
2005  gPossibleRootGvas[gRootCount].Waiting = TRUE;
2006  gPossibleRootGvas[gRootCount].SwapHandle = NULL;
2007  gRootCount++;
2008  }
2009  else
2010  {
2011  break;
2012  }
2013  }
2014  }
2015 
2016  if (0 == gRootCount && useBuffer)
2017  {
2018  WARNING("[WINOBJ] Found 0 possible root pointers inside the kernel buffer, will retry without it!\n");
2019  useBuffer = FALSE;
2020  goto _retry;
2021  }
2022 
2023 _check_roots:
2024  LOG("[NAMESPACE] Will check %d possible root pointers...\n", gRootCount);
2025 
2026  for (DWORD i = 0; i < gRootCount; i++)
2027  {
2029  QWORD root = gPossibleRootGvas[i].RootGva;
2030 
2031  TRACE("[NAMESPACE] Trying 0x%016llx (%d) with 0x%016llx...\n",
2032  root, i, root - poolHeaderOffset + OFFSET_OF(POOL_HEADER32, PoolTag));
2033 
2034  // It is ok to use the field offset of PoolTag inside POOL_HEADER32 because it is the same offset as
2035  // POOL_HEADER64.
2036  status = IntSwapMemReadData(0,
2037  root - poolHeaderOffset + OFFSET_OF(POOL_HEADER32, PoolTag),
2038  sizeof(DWORD),
2040  &gPossibleRootGvas[i],
2041  0,
2043  NULL,
2044  &gPossibleRootGvas[i].SwapHandle);
2045  if (!INT_SUCCESS(status))
2046  {
2047  ERROR("[ERROR] IntSwapMemReadData failed for 0x%016llx: 0x%08x\n", root - poolHeaderOffset, status);
2048  gPossibleRootGvas[i].Waiting = FALSE;
2049  }
2050 
2051  // If the root was already found in a sync manner, bail out now (all transactions were canceled
2052  // in IntWinObjHandleRootDirTagInMemory)
2054  {
2055  return INT_STATUS_SUCCESS;
2056  }
2057  }
2058 
2059  status = INT_STATUS_SUCCESS;
2060 
2061  if (0 == gRootCount || (IntWinObjIsRootSearchOver() && 0 == gWinGuest->ObpRootDirectoryObject))
2062  {
2063  ERROR("[ERROR] Could not find ObpRootDirectoryObject!\n");
2064 
2067 
2068  status = INT_STATUS_NOT_FOUND;
2069  }
2070  else
2071  {
2073  }
2074 
2075  return status;
2076 }
2077 
2078 
2079 void
2081  void
2082  )
2089 {
2090  PLIST_ENTRY entry = gSwapHandles.Flink;
2091  DWORD remCount = 0;
2092 
2093  // First, the root transactions (if any)
2095  TRACE("[WINOBJ] Root transactions removed: %d\n", gRootCount);
2096 
2097  if (NULL == entry)
2098  {
2099  TRACE("[WINOBJ] No swap handles are present, nothing to clean\n");
2100  return;
2101  }
2102 
2103  // Now, everything else
2104  while (entry != &gSwapHandles)
2105  {
2106  PWINOBJ_SWAPCTX pCtx = CONTAINING_RECORD(entry, WINOBJ_SWAPCTX, Link);
2107  INTSTATUS status;
2108 
2109  entry = entry->Flink;
2110 
2111  TRACE("[WINOBJ] Removing swap handle %p for %llx ID = %u\n",
2112  pCtx->SwapHandle, pCtx->ObjectGva, pCtx->Id);
2113 
2114  RemoveEntryList(&pCtx->Link);
2115 
2116  status = IntSwapMemRemoveTransaction(pCtx->SwapHandle);
2117  if (!INT_SUCCESS(status))
2118  {
2119  ERROR("[ERROR] IntSwapMemRemoveTransaction failed: 0x%08x\n", status);
2120  }
2121 
2123  remCount++;
2124  }
2125 
2126  TRACE("[WINOBJ] Queued transactions removed: %d\n", remCount);
2127  CRITICAL("[WINOBJ] Cleanup done with %u pending drivers. Found = %u\n", gPendingDrivers, gFoundDrivers);
2128 }
DWORD Id
The ID of this object (used for debugging).
Definition: winobj.c:273
#define INT_STATUS_PAGE_NOT_PRESENT
Indicates that a virtual address is not present.
Definition: introstatus.h:438
#define _Out_
Definition: intro_sal.h:22
_Bool BOOLEAN
Definition: intro_types.h:58
#define CONTAINING_RECORD(List, Type, Member)
Definition: introlists.h:36
#define ROUND_UP(what, to)
Definition: introdefs.h:158
DWORD PoolType
Definition: wddefs.h:441
static DWORD gFoundDrivers
The number of found driver objects.
Definition: winobj.c:308
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
INTSTATUS IntVirtMemUnmap(void **HostPtr)
Unmaps a memory range previously mapped with IntVirtMemMap.
Definition: introcore.c:2234
static INTSTATUS IntWinObjParseDriverDirectory(WINOBJ_SWAPCTX *Context, QWORD Cr3, QWORD Gva, QWORD Gpa, void *Data, DWORD DataSize, DWORD Flags)
This callback is invoked for namespace entries that may represent driver directories.
Definition: winobj.c:716
QWORD FileSystemDirectory
Guest virtual address of the FileSystem namespace directory.
Definition: winguest.h:840
DWORD PoolTag
Definition: wddefs.h:420
uint8_t BYTE
Definition: intro_types.h:47
#define OFFSET_OF(Type, Member)
Definition: introlists.h:33
WINDOWS_GUEST * gWinGuest
Global variable holding the state of a Windows guest.
Definition: winguest.c:37
void * SwapHandle
The swap handle used for this search. NULL if no page swap-in is needed.
Definition: winobj.c:271
#define _In_
Definition: intro_sal.h:21
DWORD DefaultObject
Definition: wddefs.h:1473
QWORD SystemCr3
The Cr3 used to map the kernel.
Definition: guests.h:211
#define INT_STATUS_SUCCESS
Definition: introstatus.h:54
#define ROOT_DIR_POOL_HEADER_OFF64
The size of the headers before a Root Directory allocation on 64-bit Windows.
Definition: winobj.c:241
#define BIT(x)
Definition: common.h:68
uint16_t WORD
Definition: intro_types.h:48
QWORD Chain
Gva to the next _OBJECT_DIRECTORY_ENTRY, may be NULL.
Definition: wddefs.h:1494
QWORD Object
Pointer to the object, may be NULL.
Definition: wddefs.h:1495
POOL_HEADER32 Header32
Definition: wddefs.h:462
#define ROOT_DIR_POOL_HEADER_OFF32
The size of the headers before a Root Directory allocation on 32-bit Windows.
Definition: winobj.c:243
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
The _POOL_HEADER structure used by 32-bit guests.
Definition: wddefs.h:403
static LIST_ENTRY gSwapHandles
List of all the swap handles used by the namespace parser.
Definition: winobj.c:318
#define INT_SUCCESS(Status)
Definition: introstatus.h:42
static BOOLEAN IsListEmpty(const LIST_ENTRY *ListHead)
Definition: introlists.h:78
struct _ROOT_SEARCH_CTX * PROOT_SEARCH_CTX
A critical structure was not found inside the guest kernel.
Definition: intro_types.h:2441
void IntWinObjCleanup(void)
Cleans up any resources allocated by the object search.
Definition: winobj.c:2080
#define PAGE_OFFSET
Definition: pgtable.h:32
#define ARRAYSIZE(A)
Definition: introdefs.h:101
INTSTATUS IntWinDrvObjCreateFromAddress(QWORD GuestAddress, BOOLEAN StaticDetected, PWIN_DRIVER_OBJECT *DriverObject)
Creates a new driver object.
Definition: windrvobj.c:227
static INTSTATUS IntWinObjHandleDirectoryEntryInMemory(WINOBJ_SWAPCTX *Context, QWORD Cr3, QWORD Gva, QWORD Gpa, void *Data, DWORD DataSize, DWORD Flags)
This callback is invoked for each object contained in the root namespace.
Definition: winobj.c:1015
#define INT_STATUS_NOT_NEEDED_HINT
Definition: introstatus.h:317
#define ERROR(fmt,...)
Definition: glue.h:62
UNICODE_STRING64 Name
Definition: wddefs.h:1449
static INTSTATUS IntWinGuestFindDriversNamespaceNoBuffer(void)
Runs the driver object namespace search ignoring the gGuest.KernelBuffer and reading the data directl...
Definition: winobj.c:1727
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
struct _ROOT_HINT * PROOT_HINT
#define HEADER_SIZE_CREATOR_INFO64
32-bit _OBJECT_HEADER_CREATOR_INFO size.
Definition: winobj.c:219
#define HpAllocWithTag(Len, Tag)
Definition: glue.h:516
struct _WINOBJ_SWAPCTX * PWINOBJ_SWAPCTX
static DWORD gPendingDrivers
The count of pending driver objects to be checked.
Definition: winobj.c:305
DWORD Buffer
The guest virtual address at which the wide-character string is located.
Definition: wddefs.h:129
int INTSTATUS
The status data type.
Definition: introstatus.h:24
#define WIN_POOL_TAG_DIRECTORY_7
Allocation tag for the _OBJECT_DIRECTORY Windows 7 kernel structure.
Definition: winobj.c:191
#define HEADER_SIZE_CREATOR_INFO32
64-bit _OBJECT_HEADER_CREATOR_INFO size.
Definition: winobj.c:220
DWORD OSVersion
Os version.
Definition: guests.h:281
#define INT_STATUS_NOT_FOUND
Definition: introstatus.h:284
#define ROOT_HINT_PTR_COUNT
The number of hint pointers around a root candidate.
Definition: winobj.c:276
UNICODE_STRING64 Name
The object name.
Definition: wddefs.h:1521
struct _OBJECT_DIRECTORY_ENTRY32 OBJECT_DIRECTORY_ENTRY32
An OBJECT_DIRECTORY_ENTRY64 structure used by 32-bit guests.
UINT16 MaximumLength
The size, in bytes, allocated for Buffer.
Definition: wddefs.h:125
UNICODE_STRING32 Name
Definition: wddefs.h:1472
static ROOT_SEARCH_CTX gPossibleRootGvas[32]
The possible addresses at which the root directory may be located.
Definition: winobj.c:293
An OBJECT_DIRECTORY_ENTRY64 structure used by 32-bit guests.
Definition: wddefs.h:1505
DWORD Flink
Definition: wddefs.h:155
struct _ROOT_HINT ROOT_HINT
Hint structure used to search for possible object namespace root directory entries.
Set if _OBJECT_HEADER_NAME_INFO is present.
Definition: winobj.c:211
LIST_ENTRY64 TypeList
Definition: wddefs.h:1448
#define LOG(fmt,...)
Definition: glue.h:61
QWORD DefaultObject
Definition: wddefs.h:1450
Set if _OBJECT_HEADER_HANDLE_INFO is present.
Definition: winobj.c:212
QWORD Pointers[ROOT_HINT_PTR_COUNT]
Pointers around the candidate.
Definition: winobj.c:284
DWORD Blink
Definition: wddefs.h:155
#define WIN_POOL_HEADER_SIZE64
The size of a pool header on 64-bit Windows.
Definition: wddefs.h:467
#define IC_TAG_WINOBJ_SWAP
Winobj swap handle.
Definition: memtags.h:109
static DWORD gRootCount
The number of valid entries inside the gPossibleRootGvas array.
Definition: winobj.c:296
INTSTATUS IntSwapMemRemoveTransaction(void *Transaction)
Remove a transaction.
Definition: swapmem.c:942
An _OBJECT_TYPE structure used by 32-bit guests.
Definition: wddefs.h:1469
IM_FLG
Info Mask flags from the Object Header.
Definition: winobj.c:208
An _OBJECT_HEADER_NAME_INFO structure used by 32-bit guests.
Definition: wddefs.h:1528
#define _Out_opt_
Definition: intro_sal.h:30
POOL_HEADER64 Header64
Definition: wddefs.h:463
static void IntWinObjCheckDrvDirSearchState(void)
Checks if the search is still going, or if it finished with success or with an error.
Definition: winobj.c:322
uint8_t * PBYTE
Definition: intro_types.h:47
A context structure used to pass information between the various callbacks that search for an object...
Definition: winobj.c:267
static BOOLEAN RemoveEntryList(LIST_ENTRY *Entry)
Definition: introlists.h:87
Holds information about a driver object.
Definition: windrvobj.h:13
QWORD Flink
Definition: wddefs.h:164
UINT16 Length
The length, in bytes, of the string in Buffer, not including the NULL terminator, if any...
Definition: wddefs.h:123
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
#define OBJECT_DIR_ENTRY_COUNT
The maximum number of entries in an object directory.
Definition: winobj.c:250
#define HEADER_SIZE_NAME_INFO64
32-bit _OBJECT_HEADER_NAME_INFO size.
Definition: winobj.c:223
struct _ROOT_SEARCH_CTX ROOT_SEARCH_CTX
A context structure used to pass information between the various callbacks that search for a Root Dir...
struct _WINOBJ_SWAPCTX WINOBJ_SWAPCTX
A context structure used to pass information between the various callbacks that search for an object...
DWORD Directory
Pointer to the _OBJECT_DIRECTORY that owns this.
Definition: wddefs.h:1530
#define TRUE
Definition: intro_types.h:30
UINT32 VirtualAddress
Definition: winpe.h:85
#define IS_KERNEL_POINTER_WIN(is64, p)
Checks if a guest virtual address resides inside the Windows kernel address space.
Definition: wddefs.h:76
#define TRACE(fmt,...)
Definition: glue.h:58
#define WIN_POOL_TAG_DIRECTORY
Allocation tag for the _OBJECT_DIRECTORY Windows kernel structure.
Definition: winobj.c:186
#define HpFreeAndNullWithTag(Add, Tag)
Definition: glue.h:517
Set if _OBJECT_HEADER_PROCESS_INFO is present.
Definition: winobj.c:214
QWORD KernelVa
The guest virtual address at which the kernel image.
Definition: guests.h:283
DWORD PoolType
Definition: wddefs.h:412
union _IMAGE_SECTION_HEADER::@214 Misc
BYTE WordSize
Guest word size. Will be 4 for 32-bit guests and 8 for 64-bit guests.
Definition: guests.h:367
Set if _OBJECT_HEADER_CREATOR_INFO is present.
Definition: winobj.c:210
static void InsertTailList(LIST_ENTRY *ListHead, LIST_ENTRY *Entry)
Definition: introlists.h:135
static void IntWinObjCancelRootTransactions(void)
Cancels any pending swap memory reads left for the root directory.
Definition: winobj.c:388
#define SWAPMEM_FLAG_ASYNC_CALL
Definition: swapmem.h:12
static DWORD gDirEntriesToCheck
The number of directory entries left to check.
Definition: winobj.c:299
#define WARNING(fmt,...)
Definition: glue.h:60
DWORD PoolTag
Definition: wddefs.h:447
static void InitializeListHead(LIST_ENTRY *ListHead)
Definition: introlists.h:69
#define PAGE_SIZE
Definition: common.h:70
#define UNREFERENCED_PARAMETER(P)
Definition: introdefs.h:29
INTSTATUS IntWinObjIsTypeObject(QWORD Gva)
Checks if the supplied guest memory location holds a valid type object.
Definition: winobj.c:1503
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
INTSTATUS IntWinObjGetPoolHeaderForObject(QWORD ObjectGva, POOL_HEADER *PoolHeader)
Reads the _POOL_HEADER structure for a given kernel object.
Definition: winobj.c:1648
LIST_ENTRY32 TypeList
Definition: wddefs.h:1471
#define WIN_POOL_TAG_OBJECT
Allocation tag for the _OBJECT_TYPE Windows kernel structure.
Definition: winobj.c:196
void * SwapHandle
Definition: winobj.c:259
#define WIN_POOL_HEADER_SIZE32
The size of a pool header on 32-bit Windows.
Definition: wddefs.h:466
#define WIN_POOL_TAG_OBJECT_7
Allocation tag for the _OBJECT_TYPE Windows 7 kernel structure.
Definition: winobj.c:201
WORD MaximumLength
The size, in bytes, allocated for Buffer.
Definition: wddefs.h:141
static INTSTATUS IntWinObjHandleDriverDirectoryEntryInMemory(WINOBJ_SWAPCTX *Context, QWORD Cr3, QWORD Gva, QWORD Gpa, void *Data, DWORD DataSize, DWORD Flags)
This callback is invoked for namespace directory entries that may represent driver objects...
Definition: winobj.c:529
static INTSTATUS IntWinObjHandleObjectInMemory(WINOBJ_SWAPCTX *Context, QWORD Cr3, QWORD Gva, QWORD Gpa, void *Data, DWORD DataSize, DWORD Flags)
This callback is invoked for each object in an object directory entries list.
Definition: winobj.c:903
#define HEADER_SIZE_NAME_INFO32
64-bit _OBJECT_HEADER_NAME_INFO size.
Definition: winobj.c:224
#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
QWORD RootGva
The guest linear address of the possible root directory.
Definition: winobj.c:258
QWORD Directory
Pointer to the _OBJECT_DIRECTORY that owns this.
Definition: wddefs.h:1520
Hint structure used to search for possible object namespace root directory entries.
Definition: winobj.c:281
__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
The _OBJECT_HEADER32 structure used by 64-bit guests.
Definition: wddefs.h:373
MM Mm
Guest memory information, such as paging mode, system Cr3 value, etc.
Definition: guests.h:374
GUEST_STATE gGuest
The current guest state.
Definition: guests.c:50
QWORD ObpRootDirectoryObject
Guest virtual address of the ObpRootDirectoryObject.
Definition: winguest.h:838
QWORD Blink
Definition: wddefs.h:164
INTSTATUS IntVirtMemRead(QWORD Gva, DWORD Length, QWORD Cr3, void *Buffer, DWORD *RetLength)
Reads data from a guest virtual memory range.
Definition: introcore.c:627
BOOLEAN IntWinDrvObjIsValidDriverObject(QWORD DriverObjectAddress)
Checks if a guest memory area contains a valid _DRIVER_OBJECT structure.
Definition: windrvobj.c:28
#define CWSTRLEN(Wstring)
Definition: introdefs.h:104
BOOLEAN Waiting
True if the callback for this context has not been invoked yet, False if it has been invoked...
Definition: winobj.c:261
DWORD Object
Pointer to the object, may be NULL.
Definition: wddefs.h:1508
An _OBJECT_HEADER_NAME_INFO structure used by 64-bit guests.
Definition: wddefs.h:1518
INTSTATUS IntKernVirtMemRead(QWORD KernelGva, DWORD Length, void *Buffer, DWORD *RetLength)
Reads data from a guest kernel virtual memory range.
Definition: introcore.c:674
#define INT_STATUS_NO_MAPPING_STRUCTURES
Indicates that not all mapping structures of a virtual address are present.
Definition: introstatus.h:434
static INTSTATUS IntWinObjGetObjectNameInfo(QWORD ObjectGva, QWORD *BufferGva, WORD *Length, QWORD *ParentDirGva)
Returns the name information for kernel objects that have one.
Definition: winobj.c:417
Set if _OBJECT_HEADER_QUOTA_INFO is present.
Definition: winobj.c:213
static BOOLEAN IntWinObjIsRootSearchOver(void)
Definition: winobj.c:372
QWORD DriverDirectory
Guest virtual address of the Driver namespace directory.
Definition: winguest.h:839
WORD Length
The length, in bytes, of the string in Buffer, not including the NULL terminator, if any...
Definition: wddefs.h:139
QWORD ObjectGva
The guest linear address at which this object is locates.
Definition: winobj.c:272
A context structure used to pass information between the various callbacks that search for a Root Dir...
Definition: winobj.c:256
BYTE * KernelBuffer
A buffer containing the entire kernel image.
Definition: winguest.h:851
#define INT_STATUS_INVALID_PARAMETER_1
Definition: introstatus.h:62
static void IntWinObjReinitGlobalState(void)
Resets the global search state.
Definition: winobj.c:1711
An OBJECT_DIRECTORY_ENTRY64 structure used by 64-bit guests.
Definition: wddefs.h:1492
static int wstrncasecmp_len(const WCHAR *buf1, const WCHAR *buf2, size_t len_buf1, size_t len_buf2)
Definition: introcrt.h:221
An _OBJECT_TYPE structure used by 64-bit guests.
Definition: wddefs.h:1446
LIST_ENTRY Link
Entry in the gSwapHandles list.
Definition: winobj.c:269
#define CRITICAL(fmt,...)
Definition: glue.h:63
INTSTATUS IntWinObjFindRootDirectory(PROOT_HINT Hint, QWORD *PossibleRoot)
Returns a possible object namespace root directory.
Definition: winobj.c:1397
BOOLEAN DisableOnReturn
Set to True if after returning from this event handler, introcore must be unloaded.
Definition: guests.h:328
The _OBJECT_HEADER32 structure used by 32-bit guests.
Definition: wddefs.h:350
UNICODE_STRING32 Name
The object name.
Definition: wddefs.h:1531
DWORD Chain
Gva to the next _OBJECT_DIRECTORY_ENTRY, may be NULL.
Definition: wddefs.h:1507
#define PAGE_MASK
Definition: pgtable.h:35
The _POOL_HEADER structure used by 64-bit guests.
Definition: wddefs.h:432
INTSTATUS IntWinGuestFindDriversNamespace(void)
Runs the driver object namespace search.
Definition: winobj.c:1855
QWORD Buffer
The guest virtual address at which the wide-character string is located.
Definition: wddefs.h:146
#define INT_STATUS_INVALID_PARAMETER_2
Definition: introstatus.h:65
static INTSTATUS IntWinObjHandleRootDirTagInMemory(ROOT_SEARCH_CTX *Context, QWORD Cr3, QWORD Gva, QWORD Gpa, void *Data, DWORD DataSize, DWORD Flags)
This callback is invoked for every candidate root directory namespace object.
Definition: winobj.c:1173
struct _OBJECT_DIRECTORY_ENTRY64 OBJECT_DIRECTORY_ENTRY64
An OBJECT_DIRECTORY_ENTRY64 structure used by 64-bit guests.
size_t SIZE_T
Definition: intro_types.h:60
static BOOLEAN gStop
Set to True when the search must be aborted.
Definition: winobj.c:313
#define FALSE
Definition: intro_types.h:34
#define INT_STATUS_INSUFFICIENT_RESOURCES
Definition: introstatus.h:281
QWORD FoundAt
The address from which the candidate was extracted.
Definition: winobj.c:283