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 
578  UNREFERENCED_PARAMETER(DataSize);
579 
580  // Remove this swap handle from the list
581  RemoveEntryList(&pSwapCtx->Link);
583 
584  if (gGuest.Guest64)
585  {
586  next = ((OBJECT_DIRECTORY_ENTRY64 *)Data)->Chain;
587  drvObjGva = ((OBJECT_DIRECTORY_ENTRY64 *)Data)->Object;
588  sizeToRead = sizeof(OBJECT_DIRECTORY_ENTRY64);
589  }
590  else
591  {
592  next = ((OBJECT_DIRECTORY_ENTRY32 *)Data)->Chain;
593  drvObjGva = ((OBJECT_DIRECTORY_ENTRY32 *)Data)->Object;
594  sizeToRead = sizeof(OBJECT_DIRECTORY_ENTRY32);
595  }
596 
597  if (IntWinDrvObjIsValidDriverObject(drvObjGva))
598  {
599  PWIN_DRIVER_OBJECT pDrvObj = NULL;
600 
601  status = IntWinDrvObjCreateFromAddress(drvObjGva, TRUE, &pDrvObj);
602  if (!INT_SUCCESS(status))
603  {
604  ERROR("[ERROR] IntWinDrvObjCreateDriverObject failed for 0x%016llx: 0x%08x\n", drvObjGva, status);
605  }
606  else
607  {
608  gFoundDrivers++;
609  }
610  }
611 
612  // It should be 0 for the last entry in the linked list, but let's try to not inject a #PF on some random numbers
614  {
615  WINOBJ_SWAPCTX *pNextCtx;
616  void *swapHandle = NULL;
617 
618  pNextCtx = HpAllocWithTag(sizeof(*pNextCtx), IC_TAG_WINOBJ_SWAP);
619  if (NULL == pNextCtx)
620  {
622  }
623 
624  pNextCtx->Id = __LINE__;
625  pNextCtx->ObjectGva = next;
626  InsertTailList(&gSwapHandles, &pNextCtx->Link);
627 
628  status = IntSwapMemReadData(0,
629  next,
630  sizeToRead,
632  pNextCtx,
633  0,
635  NULL,
636  &swapHandle);
637  if (!INT_SUCCESS(status))
638  {
639  ERROR("[ERROR] IntSwapMemReadData failoed: 0x%08x\n", status);
640  RemoveEntryList(&pNextCtx->Link);
642  }
643  else if (NULL != swapHandle)
644  {
645  gPendingDrivers++;
646  pNextCtx->SwapHandle = swapHandle;
647  }
648  }
649 
650  // If this was an async call, check if it was the last and if we managed to find what we were searching for; if
651  // the call is synchronous the caller will do this check
652  if (0 != (SWAPMEM_FLAG_ASYNC_CALL & Flags))
653  {
654  gPendingDrivers--;
656  }
657 
658  return INT_STATUS_SUCCESS;
659 }
660 
661 
662 static INTSTATUS
664  _In_ WINOBJ_SWAPCTX *Context,
665  _In_ QWORD Cr3,
666  _In_ QWORD Gva,
667  _In_ QWORD Gpa,
668  _In_ void *Data,
669  _In_ DWORD DataSize,
670  _In_ DWORD Flags
671  )
706 {
707  PWINOBJ_SWAPCTX pCtx = Context;
708  QWORD objectGva = pCtx->ObjectGva;
709  INTSTATUS status = INT_STATUS_SUCCESS;
710 
714 
715  RemoveEntryList(&pCtx->Link);
717 
718  // Note: this is a guest read string, so we're not sure that it's NULL terminated. Use wstrncasecmp_len instead
719  // of wstrcasecmp.
720  if (0 == wstrncasecmp_len(Data, u"Driver", DataSize / 2, CWSTRLEN(u"Driver")))
721  {
722  LOG("[NAMESPACE] Found `Driver` directory @ 0x%016llx\n", objectGva);
723  gWinGuest->DriverDirectory = objectGva;
724  }
725  else if (0 == wstrncasecmp_len(Data, u"FileSystem", DataSize / 2, CWSTRLEN((u"FileSystem"))))
726  {
727  LOG("[NAMESPACE] Found `FileSystem` directory @ 0x%016llx\n", objectGva);
728  gWinGuest->FileSystemDirectory = objectGva;
729  }
730  else
731  {
733  }
734 
735  if (gGuest.Guest64)
736  {
737  QWORD entries[OBJECT_DIR_ENTRY_COUNT] = {0};
738 
739  status = IntKernVirtMemRead(objectGva, sizeof(entries), entries, NULL);
740  if (!INT_SUCCESS(status))
741  {
742  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x\n", status);
743  return status;
744  }
745 
746  for (DWORD i = 0; i < OBJECT_DIR_ENTRY_COUNT; i++)
747  {
748  if (IS_KERNEL_POINTER_WIN(gGuest.Guest64, entries[i]))
749  {
750  WINOBJ_SWAPCTX *pNextCtx;
751  void *swapHandle = NULL;
752 
753  pNextCtx = HpAllocWithTag(sizeof(*pNextCtx), IC_TAG_WINOBJ_SWAP);
754  if (NULL == pNextCtx)
755  {
756  continue;
757  }
758 
759  pNextCtx->Id = __LINE__;
760  pNextCtx->ObjectGva = entries[i];
761  InsertTailList(&gSwapHandles, &pNextCtx->Link);
762 
763  status = IntSwapMemReadData(0,
764  entries[i],
765  sizeof(OBJECT_DIRECTORY_ENTRY64),
767  pNextCtx,
768  0,
770  NULL,
771  &swapHandle);
772  if (!INT_SUCCESS(status))
773  {
774  ERROR("[ERROR] IntSwapMemReadData failed: 0x%08x\n", status);
775  RemoveEntryList(&pNextCtx->Link);
777  }
778  else if (NULL != swapHandle)
779  {
780  gPendingDrivers++;
781  pNextCtx->SwapHandle = swapHandle;
782  }
783  }
784  }
785  }
786  else
787  {
788  DWORD entries[OBJECT_DIR_ENTRY_COUNT] = {0};
789 
790  status = IntKernVirtMemRead(objectGva, sizeof(entries), entries, NULL);
791  if (!INT_SUCCESS(status))
792  {
793  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x\n", status);
794  return status;
795  }
796 
797  for (DWORD i = 0; i < OBJECT_DIR_ENTRY_COUNT; i++)
798  {
799  if (IS_KERNEL_POINTER_WIN(gGuest.Guest64, entries[i]))
800  {
801  WINOBJ_SWAPCTX *pNextCtx;
802  void *swapHandle = NULL;
803 
804  pNextCtx = HpAllocWithTag(sizeof(*pNextCtx), IC_TAG_WINOBJ_SWAP);
805  if (NULL == pNextCtx)
806  {
807  continue;
808  }
809 
810  pNextCtx->Id = __LINE__;
811  pNextCtx->ObjectGva = entries[i];
812  InsertTailList(&gSwapHandles, &pNextCtx->Link);
813 
814  status = IntSwapMemReadData(0,
815  entries[i],
816  sizeof(OBJECT_DIRECTORY_ENTRY32),
818  pNextCtx,
819  0,
821  NULL,
822  &swapHandle);
823  if (!INT_SUCCESS(status))
824  {
825  ERROR("[ERROR] IntSwapMemReadData failed: 0x%08x\n", status);
826  RemoveEntryList(&pNextCtx->Link);
828  }
829  else if (NULL != swapHandle)
830  {
831  gPendingDrivers++;
832  pNextCtx->SwapHandle = swapHandle;
833  }
834  }
835  }
836  }
837 
838  // If this was an async call, check if it was the last and if we managed to find what we were searching for; if
839  // the call is synchronous the caller will do this check
840  if (0 != (SWAPMEM_FLAG_ASYNC_CALL & Flags))
841  {
843  }
844 
845  return INT_STATUS_SUCCESS;
846 }
847 
848 
849 static INTSTATUS
851  _In_ WINOBJ_SWAPCTX *Context,
852  _In_ QWORD Cr3,
853  _In_ QWORD Gva,
854  _In_ QWORD Gpa,
855  _In_ void *Data,
856  _In_ DWORD DataSize,
857  _In_ DWORD Flags
858  )
882 {
883  QWORD objectGva;
884  INTSTATUS status = INT_STATUS_SUCCESS;
885  QWORD parentDirGva = 0;
886  QWORD nameBufferGva = 0;
887  WORD nameLength = 0;
888  PWINOBJ_SWAPCTX pDrvDirCtx;
889  PWINOBJ_SWAPCTX pCurrentCtx = Context;
890  void *swapHandle;
891 
896  UNREFERENCED_PARAMETER(DataSize);
897 
898  objectGva = pCurrentCtx->ObjectGva;
899 
900  RemoveEntryList(&pCurrentCtx->Link);
902 
903  status = IntWinObjGetObjectNameInfo(objectGva, &nameBufferGva, &nameLength, &parentDirGva);
904  if (!INT_SUCCESS(status))
905  {
906  ERROR("[ERROR] IntWinObjGetObjectNameInfo failed for 0x%016llx: 0x%08x\n", objectGva, status);
907  status = INT_STATUS_SUCCESS;
908  goto cleanup_and_exit;
909  }
910 
911  // Sanity check
912  if ((!IS_KERNEL_POINTER_WIN(gGuest.Guest64, nameBufferGva)) || nameLength >= 32)
913  {
914  status = INT_STATUS_SUCCESS;
915  goto cleanup_and_exit;
916  }
917 
918  pDrvDirCtx = HpAllocWithTag(sizeof(*pDrvDirCtx), IC_TAG_WINOBJ_SWAP);
919  if (NULL == pDrvDirCtx)
920  {
922  goto cleanup_and_exit;
923  }
924 
925  pDrvDirCtx->Id = __LINE__;
926  pDrvDirCtx->ObjectGva = objectGva;
927  InsertTailList(&gSwapHandles, &pDrvDirCtx->Link);
928 
929  status = IntSwapMemReadData(0,
930  nameBufferGva,
931  nameLength,
933  pDrvDirCtx,
934  0,
936  NULL,
937  &swapHandle);
938  if (!INT_SUCCESS(status))
939  {
940  ERROR("[ERROR] IntSwapMemReadData failed: 0x%08x\n", status);
941  RemoveEntryList(&pDrvDirCtx->Link);
943  }
944  else if (NULL != swapHandle)
945  {
946  pDrvDirCtx->SwapHandle = swapHandle;
947  }
948 
949 cleanup_and_exit:
950  // If this was an async call, check if it was the last and if we managed to find what we were searching for; if
951  // the call is synchronous the caller will do this check
952  if (0 != (SWAPMEM_FLAG_ASYNC_CALL & Flags))
953  {
955  }
956 
957  return status;
958 }
959 
960 
961 static INTSTATUS
963  _In_ WINOBJ_SWAPCTX *Context,
964  _In_ QWORD Cr3,
965  _In_ QWORD Gva,
966  _In_ QWORD Gpa,
967  _In_ void *Data,
968  _In_ DWORD DataSize,
969  _In_ DWORD Flags
970  )
994 
995 {
996  QWORD next;
997  QWORD objectGva;
998  DWORD sizeToRead;
999  INTSTATUS status = INT_STATUS_SUCCESS;
1000  PWINOBJ_SWAPCTX pDrvDirCtx = NULL;
1001  PWINOBJ_SWAPCTX pCurrentCtx = Context;
1002  void *swapHandle;
1003 
1007  UNREFERENCED_PARAMETER(DataSize);
1008  UNREFERENCED_PARAMETER(Flags);
1009 
1010  RemoveEntryList(&pCurrentCtx->Link);
1012 
1013  if (gGuest.Guest64)
1014  {
1016 
1017  next = pEntry->Chain;
1018  objectGva = pEntry->Object;
1019  sizeToRead = sizeof(OBJECT_DIRECTORY_ENTRY64);
1020  }
1021  else
1022  {
1024 
1025  next = pEntry->Chain;
1026  objectGva = pEntry->Object;
1027  sizeToRead = sizeof(OBJECT_DIRECTORY_ENTRY32);
1028  }
1029 
1030  // Don't bother if this is not a kernel pointer
1031  if (!IS_KERNEL_POINTER_WIN(gGuest.Guest64, objectGva))
1032  {
1033  goto next_entry;
1034  }
1035 
1036  pDrvDirCtx = HpAllocWithTag(sizeof(*pDrvDirCtx), IC_TAG_WINOBJ_SWAP);
1037  if (NULL == pDrvDirCtx)
1038  {
1040  goto next_entry;
1041  }
1042 
1043  pDrvDirCtx->Id = __LINE__;
1044  pDrvDirCtx->ObjectGva = objectGva;
1045  InsertTailList(&gSwapHandles, &pDrvDirCtx->Link);
1046 
1047  status = IntSwapMemReadData(0,
1048  objectGva,
1049  1, // The size doesn't really matter, we just want to make the page present
1051  pDrvDirCtx,
1052  0,
1054  NULL,
1055  &swapHandle);
1056  if (!INT_SUCCESS(status))
1057  {
1058  ERROR("[ERROR] IntSwapMemReadData failed: 0x%08x\n", status);
1059  RemoveEntryList(&pDrvDirCtx->Link);
1061  }
1062  else if (NULL != swapHandle)
1063  {
1064  pDrvDirCtx->SwapHandle = swapHandle;
1065  }
1066 
1067 next_entry:
1068 
1070  {
1071  WINOBJ_SWAPCTX *pDirEntryCtx;
1072  swapHandle = NULL;
1073 
1074  pDirEntryCtx = HpAllocWithTag(sizeof(*pDirEntryCtx), IC_TAG_WINOBJ_SWAP);
1075  if (NULL == pDirEntryCtx)
1076  {
1078  }
1079 
1080  pDirEntryCtx->Id = __LINE__;
1081  pDirEntryCtx->ObjectGva = next;
1082  InsertTailList(&gSwapHandles, &pDirEntryCtx->Link);
1083 
1084  status = IntSwapMemReadData(0,
1085  next,
1086  sizeToRead,
1088  pDirEntryCtx,
1089  0,
1091  NULL,
1092  &swapHandle);
1093  if (!INT_SUCCESS(status))
1094  {
1095  ERROR("[ERROR] IntSwapMemReadData failed: 0x%08x\n", status);
1096  RemoveEntryList(&pDirEntryCtx->Link);
1097  HpFreeAndNullWithTag(&pDirEntryCtx, IC_TAG_WINOBJ_SWAP);
1098 
1099  // We failed to inject the #PF, there is nothing else we can do other than consider this bucket "done"
1102  }
1103  else if (NULL != swapHandle)
1104  {
1105  pDirEntryCtx->SwapHandle = swapHandle;
1106  }
1107  }
1108  else
1109  {
1110  // This is the last entry in this bucket, mark it as done
1113  }
1114 
1115  return status;
1116 }
1117 
1118 
1119 static INTSTATUS
1121  _In_ ROOT_SEARCH_CTX *Context,
1122  _In_ QWORD Cr3,
1123  _In_ QWORD Gva,
1124  _In_ QWORD Gpa,
1125  _In_ void *Data,
1126  _In_ DWORD DataSize,
1127  _In_ DWORD Flags
1128  )
1150 {
1151  DWORD tag = *(DWORD *)Data;
1152  PROOT_SEARCH_CTX pCtx = Context;
1153  QWORD objGva = pCtx->RootGva;
1154  INTSTATUS status;
1155  QWORD parentDirGva = 0;
1156  QWORD nameBufferGva = 0;
1157  WORD nameLength = 0;
1158 
1161  UNREFERENCED_PARAMETER(DataSize);
1162  UNREFERENCED_PARAMETER(Flags);
1163 
1164  pCtx->SwapHandle = NULL;
1165  pCtx->Waiting = FALSE;
1166 
1167  if ((gGuest.OSVersion < 9200 && WIN_POOL_TAG_DIRECTORY_7 != tag) ||
1168  (gGuest.OSVersion >= 9200 && WIN_POOL_TAG_DIRECTORY != tag))
1169  {
1170  TRACE("[NAMESPACE] Skipping tag 0x%08x @ 0x%016llx for object 0x%016llx!\n",
1171  tag, Gva, objGva);
1172  status = INT_STATUS_NOT_NEEDED_HINT;
1173  goto _check_state_and_exit;
1174  }
1175 
1176  LOG("[NAMESPACE] Found tag 0x%08x @ 0x%016llx for object 0x%016llx!\n", tag, Gva, objGva);
1177 
1178  // We know this page is in memory because we get here only if it already was in memory, or we injected a #PF
1179  // to bring it in memory
1180  status = IntWinObjGetObjectNameInfo(objGva, &nameBufferGva, &nameLength, &parentDirGva);
1181  if (!INT_SUCCESS(status))
1182  {
1183  ERROR("[ERROR] IntWinObjGetObjectNameInfo failed for 0x%016llx: 0x%08x\n", objGva, status);
1184  goto _check_state_and_exit;
1185  }
1186 
1187  if (0 != parentDirGva)
1188  {
1189  TRACE("[NAMESPACE] Skipping object 0x%016llx because it's parent directory is not NULL\n", objGva);
1190  status = INT_STATUS_NOT_NEEDED_HINT;
1191  goto _check_state_and_exit;
1192  }
1193 
1194  if (2 != nameLength || !IS_KERNEL_POINTER_WIN(gGuest.Guest64, nameBufferGva))
1195  {
1196  status = INT_STATUS_NOT_NEEDED_HINT;
1197  goto _check_state_and_exit;
1198  }
1199 
1200  LOG("[NAMESPACE] Found Root Directory (`\\`) @ 0x%016llx!\n", objGva);
1202 
1203  // Root found, cancel all other transactions
1205 
1206  if (gGuest.Guest64)
1207  {
1208  QWORD entries[OBJECT_DIR_ENTRY_COUNT] = {0};
1209 
1210  status = IntKernVirtMemRead(objGva, sizeof(entries), entries, NULL);
1211  if (!INT_SUCCESS(status))
1212  {
1213  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x\n", status);
1214  goto _check_state_and_exit;
1215  }
1216 
1217  for (DWORD i = 0; i < OBJECT_DIR_ENTRY_COUNT; i++)
1218  {
1219  if (IS_KERNEL_POINTER_WIN(gGuest.Guest64, entries[i]))
1220  {
1221  WINOBJ_SWAPCTX *pDirEntryCtx;
1222  void *swapHandle = NULL;
1223 
1224  pDirEntryCtx = HpAllocWithTag(sizeof(*pDirEntryCtx), IC_TAG_WINOBJ_SWAP);
1225  if (NULL == pDirEntryCtx)
1226  {
1227  continue;
1228  }
1229 
1230  pDirEntryCtx->Id = __LINE__;
1231  pDirEntryCtx->ObjectGva = entries[i];
1232  InsertTailList(&gSwapHandles, &pDirEntryCtx->Link);
1233 
1234  status = IntSwapMemReadData(0,
1235  entries[i],
1236  sizeof(OBJECT_DIRECTORY_ENTRY64),
1238  pDirEntryCtx,
1239  0,
1241  NULL,
1242  &swapHandle);
1243  if (!INT_SUCCESS(status))
1244  {
1245  ERROR("[ERROR] IntSwapMemReadData failed: 0x%08x\n", status);
1246  RemoveEntryList(&pDirEntryCtx->Link);
1247  HpFreeAndNullWithTag(&pDirEntryCtx, IC_TAG_WINOBJ_SWAP);
1248 
1251  }
1252  else if (NULL != swapHandle)
1253  {
1254  pDirEntryCtx->SwapHandle = swapHandle;
1255  }
1256  }
1257  else
1258  {
1259  // This entry is empty, so there's nothing to check here
1262  }
1263  }
1264  }
1265  else
1266  {
1267  DWORD entries[OBJECT_DIR_ENTRY_COUNT] = {0};
1268 
1269  status = IntKernVirtMemRead(objGva, sizeof(entries), entries, NULL);
1270  if (!INT_SUCCESS(status))
1271  {
1272  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x\n", status);
1273  goto _check_state_and_exit;
1274  }
1275 
1276  for (DWORD i = 0; i < OBJECT_DIR_ENTRY_COUNT; i++)
1277  {
1278  if (IS_KERNEL_POINTER_WIN(gGuest.Guest64, entries[i]))
1279  {
1280  WINOBJ_SWAPCTX *pDirEntryCtx;
1281  void *swapHandle = NULL;
1282 
1283  pDirEntryCtx = HpAllocWithTag(sizeof(*pDirEntryCtx), IC_TAG_WINOBJ_SWAP);
1284  if (NULL == pDirEntryCtx)
1285  {
1287  continue;
1288  }
1289 
1290  pDirEntryCtx->Id = __LINE__;
1291  pDirEntryCtx->ObjectGva = entries[i];
1292  InsertTailList(&gSwapHandles, &pDirEntryCtx->Link);
1293 
1294  status = IntSwapMemReadData(0,
1295  entries[i],
1296  sizeof(OBJECT_DIRECTORY_ENTRY32),
1298  pDirEntryCtx,
1299  0,
1301  NULL,
1302  &swapHandle);
1303  if (!INT_SUCCESS(status))
1304  {
1305  ERROR("[ERROR] IntSwapMemReadData failed: 0x%08x\n", status);
1306  RemoveEntryList(&pDirEntryCtx->Link);
1307  HpFreeAndNullWithTag(&pDirEntryCtx, IC_TAG_WINOBJ_SWAP);
1308 
1311  }
1312  else if (NULL != swapHandle)
1313  {
1314  pDirEntryCtx->SwapHandle = swapHandle;
1315  }
1316  }
1317  else
1318  {
1319  // This entry is empty, so there's nothing to check here
1322  }
1323  }
1324  }
1325 
1326 _check_state_and_exit:
1328  {
1329  ERROR("[ERROR] Could not find ObpRootDirectoryObject!\n");
1330 
1333  }
1334  else
1335  {
1337  }
1338 
1339  return status;
1340 }
1341 
1342 
1343 INTSTATUS
1345  _In_ PROOT_HINT Hint,
1346  _Out_ QWORD *PossibleRoot
1347  )
1365 {
1366  INTSTATUS status = INT_STATUS_SUCCESS;
1367  WORD foundTypes = 0;
1368  const WORD expectedTypes = 1;
1369  QWORD root = 0;
1370  DWORD delta;
1371 
1372  if (NULL == Hint)
1373  {
1375  }
1376 
1377  if (NULL == PossibleRoot)
1378  {
1380  }
1381 
1383  {
1384  *PossibleRoot = gWinGuest->ObpRootDirectoryObject;
1386  }
1387 
1389 
1390  // ObpRootDirectoryObject is before, after or between two type objects from non-paged pool.
1391  // The actual layout is not the same on every OS version.
1392 
1393  for (DWORD i = 0; i < ROOT_HINT_PTR_COUNT; i++)
1394  {
1395  // We allow NULL pointers around the root
1396  if (0 == Hint->Pointers[i])
1397  {
1398  continue;
1399  }
1400 
1401  status = IntWinObjIsTypeObject(Hint->Pointers[i]);
1402  if (!INT_SUCCESS(status))
1403  {
1404  if ((0 == root) &&
1405  // the root and it's headers must be in the same page
1406  ((Hint->Pointers[i] & PAGE_MASK) == ((Hint->Pointers[i] - delta) & PAGE_MASK)))
1407  {
1408  root = Hint->Pointers[i];
1409  }
1410  else
1411  {
1412  *PossibleRoot = 0;
1414  }
1415  }
1416  else
1417  {
1418  foundTypes++;
1419  }
1420  }
1421 
1422  if ((foundTypes < expectedTypes) || !IS_KERNEL_POINTER_WIN(gGuest.Guest64, root))
1423  {
1425  }
1426 
1427  status = INT_STATUS_SUCCESS;
1428 
1429  for (size_t i = 0; i < ROOT_HINT_PTR_COUNT; i++)
1430  {
1431  if (root == Hint->Pointers[i])
1432  {
1433  LOG("[NAMESPACE] Found possible root @ 0x%016llx = 0x%016llx\n",
1434  Hint->FoundAt + i * gGuest.WordSize, Hint->Pointers[i]);
1435  }
1436  else
1437  {
1438  LOG("[NAMESPACE] Found type @ 0x%016llx = 0x%016llx\n",
1439  Hint->FoundAt + i * gGuest.WordSize, Hint->Pointers[i]);
1440  }
1441  }
1442 
1443  *PossibleRoot = root;
1444 
1445  return status;
1446 }
1447 
1448 
1449 INTSTATUS
1451  _In_ QWORD Gva
1452  )
1477 {
1478  POOL_HEADER poolHeader = {0};
1479  INTSTATUS status;
1480  DWORD poolTag;
1481  DWORD poolType;
1482 
1484  {
1485  return INT_STATUS_NOT_FOUND;
1486  }
1487 
1488  // It must be readable as it is in non-paged pool
1489  if (gGuest.Guest64)
1490  {
1491  OBJECT_TYPE64 typeObj = {0};
1492 
1493  status = IntKernVirtMemRead(Gva, sizeof(typeObj), &typeObj, NULL);
1494  if (!INT_SUCCESS(status))
1495  {
1496  return INT_STATUS_NOT_FOUND;
1497  }
1498 
1501  {
1502  return INT_STATUS_NOT_FOUND;
1503  }
1504 
1505  if (typeObj.Name.Length > typeObj.Name.MaximumLength)
1506  {
1507  return INT_STATUS_NOT_FOUND;
1508  }
1509 
1510  if ((!IS_KERNEL_POINTER_WIN(gGuest.Guest64, typeObj.TypeList.Blink)) ||
1512  {
1513  return INT_STATUS_NOT_FOUND;
1514  }
1515  }
1516  else
1517  {
1518  OBJECT_TYPE32 typeObj = {0};
1519 
1520  status = IntKernVirtMemRead(Gva, sizeof(typeObj), &typeObj, NULL);
1521  if (!INT_SUCCESS(status))
1522  {
1523  return INT_STATUS_NOT_FOUND;
1524  }
1525 
1528  {
1529  return INT_STATUS_NOT_FOUND;
1530  }
1531 
1532  if (typeObj.Name.Length > typeObj.Name.MaximumLength)
1533  {
1534  return INT_STATUS_NOT_FOUND;
1535  }
1536 
1537  if ((!IS_KERNEL_POINTER_WIN(gGuest.Guest64, typeObj.TypeList.Blink)) ||
1539  {
1540  return INT_STATUS_NOT_FOUND;
1541  }
1542  }
1543 
1544  status = IntWinObjGetPoolHeaderForObject(Gva, &poolHeader);
1545  if (!INT_SUCCESS(status))
1546  {
1547  TRACE("[INFO] IntWinObjGetPoolHeaderForObject failed for 0x%016llx: 0x%08x\n", Gva, status);
1548  return INT_STATUS_NOT_FOUND;
1549  }
1550 
1551  poolTag = gGuest.Guest64 ? poolHeader.Header64.PoolTag : poolHeader.Header32.PoolTag;
1552 
1553  if ((gGuest.OSVersion < 9200 && WIN_POOL_TAG_OBJECT_7 != poolTag) ||
1554  (gGuest.OSVersion >= 9200 && WIN_POOL_TAG_OBJECT != poolTag))
1555  {
1556  return INT_STATUS_NOT_FOUND;
1557  }
1558 
1559  poolType = gGuest.Guest64 ? poolHeader.Header64.PoolType : poolHeader.Header32.PoolType;
1560 
1561  // An _OBJECT_TYPE is always in the paged pool and it never uses a "DontUseThisType" pool allocation
1562  if (0 != (BIT(NonPagedPool) & poolType) &&
1563  DontUseThisType != poolType &&
1564  DontUseThisTypeSession != poolType)
1565  {
1566  return INT_STATUS_NOT_FOUND;
1567  }
1568 
1569  return INT_STATUS_SUCCESS;
1570 }
1571 
1572 
1573 INTSTATUS
1575  _In_ QWORD ObjectGva,
1576  _Out_ POOL_HEADER *PoolHeader
1577  )
1594 {
1595  INTSTATUS status;
1596  DWORD delta;
1597 
1598  if (!IS_KERNEL_POINTER_WIN(gGuest.Guest64, ObjectGva))
1599  {
1601  }
1602 
1603  if (NULL == PoolHeader)
1604  {
1606  }
1607 
1608  if (gGuest.Guest64)
1609  {
1612  }
1613  else
1614  {
1617  }
1618 
1619  if (!IS_KERNEL_POINTER_WIN(gGuest.Guest64, ObjectGva - delta))
1620  {
1621  return INT_STATUS_NOT_FOUND;
1622  }
1623 
1624  status = IntKernVirtMemRead(ObjectGva - delta,
1625  gGuest.Guest64 ? sizeof(POOL_HEADER64) : sizeof(POOL_HEADER32),
1626  PoolHeader, NULL);
1627  if (!INT_SUCCESS(status))
1628  {
1629  TRACE("[INFO] IntKernVirtMemRead failed for 0x%016llx: 0x%08x\n", ObjectGva - delta, status);
1630  }
1631 
1632  return status;
1633 }
1634 
1635 
1636 static void
1638  void
1639  )
1643 {
1644  memset(gPossibleRootGvas, 0, sizeof(gPossibleRootGvas));
1645  gRootCount = 0;
1647  gStop = FALSE;
1648  InitializeListHead(&gSwapHandles);
1649 }
1650 
1651 
1652 static INTSTATUS
1654  void
1655  )
1668 {
1669  //
1670  // This could be merged into IntWinGuestFindDriversNamespace and handle everything in one place, but that will make
1671  // everything harder to read and more error-prone
1672  //
1673 
1674  IMAGE_SECTION_HEADER sec = {0};
1675  INTSTATUS status;
1676  DWORD pageCount;
1677 
1679 
1680  status = IntPeGetSectionHeadersByName(gGuest.KernelVa, NULL, ".data", 1, gGuest.Mm.SystemCr3, &sec, NULL);
1681  if (!INT_SUCCESS(status))
1682  {
1683  ERROR("[ERROR] IntPeGetSectionHeadersByName failed for `.data`: 0x%08x\n", status);
1684  return status;
1685  }
1686 
1687  pageCount = ROUND_UP(sec.Misc.VirtualSize, PAGE_SIZE) / PAGE_SIZE;
1688  for (size_t i = 0; i < pageCount; i++)
1689  {
1690  QWORD targetGva = gGuest.KernelVa + sec.VirtualAddress + i * PAGE_SIZE;
1691  PBYTE page = NULL;
1692  DWORD ptrCount;
1693 
1694  status = IntVirtMemMap(targetGva, PAGE_SIZE, 0, 0, &page);
1695  if (!INT_SUCCESS(status))
1696  {
1697  ERROR("[ERROR] IntVirtMemMap failed for 0x%016llx: 0x%08x\n", targetGva, status);
1698  continue;
1699  }
1700 
1701  if (i == pageCount - 1)
1702  {
1703  ptrCount = (sec.Misc.VirtualSize & PAGE_OFFSET) / gGuest.WordSize;
1704  }
1705  else
1706  {
1707  ptrCount = PAGE_SIZE / gGuest.WordSize;
1708  }
1709 
1710  for (DWORD j = 0; j < ptrCount; j++)
1711  {
1712  ROOT_HINT hint = {0};
1713  QWORD root = 0;
1714 
1715  hint.FoundAt = targetGva;
1716 
1717  // Make sure we don't try to read pointers from two pages when we mapped only one
1718  if (j + ROOT_HINT_PTR_COUNT < ptrCount)
1719  {
1720  for (DWORD k = 0; k < ROOT_HINT_PTR_COUNT; k++)
1721  {
1722  hint.Pointers[k] = gGuest.Guest64 ? ((QWORD *)page)[j + k] : ((DWORD *)page)[j + k];
1723  }
1724  }
1725  // There's no point in doing this for the last page
1726  else if (i < ptrCount - 1)
1727  {
1728  if (gGuest.Guest64)
1729  {
1730  status = IntKernVirtMemRead(hint.FoundAt, sizeof(hint.Pointers), hint.Pointers, NULL);
1731  }
1732  else
1733  {
1734  DWORD temp[ROOT_HINT_PTR_COUNT] = {0};
1735 
1736  status = IntKernVirtMemRead(hint.FoundAt, sizeof(temp), temp, NULL);
1737  for (DWORD k = 0; k < ROOT_HINT_PTR_COUNT; k++)
1738  {
1739  hint.Pointers[k] = temp[k];
1740  }
1741  }
1742  if (!INT_SUCCESS(status))
1743  {
1744  ERROR("[ERROR] IntKernVirtMemRead failed for 0x%016llx: 0x%08x\n", hint.FoundAt, status);
1745  goto _continue;
1746  }
1747  }
1748 
1749  status = IntWinObjFindRootDirectory(&hint, &root);
1750  if (!INT_SUCCESS(status) && INT_STATUS_NOT_FOUND != status)
1751  {
1752  ERROR("[ERROR] IntWinObjFindRootDirectory for 0x%016llx: 0x%08x\n", hint.FoundAt, status);
1753  }
1754  else if (INT_STATUS_SUCCESS == status)
1755  {
1756  if (gRootCount < ARRAYSIZE(gPossibleRootGvas))
1757  {
1758  gPossibleRootGvas[gRootCount].RootGva = root;
1759  gPossibleRootGvas[gRootCount].Waiting = TRUE;
1760  gPossibleRootGvas[gRootCount].SwapHandle = NULL;
1761  gRootCount++;
1762  }
1763  else
1764  {
1765  break;
1766  }
1767  }
1768 
1769 _continue:
1770  targetGva += gGuest.WordSize;
1771  }
1772 
1773  IntVirtMemUnmap(&page);
1774  }
1775 
1776  return INT_STATUS_SUCCESS;
1777 }
1778 
1779 
1780 INTSTATUS
1782  void
1783  )
1855 {
1856  IMAGE_SECTION_HEADER sec = {0};
1857  INTSTATUS status;
1858  DWORD ptrCount;
1859  BOOLEAN useBuffer = TRUE;
1860 
1862  {
1863  WARNING("[WARNING] A find for drivers namespace is already in progress... "
1864  "root = %u, pending = %u, found = %u\n",
1865  gRootCount,
1867  gFoundDrivers);
1868 
1870  }
1871 
1873 
1874 _retry:
1875  if (!useBuffer)
1876  {
1877  TRACE("[NAMESPACE] Kernel buffer not present, will fetch objects directly from memory!\n");
1878 
1880  if (!INT_SUCCESS(status))
1881  {
1882  ERROR("[ERROR] IntWinGuestFindDriversNamespaceNoBuffer failed: 0x%08x\n", status);
1883  }
1884 
1885  // failure or not, we need to go through this check
1886  goto _check_roots;
1887  }
1888 
1890  gGuest.Mm.SystemCr3, &sec, NULL);
1891  if (!INT_SUCCESS(status))
1892  {
1893  ERROR("[ERROR] IntPeGetSectionHeadersByName failed for `.data`: 0x%08x\n", status);
1894  goto _check_roots;
1895  }
1896 
1897  ptrCount = sec.Misc.VirtualSize / gGuest.WordSize - ROOT_HINT_PTR_COUNT;
1898 
1899  for (DWORD i = 0; i < ptrCount; i++)
1900  {
1901  DWORD bufferOffset = sec.VirtualAddress + i * gGuest.WordSize;
1902  void *target;
1903  ROOT_HINT hint;
1904  QWORD root = 0;
1905 
1906  if (bufferOffset > gWinGuest->KernelBufferSize)
1907  {
1908  ERROR("[CRITICAL ERROR] RVA 0x%08x is outside the kernel buffer (size = 0x%08x)\n",
1909  bufferOffset, gWinGuest->KernelBufferSize);
1910  break;
1911  }
1912 
1913  target = gWinGuest->KernelBuffer + bufferOffset;
1914  hint.FoundAt = gGuest.KernelVa + bufferOffset;
1915 
1916  for (DWORD k = 0; k < ROOT_HINT_PTR_COUNT; k++)
1917  {
1918  hint.Pointers[k] = gGuest.Guest64 ? ((QWORD *)target)[k] : ((DWORD *)target)[k];
1919  }
1920 
1921  status = IntWinObjFindRootDirectory(&hint, &root);
1922  if (!INT_SUCCESS(status) && INT_STATUS_NOT_FOUND != status)
1923  {
1924  ERROR("[ERROR] IntWinObjFindRootDirectory for 0x%016llx: 0x%08x\n", hint.FoundAt, status);
1925  }
1926  else if (INT_STATUS_SUCCESS == status)
1927  {
1928  if (gRootCount < ARRAYSIZE(gPossibleRootGvas))
1929  {
1930  gPossibleRootGvas[gRootCount].RootGva = root;
1931  gPossibleRootGvas[gRootCount].Waiting = TRUE;
1932  gPossibleRootGvas[gRootCount].SwapHandle = NULL;
1933  gRootCount++;
1934  }
1935  else
1936  {
1937  break;
1938  }
1939  }
1940  }
1941 
1942  if (0 == gRootCount && useBuffer)
1943  {
1944  WARNING("[WINOBJ] Found 0 possible root pointers inside the kernel buffer, will retry without it!\n");
1945  useBuffer = FALSE;
1946  goto _retry;
1947  }
1948 
1949 _check_roots:
1950  LOG("[NAMESPACE] Will check %d possible root pointers...\n", gRootCount);
1951 
1952  for (DWORD i = 0; i < gRootCount; i++)
1953  {
1955  QWORD root = gPossibleRootGvas[i].RootGva;
1956 
1957  TRACE("[NAMESPACE] Trying 0x%016llx (%d) with 0x%016llx...\n",
1958  root, i, root - poolHeaderOffset + OFFSET_OF(POOL_HEADER32, PoolTag));
1959 
1960  // It is ok to use the field offset of PoolTag inside POOL_HEADER32 because it is the same offset as
1961  // POOL_HEADER64.
1962  status = IntSwapMemReadData(0,
1963  root - poolHeaderOffset + OFFSET_OF(POOL_HEADER32, PoolTag),
1964  sizeof(DWORD),
1966  &gPossibleRootGvas[i],
1967  0,
1969  NULL,
1970  &gPossibleRootGvas[i].SwapHandle);
1971  if (!INT_SUCCESS(status))
1972  {
1973  ERROR("[ERROR] IntSwapMemReadData failed for 0x%016llx: 0x%08x\n", root - poolHeaderOffset, status);
1974  gPossibleRootGvas[i].Waiting = FALSE;
1975  }
1976 
1977  // If the root was already found in a sync manner, bail out now (all transactions were canceled
1978  // in IntWinObjHandleRootDirTagInMemory)
1980  {
1981  return INT_STATUS_SUCCESS;
1982  }
1983  }
1984 
1985  status = INT_STATUS_SUCCESS;
1986 
1987  if (0 == gRootCount || (IntWinObjIsRootSearchOver() && 0 == gWinGuest->ObpRootDirectoryObject))
1988  {
1989  ERROR("[ERROR] Could not find ObpRootDirectoryObject!\n");
1990 
1993 
1994  status = INT_STATUS_NOT_FOUND;
1995  }
1996  else
1997  {
1999  }
2000 
2001  return status;
2002 }
2003 
2004 
2005 void
2007  void
2008  )
2015 {
2016  PLIST_ENTRY entry = gSwapHandles.Flink;
2017  DWORD remCount = 0;
2018 
2019  // First, the root transactions (if any)
2021  TRACE("[WINOBJ] Root transactions removed: %d\n", gRootCount);
2022 
2023  if (NULL == entry)
2024  {
2025  TRACE("[WINOBJ] No swap handles are present, nothing to clean\n");
2026  return;
2027  }
2028 
2029  // Now, everything else
2030  while (entry != &gSwapHandles)
2031  {
2032  PWINOBJ_SWAPCTX pCtx = CONTAINING_RECORD(entry, WINOBJ_SWAPCTX, Link);
2033  INTSTATUS status;
2034 
2035  entry = entry->Flink;
2036 
2037  TRACE("[WINOBJ] Removing swap handle %p for %llx ID = %u\n",
2038  pCtx->SwapHandle, pCtx->ObjectGva, pCtx->Id);
2039 
2040  RemoveEntryList(&pCtx->Link);
2041 
2042  status = IntSwapMemRemoveTransaction(pCtx->SwapHandle);
2043  if (!INT_SUCCESS(status))
2044  {
2045  ERROR("[ERROR] IntSwapMemRemoveTransaction failed: 0x%08x\n", status);
2046  }
2047 
2049  remCount++;
2050  }
2051 
2052  TRACE("[WINOBJ] Queued transactions removed: %d\n", remCount);
2053  CRITICAL("[WINOBJ] Cleanup done with %u pending drivers. Found = %u\n", gPendingDrivers, gFoundDrivers);
2054 }
DWORD Id
The ID of this object (used for debugging).
Definition: winobj.c:273
#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:88
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:663
QWORD FileSystemDirectory
Guest virtual address of the FileSystem namespace directory.
Definition: winguest.h:826
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:35
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:1328
QWORD SystemCr3
The Cr3 used to map the kernel.
Definition: guests.h:207
#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:51
uint16_t WORD
Definition: intro_types.h:48
QWORD Chain
Gva to the next _OBJECT_DIRECTORY_ENTRY, may be NULL.
Definition: wddefs.h:1349
QWORD Object
Pointer to the object, may be NULL.
Definition: wddefs.h:1350
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:2278
void IntWinObjCleanup(void)
Cleans up any resources allocated by the object search.
Definition: winobj.c:2006
#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:962
#define INT_STATUS_NOT_NEEDED_HINT
Definition: introstatus.h:317
#define ERROR(fmt,...)
Definition: glue.h:62
UNICODE_STRING64 Name
Definition: wddefs.h:1304
static INTSTATUS IntWinGuestFindDriversNamespaceNoBuffer(void)
Runs the driver object namespace search ignoring the gGuest.KernelBuffer and reading the data directl...
Definition: winobj.c:1653
INTSTATUS IntPeGetSectionHeadersByName(QWORD ImageBase, BYTE *ImageBaseBuffer, PCHAR Name, DWORD NumberOfSectionHeadersAllocated, QWORD Cr3, IMAGE_SECTION_HEADER *SectionHeaders, DWORD *NumberOfSectionHeadersFilled)
Return all the section headers matching the indicated Name.
Definition: winpe.c:917
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:277
#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:1376
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:1327
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:1360
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:1303
#define LOG(fmt,...)
Definition: glue.h:61
QWORD DefaultObject
Definition: wddefs.h:1305
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:107
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:1324
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:1383
#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:286
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...
union _IMAGE_SECTION_HEADER::@209 Misc
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:1385
#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:279
DWORD PoolType
Definition: wddefs.h:412
BYTE WordSize
Guest word size. Will be 4 for 32-bit guests and 8 for 64-bit guests.
Definition: guests.h:363
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:53
#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:1450
uint32_t DWORD
Definition: intro_types.h:49
UINT32 VirtualSize
Definition: winpe.h:83
DWORD KernelBufferSize
The size of the KernelBuffer.
Definition: winguest.h:838
INTSTATUS IntWinObjGetPoolHeaderForObject(QWORD ObjectGva, POOL_HEADER *PoolHeader)
Reads the _POOL_HEADER structure for a given kernel object.
Definition: winobj.c:1574
LIST_ENTRY32 TypeList
Definition: wddefs.h:1326
#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:850
#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:1375
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:370
GUEST_STATE gGuest
The current guest state.
Definition: guests.c:48
QWORD ObpRootDirectoryObject
Guest virtual address of the ObpRootDirectoryObject.
Definition: winguest.h:824
QWORD Blink
Definition: wddefs.h:164
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:1363
An _OBJECT_HEADER_NAME_INFO structure used by 64-bit guests.
Definition: wddefs.h:1373
INTSTATUS IntKernVirtMemRead(QWORD KernelGva, DWORD Length, void *Buffer, DWORD *RetLength)
Reads data from a guest kernel virtual memory range.
Definition: introcore.c:674
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:825
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:837
#define INT_STATUS_INVALID_PARAMETER_1
Definition: introstatus.h:62
static void IntWinObjReinitGlobalState(void)
Resets the global search state.
Definition: winobj.c:1637
An OBJECT_DIRECTORY_ENTRY64 structure used by 64-bit guests.
Definition: wddefs.h:1347
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:1301
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:1344
BOOLEAN DisableOnReturn
Set to True if after returning from this event handler, introcore must be unloaded.
Definition: guests.h:324
The _OBJECT_HEADER32 structure used by 32-bit guests.
Definition: wddefs.h:350
UNICODE_STRING32 Name
The object name.
Definition: wddefs.h:1386
DWORD Chain
Gva to the next _OBJECT_DIRECTORY_ENTRY, may be NULL.
Definition: wddefs.h:1362
#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:1781
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:1120
struct _OBJECT_DIRECTORY_ENTRY64 OBJECT_DIRECTORY_ENTRY64
An OBJECT_DIRECTORY_ENTRY64 structure used by 64-bit guests.
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