Bitdefender Hypervisor Memory Introspection
winumcache.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 Bitdefender
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 #include "winumcache.h"
6 #include "crc32.h"
7 #include "guests.h"
8 #include "swapmem.h"
9 #include "winpe.h"
10 #include "winummodule.h"
11 
12 
31 
32 
37 {
47 };
48 
50 
52 
53 
56  _Inout_ RBNODE *Node
57  )
65 {
67 }
68 
69 
72  _In_ RBNODE *Left,
73  _In_ RBNODE *Right
74  )
85 {
86  WINUM_CACHE_EXPORT *pExport1 = CONTAINING_RECORD(Left, WINUM_CACHE_EXPORT, RbNode);
87  WINUM_CACHE_EXPORT *pExport2 = CONTAINING_RECORD(Right, WINUM_CACHE_EXPORT, RbNode);
88 
89  if (pExport1->Rva < pExport2->Rva)
90  {
91  return -1;
92  }
93  else if (pExport1->Rva > pExport2->Rva)
94  {
95  return 1;
96  }
97 
98  return 0;
99 }
100 
101 
104  _In_ RBNODE *Node,
105  _In_ void *Key
106  )
120 {
121  WINUM_CACHE_EXPORT *pExport = CONTAINING_RECORD(Node, WINUM_CACHE_EXPORT, RbNode);
122  DWORD *pair = (DWORD *)Key;
123 
124  if (pair[0] >= pExport->Rva && pair[0] < pExport->Rva + pair[1])
125  {
126  return 0;
127  }
128  else if (pExport->Rva < pair[0])
129  {
130  return -1;
131  }
132  else
133  {
134  return 1;
135  }
136 }
137 
138 
140 static BOOLEAN
142  _In_ RBNODE *Node,
143  _In_ void *Module
144  )
158 {
159  WIN_PROCESS_MODULE *pMod = Module;
160  WINUM_MODULE_CACHE *pCache = pMod->Cache;
161 
162  WINUM_CACHE_EXPORT *pExport = CONTAINING_RECORD(Node, WINUM_CACHE_EXPORT, RbNode);
163 
164  for (DWORD i = 0; i < pExport->NumberOfOffsets; i++)
165  {
166  pExport->Names[i] = pCache->Exports.Names + (pExport->NameOffsets[i] - pCache->Exports.StartNames);
167  // It's okay to typecast to DWORD here, we don't parse images larger than 2G anyway.
168  pExport->NameLens[i] = (DWORD)strlen(pExport->Names[i]);
169  pExport->NameHashes[i] = Crc32Compute(pExport->Names[i], pExport->NameLens[i], INITIAL_CRC_VALUE);
170 
171  if (pMod->Path->NameHash == NAMEHASH_NTDLL && !pCache->MemoryFuncsRead)
172  {
173  switch (pExport->NameLens[i])
174  {
175  case 6:
176  if (0 == strcmp(pExport->Names[i], "memcpy"))
177  {
178  pCache->MemFuncs.MemcpyRva = pExport->Rva;
179  }
180  else if (0 == strcmp(pExport->Names[i], "memset"))
181  {
182  pCache->MemFuncs.MemsetRva = pExport->Rva;
183  }
184  break;
185  case 7:
186  if (0 == strcmp(pExport->Names[i], "memmove"))
187  {
188  pCache->MemFuncs.MemmoveRva = pExport->Rva;
189  }
190  break;
191  case 8:
192  if (0 == strcmp(pExport->Names[i], "memcpy_s"))
193  {
194  pCache->MemFuncs.MemcpySRva = pExport->Rva;
195  }
196  break;
197  case 9:
198  if (0 == strcmp(pExport->Names[i], "memmove_s"))
199  {
200  pCache->MemFuncs.MemmoveSRva = pExport->Rva;
201  }
202  break;
203 
204  default:
205  break;
206  }
207 
208  if (pCache->MemFuncs.MemcpyRva != 0 &&
209  pCache->MemFuncs.MemcpySRva != 0 &&
210  pCache->MemFuncs.MemmoveRva != 0 &&
211  pCache->MemFuncs.MemmoveSRva != 0 &&
212  pCache->MemFuncs.MemsetRva != 0)
213  {
214  pCache->MemoryFuncsRead = TRUE;
215  }
216 
217  }
218  }
219 
220  return TRUE;
221 }
222 
223 
226  _In_ WIN_PROCESS_MODULE *Module,
227  _In_ QWORD Gva,
228  _In_ DWORD Length
229  )
241 {
242  if (Module == NULL)
243  {
244  return NULL;
245  }
246 
247  for (QWORD crtGva = Gva; Gva - crtGva < Length; crtGva--)
248  {
249  WINUM_CACHE_EXPORT *pExport = IntWinUmModCacheExportFind(Module, (DWORD)(crtGva - Module->VirtualBase), 0);
250 
251  if (pExport != NULL)
252  {
253  return pExport;
254  }
255  }
256 
257  return NULL;
258 }
259 
260 
263  _In_ WIN_PROCESS_MODULE *Module,
264  _In_ DWORD Rva,
265  _In_ DWORD ErrorRange
266  )
278 {
279  WINUM_CACHE_EXPORT target = {0};
280  WINUM_MODULE_CACHE *pCache;
281  PRBNODE found;
282 
283  pCache = Module->Cache;
284  if (NULL == pCache || !pCache->ExportDirRead)
285  {
286  return NULL;
287  }
288 
289  target.Rva = Rva;
290  found = NULL;
291 
292  if (0 == ErrorRange)
293  {
294  RbLookupNode(&pCache->Exports.Tree, &target.RbNode, &found);
295  }
296  else
297  {
298  DWORD pair[2] = { 0 };
299  pair[0] = Rva;
300  pair[1] = ErrorRange;
301 
304  pair,
305  &found);
306  }
307 
308  if (NULL == found)
309  {
310  return NULL;
311  }
312 
313  return CONTAINING_RECORD(found, WINUM_CACHE_EXPORT, RbNode);
314 }
315 
316 
317 static INTSTATUS
319  _In_ WINUM_MODULE_CACHE *Cache
320  )
328 {
329  INTSTATUS status, finalStatus = INT_STATUS_SUCCESS;
330 
331  list_for_each(gWinProcesses, WIN_PROCESS_OBJECT, pProc)
332  {
333  if (pProc->Subsystemx64 != NULL)
334  {
335  list_for_each(pProc->Subsystemx64->ProcessModules, WIN_PROCESS_MODULE, pMod)
336  {
337  if (pMod->Cache == Cache && pMod->ExportsSwapHandle != NULL)
338  {
339  status = IntSwapMemRemoveTransaction(pMod->ExportsSwapHandle);
340  if (!INT_SUCCESS(status))
341  {
342  ERROR("[ERROR] IntSwapMemRemoveTransaction failed: 0x%08x\n", status);
343  finalStatus = status;
344  }
345 
346  pMod->ExportsSwapHandle = NULL;
347  }
348  }
349  }
350 
351  if (pProc->Subsystemx86 != NULL)
352  {
353  list_for_each(pProc->Subsystemx86->ProcessModules, WIN_PROCESS_MODULE, pMod)
354  {
355  if (pMod->Cache == Cache && pMod->ExportsSwapHandle != NULL)
356  {
357  status = IntSwapMemRemoveTransaction(pMod->ExportsSwapHandle);
358  if (!INT_SUCCESS(status))
359  {
360  ERROR("[ERROR] IntSwapMemRemoveTransaction failed: 0x%08x\n", status);
361  finalStatus = status;
362  }
363 
364  pMod->ExportsSwapHandle = NULL;
365  }
366  }
367  }
368  }
369 
370  return finalStatus;
371 }
372 
373 
374 static INTSTATUS
376  _In_ void *Context,
377  _In_ QWORD Cr3,
378  _In_ QWORD VirtualAddress,
379  _In_ QWORD PhysicalAddress,
380  _In_reads_bytes_(DataSize) void *Data,
381  _In_ DWORD DataSize,
382  _In_ DWORD Flags
383  )
407 {
408  WIN_PROCESS_MODULE *pMod;
409  WINUM_MODULE_CACHE *pCache;
410  const IMAGE_EXPORT_DIRECTORY *exportDir;
411  INTSTATUS status;
412  PBYTE buffer;
413 
415  UNREFERENCED_PARAMETER(VirtualAddress);
416  UNREFERENCED_PARAMETER(DataSize);
417  UNREFERENCED_PARAMETER(PhysicalAddress);
418  UNREFERENCED_PARAMETER(Flags);
419 
420  pMod = (WIN_PROCESS_MODULE *)Context;
421  pCache = pMod->Cache;
422 
423  pMod->ExportsSwapHandle = NULL;
424 
425  // We already completed our Exports cache from a swap handle, so no bother
426  if (pCache->Exports.Array != NULL)
427  {
428  TRACE("[INFO] Export cache for %s is already completed, bailing out\n", utf16_for_log(pMod->Path->Path));
429  return INT_STATUS_SUCCESS;
430  }
431 
432  status = IntWinModCancelExportTransactions(pCache);
433  if (!INT_SUCCESS(status))
434  {
435  ERROR("[ERROR] IntWinModCancelExportTransactions failed: 0x%08x\n", status);
436  }
437 
438  pCache->Exports.StartNames = DWORD_MAX;
439  pCache->Exports.EndNames = 0;
440 
441  buffer = Data;
442  exportDir = (PIMAGE_EXPORT_DIRECTORY)Data;
443 
444  if (exportDir->NumberOfNames > WINUMCACHE_MAX_EXPORTS)
445  {
446  WARNING("[WARNING] NumberOfNames is %d for module %s\n",
447  exportDir->NumberOfNames, utf16_for_log(pMod->Path->Path));
448  }
449 
450  if ((exportDir->AddressOfNameOrdinals >= pCache->Info.EatRva + pCache->Info.EatSize) ||
451  (exportDir->AddressOfNameOrdinals < pCache->Info.EatRva) ||
452  (exportDir->AddressOfNameOrdinals + exportDir->NumberOfNames * 2 >= pCache->Info.EatRva + pCache->Info.EatSize))
453  {
454  WARNING("[WARNING] AddressOfNameOrdinals %08x points outside of EAT %08x:%08x, %d names\n",
455  exportDir->AddressOfNameOrdinals, pCache->Info.EatRva, pCache->Info.EatSize, exportDir->NumberOfNames);
456 
458  goto _err_exit;
459  }
460 
461  if ((exportDir->AddressOfFunctions >= pCache->Info.EatRva + pCache->Info.EatSize) ||
462  (exportDir->AddressOfFunctions < pCache->Info.EatRva) ||
463  (exportDir->AddressOfFunctions + exportDir->NumberOfFunctions * 4 >=
464  pCache->Info.EatRva + pCache->Info.EatSize))
465  {
466  WARNING("[WARNING] AddressOfFunctions %08x points outside of Eat %08x:%08x, %d functions\n",
467  exportDir->AddressOfFunctions, pCache->Info.EatRva, pCache->Info.EatSize,
468  exportDir->NumberOfFunctions);
469 
471  goto _err_exit;
472  }
473 
474  // We can't check more than this since names size are variable. There are more checks below anyway.
475  // NOTE: we could impose a minimum of 2 chars per name (one letter + NULL terminator)
476  if ((exportDir->AddressOfNames >= pCache->Info.EatRva + pCache->Info.EatSize) ||
477  (exportDir->AddressOfNames < pCache->Info.EatRva))
478  {
479  WARNING("[WARNING] AddressOfNames %08x points outside of Eat %08x:%08x, %d names\n",
480  exportDir->AddressOfNames, pCache->Info.EatRva, pCache->Info.EatSize, exportDir->NumberOfNames);
481 
483  goto _err_exit;
484  }
485 
487  exportDir->NumberOfNames) * sizeof(*pCache->Exports.Array),
488  IC_TAG_EXPCH);
489  if (NULL == pCache->Exports.Array)
490  {
492  goto _err_exit;
493  }
494 
495  for (DWORD i = 0; i < MIN(WINUMCACHE_MAX_EXPORTS, exportDir->NumberOfNames); i++)
496  {
497  DWORD offset, exportRva, namePointer, length;
498  char *name;
499  WORD exportOrdinal;
500  WINUM_CACHE_EXPORT *pExport, target;
501  RBNODE *retNode = NULL;
502 
504 
505  offset = (exportDir->AddressOfNameOrdinals - pCache->Info.EatRva) + i * 2;
506  if (offset + sizeof(WORD) > pCache->Info.EatSize)
507  {
508  ERROR("[WARNING] Name ordinal %08x outside of EAT %08x:%08x (AddressOfNameOrdinals at %08x)\n",
509  offset, pCache->Info.EatRva, pCache->Info.EatRva + pCache->Info.EatSize,
510  exportDir->AddressOfNameOrdinals);
511 
512  goto _err_exit;
513  }
514 
515  exportOrdinal = *(WORD *)(buffer + offset);
516 
517  if (exportOrdinal > exportDir->NumberOfFunctions)
518  {
519  ERROR("[WARNING] We have export ordinal %d, but only %d functions, %d names!\n",
520  exportOrdinal, exportDir->NumberOfFunctions, exportDir->NumberOfNames);
521 
522  goto _err_exit;
523  }
524 
525  offset = (exportDir->AddressOfFunctions - pCache->Info.EatRva) + exportOrdinal * 4;
526  if (offset + sizeof(DWORD) > pCache->Info.EatSize)
527  {
528  ERROR("[WARNING] Function offset %08x outside of EAT %08x:%08x (AddressOfFunctions at %08x)\n",
529  offset, pCache->Info.EatRva, pCache->Info.EatRva + pCache->Info.EatSize,
530  exportDir->AddressOfFunctions);
531 
532  goto _err_exit;
533  }
534 
535  exportRva = *(DWORD *)(buffer + offset);
536 
537  offset = (exportDir->AddressOfNames - pCache->Info.EatRva) + i * 4;
538  if (offset + sizeof(DWORD) > pCache->Info.EatSize)
539  {
540  ERROR("[WARNING] Name offset %08x outside of EAT %08x:%08x (AddressOfNames at %08x)\n",
541  offset, pCache->Info.EatRva, pCache->Info.EatRva + pCache->Info.EatSize,
542  exportDir->AddressOfNames);
543 
544  goto _err_exit;
545  }
546 
547  namePointer = *(DWORD *)(buffer + offset) - pCache->Info.EatRva;
548  if (namePointer >= pCache->Info.EatSize)
549  {
550  ERROR("[WARNING] Name pointer %08x outside of EAT %08x:%08x\n",
551  namePointer, pCache->Info.EatRva, pCache->Info.EatRva + pCache->Info.EatSize);
552  goto _err_exit;
553  }
554 
555  length = 0;
556  name = (char *)buffer + namePointer;
557  while (*name)
558  {
559  length++;
560  name++;
561 
562  if (namePointer + length >= pCache->Info.EatSize)
563  {
564  ERROR("[WARNING] Name pointer %08x will be outside of EAT %08x:%08x after %d bytes\n",
565  namePointer, pCache->Info.EatRva,
566  pCache->Info.EatRva + pCache->Info.EatSize, length);
567  goto _err_exit;
568  }
569  }
570 
571  if (namePointer + length >= pCache->Info.EatSize)
572  {
573  goto _err_exit;
574  }
575 
576  if (pCache->Exports.StartNames > namePointer)
577  {
578  pCache->Exports.StartNames = namePointer;
579  }
580 
581  if (pCache->Exports.EndNames < namePointer + length + 1)
582  {
583  pCache->Exports.EndNames = namePointer + length + 1;
584  }
585 
586  target.Rva = exportRva;
587  if (!INT_SUCCESS(RbLookupNode(&pCache->Exports.Tree, &target.RbNode, &retNode)) || retNode == NULL)
588  {
589  pExport = &pCache->Exports.Array[i];
590  }
591  else
592  {
593  pExport = CONTAINING_RECORD(retNode, WINUM_CACHE_EXPORT, RbNode);
594  }
595 
596  pExport->Rva = exportRva;
597 
598  if (pExport->NumberOfOffsets < MAX_OFFSETS_PER_NAME)
599  {
600  pExport->NameOffsets[pExport->NumberOfOffsets++] = namePointer;
601  }
602 
603  status = RbInsertNode(&pCache->Exports.Tree, &pExport->RbNode);
604  if (INT_STATUS_KEY_ALREADY_EXISTS == status)
605  {
606  continue;
607  }
608  }
609 
610  if (pCache->Exports.StartNames >= pCache->Exports.EndNames)
611  {
612  ERROR("[ERROR] Start of names (%x) it's after (or equal tot he end of names (%x)\n",
613  pCache->Exports.StartNames, pCache->Exports.EndNames);
614  goto _err_exit;
615  }
616 
618  if (NULL == pCache->Exports.Names)
619  {
621  goto _err_exit;
622  }
623 
624  memcpy(pCache->Exports.Names,
625  buffer + pCache->Exports.StartNames,
626  pCache->Exports.EndNames - pCache->Exports.StartNames);
627 
629 
630  pCache->ExportDirRead = TRUE;
631 
632  return INT_STATUS_SUCCESS;
633 
634 _err_exit:
635 
636  // This may make the Introcore inject a #PF any time this dll is loaded into a process.
637  RbUninit(&pCache->Exports.Tree);
638 
639  if (pCache->Exports.Array)
640  {
642  }
643 
644  return status;
645 }
646 
647 
648 static BOOLEAN
650  _In_ DWORD NameHash
651  )
660 {
661  for (DWORD i = 0; i < ARRAYSIZE(gExportedDirsToCache); i++)
662  {
663  if (gExportedDirsToCache[i] == NameHash)
664  {
665  return TRUE;
666  }
667  }
668 
669  return FALSE;
670 }
671 
672 
673 static WINUM_MODULE_CACHE *
675  _In_ WIN_PROCESS_MODULE *Module
676  )
684 {
685  WINUM_MODULE_CACHE *pCache = HpAllocWithTag(sizeof(*pCache), IC_TAG_MODCH);
686  if (NULL == pCache)
687  {
688  return NULL;
689  }
690 
691  pCache->ModuleNameHash = Module->Path->NameHash;
692  pCache->Wow64 = gGuest.Guest64 && (Module->Subsystem->SubsystemType == winSubsys32Bit);
693  pCache->Dirty = Module->Subsystem->Process->StaticDetected != 0 || Module->Subsystem->Process->LateProtection;
694 
695  TRACE("[WINUMCACHE] Create cache for module '%s', wow64: %d, dirty: %d.\n",
696  utf16_for_log(Module->Path->Name), pCache->Wow64, pCache->Dirty);
697 
698  InsertTailList(&gWinUmCaches, &pCache->Link);
699 
700  return pCache;
701 }
702 
703 
704 static INTSTATUS
706  _In_ WIN_PROCESS_MODULE *Module
707  )
720 {
721  INTSTATUS status = INT_STATUS_SUCCESS;
722  WINUM_MODULE_CACHE *pCache = Module->Cache;
723 
724  if (NULL != pCache->Exports.Array)
725  {
726  TRACE("[INFO] Already filled cache for module %s\n", utf16_for_log(Module->Path->Path));
728  }
729 
730  RbPreinit(&pCache->Exports.Tree);
731 
733 
734  if (!IntWinUmModMustCacheExports(Module->Path->NameHash))
735  {
737  }
738 
739  if ((pCache->Info.EatRva >= pCache->Info.SizeOfImage) ||
740  ((QWORD)pCache->Info.EatRva + pCache->Info.EatSize > pCache->Info.SizeOfImage))
741  {
742  ERROR("[ERROR] EAT %08x:%08x points outside image (%08x) for module %s!\n",
743  pCache->Info.EatRva, pCache->Info.EatSize, pCache->Info.SizeOfImage,
744  utf16_for_log(Module->Path->Path));
745 
747  goto _cleanup_and_exit;
748  }
749 
750  if (pCache->Info.EatSize > ONE_MEGABYTE)
751  {
752  ERROR("[WARNING] Module '%s' has EAT bigger than 1 MB: %08x:%08x\n",
753  utf16_for_log(Module->Path->Path), pCache->Info.EatRva, pCache->Info.EatSize);
754 
755  status = INT_STATUS_NOT_SUPPORTED;
756  goto _cleanup_and_exit;
757  }
758 
759  status = IntSwapMemReadData(Module->Subsystem->Process->Cr3,
760  Module->VirtualBase + pCache->Info.EatRva,
761  pCache->Info.EatSize,
763  Module,
764  0,
766  NULL,
767  &Module->ExportsSwapHandle);
768  if (!INT_SUCCESS(status))
769  {
770  ERROR("[ERROR] IntSwapMemReadData failed for 0x%016llx, 0x%x: 0x%08x\n",
771  Module->VirtualBase + pCache->Info.EatRva, pCache->Info.EatSize, status);
772  }
773 
774 _cleanup_and_exit:
775  if (!INT_SUCCESS(status))
776  {
777  RbUninit(&pCache->Exports.Tree);
778  }
779  else
780  {
781  TRACE("[WINUMCACHE] Fill export cache for module '%s', wow64 %d.\n",
782  utf16_for_log(Module->Path->Name), pCache->Wow64);
783  }
784 
785  return status;
786 }
787 
788 
789 static INTSTATUS
791  _In_ WIN_PROCESS_MODULE *Module,
792  _In_ BYTE *Headers
793  )
806 {
807  INTSTATUS status;
808  WINUM_MODULE_CACHE *pCache = Module->Cache;
809  IMAGE_DATA_DIRECTORY iatDirectory = { 0 };
810  IMAGE_DATA_DIRECTORY eatDirectory = { 0 };
811  INTRO_PE_INFO peInfo = { 0 };
812 
813  if (pCache->Headers != NULL)
814  {
816  }
817 
818  status = IntPeValidateHeader(Module->VirtualBase, Headers, PAGE_SIZE, &peInfo, 0);
819  if (!INT_SUCCESS(status))
820  {
821  ERROR("[ERROR] We have invalid headers for module '%s'!\n",
822  utf16_for_log(Module->Path->Name));
823  return status;
824  }
825 
826  status = IntPeGetDirectory(Module->VirtualBase, Headers, IMAGE_DIRECTORY_ENTRY_IAT, &iatDirectory);
827  if (!INT_SUCCESS(status) && status != INT_STATUS_NOT_FOUND)
828  {
829  ERROR("[ERROR] IntPeGetDirectory failed: 0x%08x\n", status);
830  return status;
831  }
832 
833  status = IntPeGetDirectory(Module->VirtualBase, Headers, IMAGE_DIRECTORY_ENTRY_EXPORT, &eatDirectory);
834  if (!INT_SUCCESS(status) && status != INT_STATUS_NOT_FOUND)
835  {
836  ERROR("[ERROR] IntPeGetDirectory failed: 0x%08x\n", status);
837  return status;
838  }
839 
841  if (NULL == pCache->Headers)
842  {
843  return status;
844  }
845 
846  memcpy(pCache->Headers, Headers, 4096);
847 
848  TRACE("[WINUMCACHE] Fill header cache for module '%s', wow64: %d\n",
849  utf16_for_log(Module->Path->Name), pCache->Wow64);
850 
851  pCache->Info.IatRva = iatDirectory.VirtualAddress;
852  pCache->Info.IatSize = iatDirectory.Size;
853 
854  pCache->Info.EatRva = eatDirectory.VirtualAddress;
855  pCache->Info.EatSize = eatDirectory.Size;
856 
857  pCache->Info.SizeOfImage = peInfo.SizeOfImage;
858  pCache->Info.TimeDateStamp = peInfo.TimeDateStamp;
859  Module->Size = peInfo.SizeOfImage;
860 
861  Module->Is64BitModule = peInfo.Image64Bit;
862 
863  if ((Module->Is64BitModule && (Module->Subsystem->SubsystemType != winSubsys64Bit)) ||
864  (!Module->Is64BitModule && (Module->Subsystem->SubsystemType == winSubsys64Bit)))
865  {
866  ERROR("[ERROR] %d/%d invalid for module '%s'\n",
867  Module->Is64BitModule, (Module->Subsystem->SubsystemType == winSubsys64Bit),
868  utf16_for_log(Module->Path->Path));
869  }
870 
871  if (pCache->Info.SizeOfImage > Module->Size)
872  {
873  WARNING("[WARNING] Shady image size for module '%s': 0x%x/0x%x\n",
874  utf16_for_log(Module->Path->Path), pCache->Info.SizeOfImage, Module->Size);
875  }
876 
877  if ((pCache->Info.IatRva >= pCache->Info.SizeOfImage) ||
878  (pCache->Info.IatSize > pCache->Info.SizeOfImage) ||
879  ((QWORD)pCache->Info.IatRva + pCache->Info.IatSize > pCache->Info.SizeOfImage))
880  {
881  WARNING("[WARNING] Shady IAT for module '%s': RVA = %x, size = 0x%x\n",
882  utf16_for_log(Module->Path->Path), pCache->Info.IatRva, pCache->Info.IatSize);
883  }
884 
885  if ((pCache->Info.EatRva >= pCache->Info.SizeOfImage) ||
886  (pCache->Info.EatSize > pCache->Info.SizeOfImage) ||
887  ((QWORD)pCache->Info.EatRva + pCache->Info.EatSize > pCache->Info.SizeOfImage))
888  {
889  WARNING("[WARNING] Shady EAT for module %s: RVA = %x, size = 0x%x, image size = 0x%x\n",
890  utf16_for_log(Module->Path->Path), pCache->Info.EatRva,
891  pCache->Info.EatSize, pCache->Info.SizeOfImage);
892  }
893 
894  return INT_STATUS_SUCCESS;
895 }
896 
897 
898 static WINUM_MODULE_CACHE *
900  _In_ WIN_PROCESS_MODULE *Module
901  )
913 {
914  BOOLEAN wow64 = gGuest.Guest64 && (Module->Subsystem->SubsystemType == winSubsys32Bit);
915 
916  LIST_ENTRY *list = gWinUmCaches.Flink;
917  while (list != &gWinUmCaches)
918  {
920  list = list->Flink;
921 
922  if (pCache->ModuleNameHash == Module->Path->NameHash &&
923  pCache->Wow64 == wow64 &&
924  !pCache->Dirty)
925  {
926  TRACE("[WINUMCACHE] Reuse cache for module '%s'\n", utf16_for_log(Module->Path->Name));
927  return pCache;
928  }
929  }
930 
931  return NULL;
932 }
933 
934 
935 void
937  _In_ WIN_PROCESS_MODULE *Module
938  )
948 {
949  Module->Cache = IntWinUmModCacheFetch(Module);
950  if (NULL != Module->Cache)
951  {
952  return;
953  }
954 
955  Module->Cache = IntWinModCacheCreate(Module);
956 }
957 
958 
959 static void
961  _In_ WINUM_MODULE_CACHE *Cache
962  )
971 {
972  if (NULL == Cache)
973  {
974  return;
975  }
976 
977  RemoveEntryList(&Cache->Link);
978 
979  if (Cache->Exports.Array != NULL)
980  {
981  RbUninit(&Cache->Exports.Tree);
982  HpFreeAndNullWithTag(&Cache->Exports.Array, IC_TAG_EXPCH);
983  }
984 
985  if (NULL != Cache->Exports.Names)
986  {
987  HpFreeAndNullWithTag(&Cache->Exports.Names, IC_TAG_NAME);
988  }
989 
990  if (Cache->Headers != NULL)
991  {
992  HpFreeAndNullWithTag(&Cache->Headers, IC_TAG_HDRS);
993  }
994 
996 }
997 
998 
999 void
1001  _In_ WINUM_MODULE_CACHE *Cache
1002  )
1011 {
1012  if (NULL == Cache)
1013  {
1014  return;
1015  }
1016 
1017  if (Cache->Dirty)
1018  {
1019  IntWinUmCacheRemoveCache(Cache);
1020  }
1021 }
1022 
1023 
1024 INTSTATUS
1026  _In_ WIN_PROCESS_MODULE *Module,
1027  _In_reads_bytes_(4096) BYTE *Headers
1028  )
1037 {
1038  INTSTATUS status;
1039 
1040  if (Module == NULL)
1041  {
1043  }
1044 
1045  status = IntWinUmModCacheFillHeaders(Module, Headers);
1046  if (!INT_SUCCESS(status))
1047  {
1048  WARNING("[WARNING] Failed to fill header cache with status: 0x%08X.\n", status);
1049  return status;
1050  }
1051 
1052  status = IntWinUmModCacheFillExports(Module);
1053  if (!INT_SUCCESS(status))
1054  {
1055  WARNING("[WARNING] Failed to fill export cache with status: 0x%08X.\n", status);
1056  }
1057 
1058  return status;
1059 }
1060 
1061 
1062 BOOLEAN
1064  _In_ WIN_PROCESS_MODULE *Module
1065  )
1071 {
1072  if (NULL == Module || NULL == Module->Cache)
1073  {
1074  return FALSE;
1075  }
1076 
1077  return Module->Cache->ExportDirRead;
1078 }
1079 
1080 
1081 void
1083  void
1084  )
1088 {
1089  LIST_ENTRY *list = gWinUmCaches.Flink;
1090  while (list != &gWinUmCaches)
1091  {
1093 
1094  list = list->Flink;
1095 
1096  IntWinUmCacheRemoveCache(pCache);
1097  }
1098 }
WINUM_PATH * Path
Module path.
Definition: winummodule.h:62
void IntWinUmModCacheRelease(WINUM_MODULE_CACHE *Cache)
Removes a module cache, if it was written (it&#39;s dirty).
Definition: winumcache.c:1000
BOOLEAN ExportDirRead
True if the exports directory has been read.
Definition: winumcache.h:101
RBTREE Tree
The RB tree containing all the exports (WINUM_CACHE_EXPORT entries).
Definition: winumcache.h:61
int FUNC_RbTreeNodeCustomCompare(RBNODE *Node, void *Key)
Definition: rbtree.h:70
_Bool BOOLEAN
Definition: intro_types.h:58
static BOOLEAN IntWinModCacheFixNamePointers(RBNODE *Node, void *Module)
Fixes the names, lens and hashes inside the given RB node, provided the info inside the module...
Definition: winumcache.c:141
#define CONTAINING_RECORD(List, Type, Member)
Definition: introlists.h:36
uint8_t BYTE
Definition: intro_types.h:47
static INTSTATUS IntWinUmModCacheFillExports(WIN_PROCESS_MODULE *Module)
Fills the exports cache of the provided module with each exported symbol.
Definition: winumcache.c:705
#define NAMEHASH_KERNELBASE
Definition: winummodule.h:13
DWORD IatSize
Size of the imports table.
Definition: winumcache.h:88
DWORD Crc32Compute(const void *Buffer, size_t Size, DWORD InitialCrc)
Computes the CRC for a byte array.
Definition: crc32.c:103
#define _In_
Definition: intro_sal.h:21
INTSTATUS IntPeGetDirectory(QWORD ImageBase, BYTE *ImageBaseBuffer, DWORD DirectoryEntry, IMAGE_DATA_DIRECTORY *Directory)
Validate & return the indicated image data directory.
Definition: winpe.c:552
#define NAMEHASH_WOW64CPU
Definition: winummodule.h:18
BOOLEAN MemoryFuncsRead
True if the memory functions have been identified.
Definition: winumcache.h:102
#define INT_STATUS_SUCCESS
Definition: introstatus.h:54
uint16_t WORD
Definition: intro_types.h:48
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
DWORD StartNames
First RVA pointing to the exported names.
Definition: winumcache.h:67
DWORD NumberOfOffsets
Number of symbols pointing to the exported RVA.
Definition: winumcache.h:27
static BOOLEAN IntWinUmModMustCacheExports(DWORD NameHash)
Checks of the exports of a module need to be cached.
Definition: winumcache.c:649
void FUNC_RbTreeNodeFree(RBNODE *Node)
Definition: rbtree.h:47
UINT32 VirtualAddress
Definition: winpe.h:98
#define INT_SUCCESS(Status)
Definition: introstatus.h:42
DWORD TimeDateStamp
Time/date stamp.
Definition: winpe.h:600
WINUM_CACHE_EXPORT * IntWinUmModCacheExportFind(WIN_PROCESS_MODULE *Module, DWORD Rva, DWORD ErrorRange)
Tries to find an export in the range [Rva, Rva + ErrorRange].
Definition: winumcache.c:262
Definition: rbtree.h:34
#define ARRAYSIZE(A)
Definition: introdefs.h:101
INTSTATUS RbLookupNode(RBTREE *Tree, RBNODE *NodeToSearch, RBNODE **NodeFound)
Definition: rbtree.c:517
INTSTATUS RbWalkInorderTree(RBTREE *Tree, PFUNC_RbTreeWalkCallback Callback, void *WalkContext)
Definition: rbtree.c:806
#define INT_STATUS_NOT_NEEDED_HINT
Definition: introstatus.h:317
#define ERROR(fmt,...)
Definition: glue.h:62
INTSTATUS RbLookupNodeCustomCompare(RBTREE *Tree, PFUNC_RbTreeNodeCustomCompare CompareFunc, void *Key, RBNODE **NodeFound)
Definition: rbtree.c:552
#define ONE_MEGABYTE
Definition: introdefs.h:90
#define HpAllocWithTag(Len, Tag)
Definition: glue.h:516
#define NAMEHASH_WININET
Definition: winummodule.h:21
int INTSTATUS
The status data type.
Definition: introstatus.h:24
struct _WINUM_MODULE_CACHE::@242 Info
void IntWinUmCacheUninit(void)
Uninit the module cache system. This will remove all cache entries. Use this during Introcore uninit...
Definition: winumcache.c:1082
INTSTATUS RbInit(RBTREE *Tree, PFUNC_RbTreeNodeFree NodeFree, PFUNC_RbTreeNodeCompare NodeCompare)
Definition: rbtree.c:386
int FUNC_RbTreeNodeCompare(RBNODE *Left, RBNODE *Right)
Definition: rbtree.h:59
#define INT_STATUS_NOT_FOUND
Definition: introstatus.h:284
UINT32 AddressOfNameOrdinals
Definition: winpe.h:339
DWORD EatRva
RVA of the exports table.
Definition: winumcache.h:84
DWORD EndNames
Last RVA pointing to the exported names.
Definition: winumcache.h:68
static INTSTATUS IntWinModHandleExportsInMemory(void *Context, QWORD Cr3, QWORD VirtualAddress, QWORD PhysicalAddress, void *Data, DWORD DataSize, DWORD Flags)
Called as soon as the exports section of a module is swapped in.
Definition: winumcache.c:375
static INTSTATUS IntWinModCancelExportTransactions(WINUM_MODULE_CACHE *Cache)
Cancels all pending swap transactions, in any process, for the provided Cache.
Definition: winumcache.c:318
static WINUM_MODULE_CACHE * IntWinUmModCacheFetch(WIN_PROCESS_MODULE *Module)
Returns the cache associated with the provided module.
Definition: winumcache.c:899
static void IntWinModCacheExportNodeFree(RBNODE *Node)
RB tree free function.
Definition: winumcache.c:55
#define MIN(a, b)
Definition: introdefs.h:146
Process subsystem type 32 bit.
Definition: winprocess.h:34
WINUM_CACHE_MEMORY_FUNCS MemFuncs
Memory related functions RVAs.
Definition: winumcache.h:95
#define WINUMCACHE_MAX_EXPORTS
We will not cache more than this many exports.
Definition: winumcache.h:112
#define IC_TAG_EXPCH
Windows UM exports cache.
Definition: memtags.h:106
WINUM_CACHE_EXPORT * Array
The array of WINUM_CACHE_EXPORT entries.
Definition: winumcache.h:62
#define NAMEHASH_NTDLL
Definition: winummodule.h:11
#define _Inout_
Definition: intro_sal.h:20
INTSTATUS IntSwapMemRemoveTransaction(void *Transaction)
Remove a transaction.
Definition: swapmem.c:942
BOOLEAN Wow64
True if this module is Wow64.
Definition: winumcache.h:99
#define NAMEHASH_WS2_32
Definition: winummodule.h:20
#define INITIAL_CRC_VALUE
Definition: introdefs.h:221
uint8_t * PBYTE
Definition: intro_types.h:47
UINT32 NumberOfFunctions
Definition: winpe.h:335
static BOOLEAN RemoveEntryList(LIST_ENTRY *Entry)
Definition: introlists.h:87
BOOLEAN Guest64
True if this is a 64-bit guest, False if it is a 32-bit guest.
Definition: guests.h:290
unsigned long long QWORD
Definition: intro_types.h:53
DWORD MemmoveSRva
RVA of the memmoves function.
Definition: winumcache.h:47
struct _IMAGE_EXPORT_DIRECTORY * PIMAGE_EXPORT_DIRECTORY
LIST_HEAD gWinUmCaches
Definition: winumcache.c:51
static INTSTATUS IntWinUmModCacheFillHeaders(WIN_PROCESS_MODULE *Module, BYTE *Headers)
Fills MZ/PE headers information for the provided module.
Definition: winumcache.c:790
#define TRUE
Definition: intro_types.h:30
LIST_ENTRY Link
Link inside the global list of module caches.
Definition: winumcache.h:78
#define HpFreeAndNullWithTag(Add, Tag)
Definition: glue.h:517
#define TRACE(fmt,...)
Definition: glue.h:58
#define INT_STATUS_KEY_ALREADY_EXISTS
Definition: introstatus.h:239
DWORD MemmoveRva
RVA of the memmove function.
Definition: winumcache.h:46
DWORD MemcpyRva
RVA of the memcpy function.
Definition: winumcache.h:44
DWORD TimeDateStamp
Module time & date stamp.
Definition: winumcache.h:90
WINUM_CACHE_EXPORT * IntWinUmCacheGetExportFromRange(WIN_PROCESS_MODULE *Module, QWORD Gva, DWORD Length)
Tries to find an export in the range [Gva - Length, Gva].
Definition: winumcache.c:225
static void InsertTailList(LIST_ENTRY *ListHead, LIST_ENTRY *Entry)
Definition: introlists.h:135
void RbUninit(RBTREE *Tree)
Definition: rbtree.c:419
#define INT_STATUS_ALREADY_INITIALIZED_HINT
Definition: introstatus.h:323
#define WARNING(fmt,...)
Definition: glue.h:60
Process subsystem type 64 bit.
Definition: winprocess.h:33
LIST_HEAD gWinProcesses
The list of all the processes inside the guest.
Definition: winprocesshp.c:11
#define PAGE_SIZE
Definition: common.h:70
DWORD NameOffsets[MAX_OFFSETS_PER_NAME]
Name RVAs pointing to this exported RVA.
Definition: winumcache.h:26
void RbPreinit(RBTREE *Tree)
Definition: rbtree.c:377
#define UNREFERENCED_PARAMETER(P)
Definition: introdefs.h:29
#define IMAGE_DIRECTORY_ENTRY_EXPORT
Definition: winpe.h:20
DWORD NameHash
The CRC32 hash of the name. Used for fast matching.
Definition: winumpath.h:23
DWORD Rva
The RVA of this export.
Definition: winumcache.h:23
uint32_t DWORD
Definition: intro_types.h:49
DWORD MemcpySRva
RVA of the memcpys function.
Definition: winumcache.h:45
static WINUM_MODULE_CACHE * IntWinModCacheCreate(WIN_PROCESS_MODULE *Module)
Creates an exports cache entry for the provided module.
Definition: winumcache.c:674
INTSTATUS IntPeValidateHeader(QWORD ImageBase, BYTE *ImageBaseBuffer, DWORD ImageBaseBufferSize, INTRO_PE_INFO *PeInfo, QWORD Cr3)
Validates a PE header.
Definition: winpe.c:131
DWORD EatSize
Size of the exports table.
Definition: winumcache.h:85
DWORD NameHashes[MAX_OFFSETS_PER_NAME]
Hashes of the names pointing to this RVA.
Definition: winumcache.h:24
#define _In_reads_bytes_(expr)
Definition: intro_sal.h:25
DWORD SizeOfImage
Size of image.
Definition: winumcache.h:91
#define INT_STATUS_INVALID_OBJECT_TYPE
Definition: introstatus.h:145
#define IC_TAG_NAME
Object name.
Definition: memtags.h:56
#define NAMEHASH_WOW64
Definition: winummodule.h:16
#define DWORD_MAX
Definition: introtypes.h:33
DWORD NameLens[MAX_OFFSETS_PER_NAME]
Length of each name pointing to this RVA.
Definition: winumcache.h:25
GUEST_STATE gGuest
The current guest state.
Definition: guests.c:50
DWORD MemsetRva
RVA of the memset function.
Definition: winumcache.h:48
WINUM_MODULE_CACHE * Cache
Module headers cache.
Definition: winummodule.h:63
#define _Function_class_(expr)
Definition: intro_sal.h:40
#define IMAGE_DIRECTORY_ENTRY_IAT
Definition: winpe.h:33
#define MAX_OFFSETS_PER_NAME
We can have up to this many exports pointing to the same RVA.
Definition: winumcache.h:13
void IntWinUmModCacheGet(WIN_PROCESS_MODULE *Module)
Initializes the cache for the provided module.
Definition: winumcache.c:936
BOOLEAN IntWinUmCacheIsExportDirRead(WIN_PROCESS_MODULE *Module)
Checks if the exports directory of the given module has been read.
Definition: winumcache.c:1063
BOOLEAN Dirty
True if this caches was created for a module loaded by a statically detected process. Dirty caches are NOT reused by other loaded modules, and they will be destroyed when the module is unloaded.
Definition: winumcache.h:106
RBNODE RbNode
RB tree node entry.
Definition: winumcache.h:21
#define LIST_HEAD_INIT(Name)
Definition: introlists.h:39
INTSTATUS RbInsertNode(RBTREE *Tree, RBNODE *Node)
Definition: rbtree.c:606
PCHAR Names[MAX_OFFSETS_PER_NAME]
The names pointing to this RVA. Each name will point inside the Names structure inside WINUM_CACHE_EX...
Definition: winumcache.h:31
char * utf16_for_log(const WCHAR *WString)
Converts a UTF-16 to a UTF-8 string to be used inside logging macros.
Definition: introcore.c:2845
#define NAMEHASH_USER32
Definition: winummodule.h:14
#define INT_STATUS_INVALID_PARAMETER_1
Definition: introstatus.h:62
BOOLEAN FUNC_RbTreeWalkCallback(RBNODE *Node, void *WalkContext)
Definition: rbtree.h:78
#define INT_STATUS_NOT_SUPPORTED
Definition: introstatus.h:287
static void IntWinUmCacheRemoveCache(WINUM_MODULE_CACHE *Cache)
Removes a module cache.
Definition: winumcache.c:960
#define IC_TAG_MODCH
Windows UM module cache.
Definition: memtags.h:107
INTSTATUS IntWinUmModCacheSetHeaders(WIN_PROCESS_MODULE *Module, BYTE *Headers)
Sets the MZ/PE headers in the cache of a given module.
Definition: winumcache.c:1025
UINT32 AddressOfFunctions
Definition: winpe.h:337
static int IntWinModCacheExportNodeCompare(RBNODE *Left, RBNODE *Right)
Compares two RB tree nodes, representing cached exports.
Definition: winumcache.c:71
DWORD SizeOfImage
Size of the image.
Definition: winpe.h:599
BOOLEAN Image64Bit
True if the image is 64 bit.
Definition: winpe.h:596
void * ExportsSwapHandle
Swap handle for the exports.
Definition: winummodule.h:70
#define IC_TAG_HDRS
Module headers as cached inside a KERNEL_MODULE structure.
Definition: memtags.h:52
const DWORD gExportedDirsToCache[]
Definition: winumcache.c:36
#define NAMEHASH_KERNEL32
Definition: winummodule.h:12
#define list_for_each(_head, _struct_type, _var)
Definition: introlists.h:41
DWORD IatRva
RVA of the imports table.
Definition: winumcache.h:87
BYTE * Headers
A buffer containing the MZ/PE headers of this module.
Definition: winumcache.h:97
static int IntWinModCacheExportNodeCompareWithErorr(RBNODE *Node, void *Key)
Checks if the provided key is inside the given RB tree node.
Definition: winumcache.c:103
#define NAMEHASH_WOW64WIN
Definition: winummodule.h:17
WINUM_CACHE_EXPORTS Exports
The exports cache.
Definition: winumcache.h:94
#define SWAPMEM_OPT_UM_FAULT
If set, the PF must be injected only while in user-mode. Use it when reading user-mode memory...
Definition: swapmem.h:21
DWORD ModuleNameHash
The hash on the name of the cached module.
Definition: winumcache.h:80
PCHAR Names
A pointer to a contiguous memory area containing all the exported names.
Definition: winumcache.h:65
#define FALSE
Definition: intro_types.h:34
This structure describes a running process inside the guest.
Definition: winprocess.h:83
#define INT_STATUS_INSUFFICIENT_RESOURCES
Definition: introstatus.h:281
WCHAR * Path
The string which represents the user-mode module path.
Definition: winumpath.h:17