Bitdefender Hypervisor Memory Introspection
winpfn.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 Bitdefender
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 #include "winpfn.h"
6 #include "hook.h"
7 
8 
10 static LIST_HEAD gWinPfns = LIST_HEAD_INIT(gWinPfns);
11 
20 #define for_each_pfn_lock(_var_name) list_for_each (gWinPfns, WIN_PFN_LOCK, _var_name)
21 
22 
25  _In_ QWORD MmPfnDatabase
26  )
35 {
36  INTSTATUS status;
37  VA_TRANSLATION pfnTranslation = {0};
38  VA_TRANSLATION kernTrans = {0};
39  QWORD pteAddress, pfnAddress;
40  BYTE pPfn[0x60]; // there is no way the _MM_PFN will be bigger than this, ever!
41  DWORD pfnSize;
42 
43  if (MmPfnDatabase % (2 * ONE_MEGABYTE) != 0)
44  {
46  }
47 
48  if (gGuest.Guest64)
49  {
51  if (!INT_SUCCESS(status))
52  {
53  ERROR("[ERROR] IntTranslateVirtualAddressEx: 0x%08x\n", status);
54  return status;
55  }
56 
57  pfnAddress = WIN_PFN_GET_STRUCT_VA(MmPfnDatabase, kernTrans.PhysicalAddress);
58  pfnAddress += WIN_KM_FIELD(Mmpfn, Pte);
59 
60  // Read the pfn entry corresponding to the kernel and analyze it
61  status = IntKernVirtMemFetchQword(pfnAddress, &pteAddress);
62  if (!INT_SUCCESS(status))
63  {
64  return status;
65  }
66 
67  status = IntTranslateVirtualAddressEx(pteAddress, gGuest.Mm.SystemCr3, TRFLG_NONE, &pfnTranslation);
68  if (!INT_SUCCESS(status))
69  {
70  return status;
71  }
72 
73  for (DWORD l = 0; l < kernTrans.MappingsCount - 1; l++)
74  {
75  // The translations must be the same (the second to the first, third to second, etc)
76  if (pfnTranslation.MappingsTrace[l + 1] != kernTrans.MappingsTrace[l])
77  {
79  }
80  }
81 
82  pfnSize = WIN_KM_FIELD(Mmpfn, Size);
83  if (pfnSize > sizeof(pPfn))
84  {
85  ERROR("[ERROR] The PFN size is too large: %d bytes!", pfnSize);
87  }
88 
89  // The read is OK, we make sure that pfnSize <= sizeof(pPfn).
90  status = IntKernVirtMemRead(MmPfnDatabase, pfnSize, pPfn, NULL);
91  if (!INT_SUCCESS(status))
92  {
94  }
95 
96  status = INT_STATUS_SUCCESS;
97  }
98  else
99  {
100  // Check the pfn of the first 100 pages in the
101  // .text section. This works because we have the guarantee that the
102  // section is not paged and present at this point.
103  QWORD kVa = gGuest.KernelVa;
104 
105  if (gGuest.PaeEnabled)
106  {
107  pfnSize = WIN_KM_FIELD(Mmpfn, PaeSize);
108  }
109  else
110  {
111  pfnSize = WIN_KM_FIELD(Mmpfn, Size);
112  }
113  if (pfnSize > sizeof(pPfn))
114  {
115  ERROR("[ERROR] The PFN size is too large: %d bytes!", pfnSize);
117  }
118 
119  for (DWORD page = 0; page < 100; page++)
120  {
121  VA_TRANSLATION translation = {0};
122  WORD refCount, pageLocation;
123 
124  status = IntTranslateVirtualAddressEx(kVa, gGuest.Mm.SystemCr3, TRFLG_NONE, &translation);
125  if (!INT_SUCCESS(status) || (translation.Flags & PT_P) == 0)
126  {
127  ERROR("[ERROR] IntTranslateVirtualAddressEx failed for 0x%016llx (kernel at 0x%016llx): 0x%08x\n",
128  kVa, gGuest.KernelVa, status);
129  return status;
130  }
131 
132  pfnAddress = WIN_PFN_GET_STRUCT_VA(MmPfnDatabase, translation.PhysicalAddress);
133 
134  // XEN stuff: we can't map two pages, so read the memory
135  // The read is OK, we make sure that pfnSize <= sizeof(pPfn).
136  status = IntKernVirtMemRead(pfnAddress, pfnSize, pPfn, NULL);
137  if (!INT_SUCCESS(status))
138  {
139  return status;
140  }
141 
142  if (gGuest.PaeEnabled)
143  {
144  pteAddress = *(DWORD *)(pPfn + WIN_KM_FIELD(Mmpfn, PaePte));
145  refCount = *(WORD *)(pPfn + WIN_KM_FIELD(Mmpfn, PaeRefCount));
146  pageLocation = *(WORD *)(pPfn + WIN_KM_FIELD(Mmpfn, PaeFlags)) & 7;
147  }
148  else
149  {
150  pteAddress = *(DWORD *)(pPfn + WIN_KM_FIELD(Mmpfn, Pte));
151  refCount = *(WORD *)(pPfn + WIN_KM_FIELD(Mmpfn, RefCount));
152  pageLocation = *(WORD *)(pPfn + WIN_KM_FIELD(Mmpfn, Flags)) & 7;
153  }
154 
155  if (!IS_KERNEL_POINTER_WIN(FALSE, pteAddress) ||
156  pteAddress == 0xffffffff ||
157  pteAddress < gGuest.KernelVa)
158  {
160  }
161 
162  if (refCount > 0xff || refCount == 0 ||
163  (pageLocation != WinPfnActivePage &&
164  pageLocation != WinPfnModifiedPage))
165  {
167  }
168 
169  kVa += PAGE_SIZE;
170  }
171 
172  // The read is OK, we make sure that pfnSize <= sizeof(pPfn).
173  status = IntKernVirtMemRead(MmPfnDatabase, pfnSize, pPfn, NULL);
174  if (!INT_SUCCESS(status))
175  {
177  }
178 
179  // NULL page pfn should be NULL
180  for (DWORD j = 8; j < pfnSize; j++)
181  {
182  if (pPfn[j] != 0)
183  {
184  ERROR("[ERROR] NULL page pfn 0x%016llx should be NULL!\n", MmPfnDatabase);
186  }
187  }
188 
189  status = INT_STATUS_SUCCESS;
190  }
191 
192  return status;
193 }
194 
195 
196 static INTSTATUS
198  _In_ QWORD PhysicalAddress,
199  _In_ BOOLEAN Increment
200  )
221 {
222  BYTE *pPfnMap;
223  QWORD pfnGva = 0;
224  WORD pageLocation, initialFlags, initialRefCount;
225  DWORD refCount;
226  INTSTATUS status;
227  BOOLEAN save = FALSE;
228 
229  // Get the address of PFN entry for the given physical address
230  pfnGva = WIN_PFN_GET_STRUCT_VA(gWinGuest->MmPfnDatabase, PhysicalAddress);
231 
233  {
234  pfnGva += WIN_KM_FIELD(Mmpfn, RefCount);
235  }
236  else
237  {
238  pfnGva += WIN_KM_FIELD(Mmpfn, PaeRefCount);
239  }
240 
241  pfnGva = FIX_GUEST_POINTER(gGuest.Guest64, pfnGva);
242 
243  status = IntVirtMemMap(pfnGva, 4, gGuest.Mm.SystemCr3, 0, &pPfnMap);
244  if (!INT_SUCCESS(status))
245  {
246  ERROR("[ERROR] Failed to get _MMPFN.u3 from address 0x%016llx for GPA 0x%016llx: 0x%08x\n",
247  pfnGva, PhysicalAddress, status);
248  return status;
249  }
250 
251  pageLocation = ((0xFFFF0000 & *(DWORD *)pPfnMap) >> 16) & 7; // the first 3 bits represents the page location
252  initialFlags = (0xffff0000 & *(DWORD *)pPfnMap) >> 16;
253  initialRefCount = 0x0000ffff & *(DWORD *)pPfnMap;
254  refCount = initialRefCount;
255 
256  if (Increment)
257  {
258  // Make sure the page is good (when coming from hibernate/standby pages can be in modified state)
259  if (pageLocation != WinPfnModifiedPage &&
260  pageLocation != WinPfnActivePage &&
261  pageLocation != WinPfnModifiedNowritePage &&
262  pageLocation != WinPfnStandbyPage)
263  {
264  WARNING("[WARNING] Cannot lock a page that isn't active. u3=0x%08x, pageLocation=%d\n",
265  *(DWORD *)pPfnMap, pageLocation);
266  status = INT_STATUS_NOT_SUPPORTED;
267  goto leave;
268  }
269 
270  // Sometimes, even if the page is present and the driver that uses it loaded, the page has
271  // the reference count 0, so mark this page uninitialized and will be locked after the windows initializes it
272  if (refCount == 0)
273  {
274  ERROR("[ERROR] The page is not initialized!\n");
276  goto leave;
277  }
278 
279  if (refCount + WIN_PFN_INC_VALUE > WIN_PFN_REF_MAX)
280  {
281  CRITICAL("[ERROR] Reference counter is higher than expected: 0x%08x\n", refCount);
282  goto leave;
283  }
284 
285  save = TRUE;
286  refCount += WIN_PFN_INC_VALUE;
287  }
288  else // Decrement the reference count
289  {
290  if (refCount < WIN_PFN_INC_VALUE)
291  {
292  ERROR("[ERROR] Ref counter underflow! rc = 0x%08x\n", refCount);
294  goto leave;
295  }
296 
297  refCount -= WIN_PFN_INC_VALUE;
298  save = TRUE;
299  }
300 
301  if (!save)
302  {
303  goto leave;
304  }
305 
306  // Write the modifications back, but only the ref count. If the page was change from Active to Modified since
307  // the mapping, leave it as it is, since who may know what can happen
308  refCount = (WORD)_InterlockedCompareExchange16((INT16 *)pPfnMap, (INT16)refCount, (INT16)initialRefCount);
309  if (initialRefCount != refCount)
310  {
311  ERROR("[ERROR] The ref count was change since we started locking it "
312  "until now... Initial: 0x%04x, Now: 0x%04x\n", initialRefCount, *(WORD *)pPfnMap);
313 
314  // if somehow the page was released in the meantime, return with error...
315  if (refCount == 0)
316  {
317  ERROR("[ERROR] The page was released in the meantime! rc = 0x%08x\n", refCount);
318  status = INT_STATUS_NOT_SUPPORTED;
319  }
320  }
321 
322  // Just a check so we know if there are race conditions (maybe some other processor starts unloading this page
323  // and the flags for RemovalRequested/InPageError are set)... If we ever see this, we will treat it.
324  if (initialFlags != (WORD)_InterlockedCompareExchange16((INT16 *)(pPfnMap + 2), (INT16)initialFlags,
325  (INT16)initialFlags))
326  {
327  ERROR("[ERROR] Initial flags changed: Initial: 0x%04x, Now: 0x%04x\n", initialFlags, *(WORD *)(pPfnMap + 2));
328  }
329 
330 leave:
331  IntVirtMemUnmap(&pPfnMap);
332 
333  return status;
334 }
335 
336 
337 static PWIN_PFN_LOCK
339  _In_ QWORD GpaPage
340  )
348 {
349  GpaPage &= PHYS_PAGE_MASK;
350 
351  for_each_pfn_lock(pLock)
352  {
353  if (GpaPage == pLock->GpaPage)
354  {
355  return pLock;
356  }
357  }
358 
359  return NULL;
360 }
361 
362 
363 static PWIN_PFN_LOCK
365  _In_ QWORD GvaPage
366  )
374 {
375  GvaPage &= PAGE_MASK;
376 
377  for_each_pfn_lock(pLock)
378  {
379  if (GvaPage == pLock->Page)
380  {
381  return pLock;
382  }
383  }
384 
385  return NULL;
386 }
387 
388 
389 static INTSTATUS
391  _Inout_ WIN_PFN_LOCK *PfnLock,
392  _In_ QWORD NewGpa
393  )
412 {
413  INTSTATUS status = INT_STATUS_SUCCESS;
414 
415  // Decrement the reference count of the old page
416  if (0 != PfnLock->GpaPage)
417  {
418  status = IntWinPfnModifyRefCount(PfnLock->GpaPage, FALSE);
419  if (!INT_SUCCESS(status))
420  {
421  ERROR("[ERROR] IntWinPfnModifyRefCount failed for GVA %llx GPA %llx: 0x%08x\n",
422  PfnLock->Page, PfnLock->GpaPage, status);
423  }
424  }
425 
426  PfnLock->GpaPage = NewGpa & PAGE_MASK;
427 
428  if (0 != PfnLock->GpaPage)
429  {
430  // Increment the reference count of the new page
431  status = IntWinPfnModifyRefCount(PfnLock->GpaPage, TRUE);
432  if (!INT_SUCCESS(status))
433  {
434  ERROR("[ERROR] IntWinPfnModifyRefCount failed for 0x%016llx 0x%016llx: 0x%08x\n",
435  PfnLock->Page, PfnLock->GpaPage, status);
436 
437  PfnLock->GpaPage = 0;
438  }
439  }
440 
441  return status;
442 }
443 
444 
445 static INTSTATUS
447  _In_ QWORD Address,
448  _In_ BOOLEAN IsPhysical
449  )
467 {
468  INTSTATUS status;
469  WIN_PFN_LOCK *pLock;
470 
471  if (!IsPhysical && !IS_KERNEL_POINTER_WIN(gGuest.Guest64, Address))
472  {
473  WARNING("[WARNING] Cannot unlock user-mode pages for now: 0x%016llx!\n", Address);
475  }
476 
477  Address &= PAGE_MASK;
478 
479  // When we release by GVA, it should already be present in memory, and if it isn't (was
480  // transitioned to big page), we just ignore it (we check all these below)
481  if (!IsPhysical)
482  {
483  pLock = IntWinPfnFindByGva(Address);
484  }
485  else
486  {
487  pLock = IntWinPfnFindByGpa(Address);
488  }
489 
490  if (NULL == pLock)
491  {
492  return INT_STATUS_NOT_FOUND;
493  }
494 
495  if (0 != --pLock->RefCount)
496  {
497  return INT_STATUS_SUCCESS;
498  }
499 
500  if (pLock->Present && !pLock->LargePage && pLock->GpaPage)
501  {
502  status = IntWinPfnModifyRefCount(pLock->GpaPage, FALSE);
503  if (INT_STATUS_NOT_NEEDED_HINT == status)
504  {
505  LOG("[INFO] Page at 0x%016llx it's not locked by us...\n", Address);
506  }
507  else if (!INT_SUCCESS(status))
508  {
509  return status;
510  }
511  }
512 
513  if (NULL != pLock->SwapHook)
514  {
515  status = IntHookPtsRemoveHook((HOOK_PTS **)&pLock->SwapHook, 0);
516  if (!INT_SUCCESS(status))
517  {
518  ERROR("[ERROR] IntHookPtsRemoveHook failed for 0x%016llx: 0x%08x\n", Address, status);
519  }
520  }
521 
522  RemoveEntryList(&pLock->Link);
523 
525 
526  return INT_STATUS_SUCCESS;
527 }
528 
529 
530 static INTSTATUS
532  _In_ WIN_PFN_LOCK *Context,
533  _In_ QWORD VirtualAddress,
534  _In_ QWORD OldEntry,
535  _In_ QWORD NewEntry,
536  _In_ QWORD OldPageSize,
537  _In_ QWORD NewPageSize
538  )
559 {
560  WIN_PFN_LOCK *pLock = Context;
561  INTSTATUS status;
562  QWORD newGpa;
563 
564  UNREFERENCED_PARAMETER(OldPageSize);
565 
566  if (pLock->RefCount == 0)
567  {
568  LOG("[PFN] Page at 0x%016llx is already released, will leave now...\n", VirtualAddress);
569  return INT_STATUS_SUCCESS;
570  }
571 
572  if (gGuest.Guest64)
573  {
574  newGpa = CLEAN_PHYS_ADDRESS64(NewEntry);
575  }
576  else if (gGuest.PaeEnabled)
577  {
578  newGpa = CLEAN_PHYS_ADDRESS32PAE(NewEntry);
579  }
580  else
581  {
582  newGpa = CLEAN_PHYS_ADDRESS32(NewEntry);
583  }
584 
585  if ((NewPageSize != PAGE_SIZE_4K) || // Transition to a large page, or
586  (0 == (NewEntry & PT_P) && pLock->Present)) // The page is swapped out
587  {
588  TRACE("[PFN] Removing lock for 0x%016llx : 0x%016llx transitions from 0x%016llx to 0x%016llx (large: %d)\n",
589  pLock->Page, pLock->GpaPage, OldEntry, NewEntry, PAGE_SIZE_4K != NewPageSize);
590 
591  // Don't actually remove the lock (there may be pointers to it in other structures),
592  // so just remove the refcount
593  if (0 != pLock->GpaPage)
594  {
595  status = IntWinPfnModifyRefCount(pLock->GpaPage, FALSE);
596  if (!INT_SUCCESS(status))
597  {
598  ERROR("[ERROR] IntWinPfnModifyRefCount failed: 0x%08x\n", status);
599  }
600  }
601 
602  pLock->GpaPage = 0;
603 
604  pLock->Present = 0 != (NewEntry & PT_P);
605  pLock->LargePage = PAGE_SIZE_4K != NewPageSize;
606 
607  if (pLock->LargePage)
608  {
609  status = IntHookPtsRemoveHook((HOOK_PTS **)&pLock->SwapHook, 0);
610  if (!INT_SUCCESS(status))
611  {
612  ERROR("[ERROR] IntHookPtsRemoveHook failed for 0x%016llx: 0x%08x\n", pLock->Page, status);
613  }
614  }
615  }
616  else if (newGpa != pLock->GpaPage)
617  {
618  TRACE("[PFN] Moving lock for 0x%016llx from 0x%016llx -> 0x%016llx\n", pLock->Page, pLock->GpaPage, newGpa);
619 
620  pLock->Present = TRUE;
621  pLock->LargePage = FALSE;
622 
623  status = IntWinPfnMoveLock(pLock, newGpa);
624  if (!INT_SUCCESS(status))
625  {
626  ERROR("[ERROR] IntWinPfnMoveLock failed: 0x%08x\n", status);
627  return status;
628  }
629  }
630 
631  return INT_STATUS_SUCCESS;
632 }
633 
634 
635 static INTSTATUS
637  _In_ QWORD Address,
638  _In_ BOOLEAN IsPhysical,
639  _Out_opt_ PWIN_PFN_LOCK *PfnLock
640  )
659 {
660  INTSTATUS status;
661  WIN_PFN_LOCK *pLock;
662  BOOLEAN isPresent;
663  QWORD gpa, size;
664 
665  if (!IsPhysical && !IS_KERNEL_POINTER_WIN(gGuest.Guest64, Address))
666  {
667  WARNING("[WARNING] Cannot lock user-mode pages for now: 0x%016llx!\n", Address);
669  }
670 
671  Address &= PAGE_MASK;
672 
673  if (!IsPhysical)
674  {
675  VA_TRANSLATION translation = {0};
676 
677  status = IntTranslateVirtualAddressEx(Address, gGuest.Mm.SystemCr3, TRFLG_NONE, &translation);
678  if (!INT_SUCCESS(status) && (INT_STATUS_PAGE_NOT_PRESENT != status) &&
680  {
681  ERROR("[ERROR] IntTranslateVirtualAddressEx failed for GVA 0x%016llx: 0x%08x\n", Address, status);
682  return status;
683  }
684 
685  if (INT_SUCCESS(status) && (0 != (translation.Flags & PT_P)))
686  {
687  isPresent = TRUE;
688  gpa = translation.PhysicalAddress;
689  size = translation.PageSize;
690  }
691  else
692  {
693  isPresent = FALSE;
694  gpa = 0;
695  size = PAGE_SIZE_4K;
696  }
697  }
698  else
699  {
700  isPresent = TRUE;
701  gpa = Address;
702  size = PAGE_SIZE_4K;
703  }
704 
705  if (size != PAGE_SIZE_4K)
706  {
707  // We don't lock big pages anymore
708  if (PfnLock != NULL)
709  {
710  *PfnLock = NULL;
711  }
713  }
714 
715  if (!IsPhysical)
716  {
717  pLock = IntWinPfnFindByGva(Address);
718  }
719  else
720  {
721  pLock = IntWinPfnFindByGpa(gpa);
722  }
723 
724  if (NULL == pLock)
725  {
726  pLock = HpAllocWithTag(sizeof(*pLock), IC_TAG_WPFN);
727  if (NULL == pLock)
728  {
730  }
731  }
732  else // the page is already locked. Increase RefCount and exit
733  {
734  pLock->RefCount++;
735 
736  if (NULL != PfnLock)
737  {
738  *PfnLock = pLock;
739  }
740 
741  return INT_STATUS_SUCCESS;
742  }
743 
744  pLock->Page = Address;
745  pLock->GpaPage = gpa;
746  pLock->RefCount = 1;
747  pLock->Present = isPresent;
748  pLock->LargePage = size != PAGE_SIZE_4K;
749 
750  if (pLock->Present && !pLock->LargePage)
751  {
752  status = IntWinPfnModifyRefCount(pLock->GpaPage, TRUE);
753  }
754  else
755  {
756  // Wait for modification
757  status = INT_STATUS_SUCCESS;
758  }
759 
760  if (INT_STATUS_NOT_INITIALIZED == status)
761  {
762  LOG("[INFO] Page at gva 0x%016llx and gpa 0x%016llx it's not initialized. Will hook the page!\n",
763  Address, gpa);
764  }
765  else if (!INT_SUCCESS(status))
766  {
768 
769  return status;
770  }
771 
772  if (!IsPhysical)
773  {
774  status = IntHookPtsSetHook(0,
775  Address,
777  pLock,
778  NULL,
779  0,
780  (PHOOK_PTS *)&pLock->SwapHook);
781  if (!INT_SUCCESS(status))
782  {
783  ERROR("[ERROR] IntHookPtsSetHook failed: 0x%08x\n", status);
784  }
785  }
786 
787  InsertTailList(&gWinPfns, &pLock->Link);
788 
789  if (NULL != PfnLock)
790  {
791  *PfnLock = pLock;
792  }
793 
794  return INT_STATUS_SUCCESS;
795 }
796 
797 
798 INTSTATUS
800  _In_ QWORD Gva,
801  _Out_opt_ WIN_PFN_LOCK **PfnLock
802  )
814 {
815  return IntWinPfnLockAddress(Gva, FALSE, PfnLock);
816 }
817 
818 
819 INTSTATUS
821  _In_ QWORD Gpa,
822  _Out_opt_ WIN_PFN_LOCK **PfnLock
823  )
832 {
833  return IntWinPfnLockAddress(Gpa, TRUE, PfnLock);
834 }
835 
836 
837 INTSTATUS
839  _Inout_ WIN_PFN_LOCK *PfnLock,
840  _In_ BOOLEAN Force
841  )
855 {
856  INTSTATUS status;
857 
858  if (NULL == PfnLock)
859  {
861  }
862 
863  //
864  // If there still are references to this pfn lock then exit... If we force
865  // it, then down below we set the refcount to 0, and the next time we call
866  // this the refcount will be negative and != 0
867  //
868  if (!Force && (--PfnLock->RefCount) != 0)
869  {
871  }
872 
873  if (!gGuest.ShutDown && PfnLock->GpaPage != 0)
874  {
875  status = IntWinPfnModifyRefCount(PfnLock->GpaPage, FALSE);
876  if (!INT_SUCCESS(status))
877  {
878  ERROR("[ERROR] IntWinPfnModifyRefCount failed: 0x%08x\n", status);
879  }
880  }
881 
882  if (NULL != PfnLock->SwapHook)
883  {
884  status = IntHookPtsRemoveHook((HOOK_PTS **)&PfnLock->SwapHook, 0);
885  if (!INT_SUCCESS(status))
886  {
887  ERROR("[ERROR] IntHookPtsRemoveHook failed for gva 0x%016llx, gpa 0x%016llx: 0x%08x\n",
888  PfnLock->Page, PfnLock->GpaPage, status);
889  }
890  }
891 
892  RemoveEntryList(&PfnLock->Link);
893 
895 
896  return INT_STATUS_SUCCESS;
897 }
898 
899 
900 void
902  void
903  )
907 {
908  DWORD index = 0;
909 
910  LOG("[DBGINTRO] Pfn locks:\n");
911 
912  for_each_pfn_lock(pLock)
913  {
914  LOG(" ## %04d @ %p -> VA: 0x%016llx, GPA: 0x%016llx, SwapHook: %p, RefCount: %x, Present: %d, LargePage: %d\n",
915  index++, pLock, pLock->Page, pLock->GpaPage, pLock->SwapHook, pLock->RefCount,
916  pLock->Present, pLock->LargePage);
917  }
918 }
919 
920 
921 void
923  void
924  )
931 {
932  INTSTATUS status;
933 
934  for_each_pfn_lock(pLock)
935  {
936  ERROR("[ERROR] There should be no pfn locks remaining... Got one on %llx, %llx!\n",
937  pLock->GpaPage, pLock->Page);
938 
939  status = IntWinPfnRemoveLock(pLock, TRUE);
940  if (!INT_SUCCESS(status))
941  {
942  ERROR("[ERROR] IntWinPfnRemoveLock failed: 0x%08x\n", status);
943  continue;
944  }
945  }
946 }
The page is active and valid, but not part of any list (ActiveAndValid).
Definition: winpfn.h:77
#define INT_STATUS_PAGE_NOT_PRESENT
Indicates that a virtual address is not present.
Definition: introstatus.h:438
QWORD PhysicalAddress
The physical address to which VirtualAddress translates to.
Definition: introcore.h:107
_Bool BOOLEAN
Definition: intro_types.h:58
WORD RefCount
The reference count.
Definition: winpfn.h:42
INTSTATUS IntVirtMemUnmap(void **HostPtr)
Unmaps a memory range previously mapped with IntVirtMemMap.
Definition: introcore.c:2234
static int16_t _InterlockedCompareExchange16(int16_t volatile *Destination, int16_t Exchange, int16_t Comparand)
Definition: intrinsics.h:611
QWORD MmPfnDatabase
Guest virtual address of the PFN data base.
Definition: winguest.h:837
uint8_t BYTE
Definition: intro_types.h:47
WINDOWS_GUEST * gWinGuest
Global variable holding the state of a Windows guest.
Definition: winguest.c:37
INTSTATUS IntWinPfnIsMmPfnDatabase(QWORD MmPfnDatabase)
Checks if a a guest virtual address points to MmPfnDatabase.
Definition: winpfn.c:24
#define _In_
Definition: intro_sal.h:21
#define CLEAN_PHYS_ADDRESS64(x)
Definition: pgtable.h:119
QWORD SystemCr3
The Cr3 used to map the kernel.
Definition: guests.h:211
#define INT_STATUS_SUCCESS
Definition: introstatus.h:54
uint16_t WORD
Definition: intro_types.h:48
static LIST_HEAD gWinPfns
The list of locked PFNs.
Definition: winpfn.c:10
BOOLEAN ShutDown
True if the system process protection is in beta (log-only) mode.
Definition: guests.h:313
BOOLEAN Present
True if Page is present.
Definition: winpfn.h:46
#define INT_SUCCESS(Status)
Definition: introstatus.h:42
#define IC_TAG_WPFN
Windows PFN locked page.
Definition: memtags.h:21
INTSTATUS IntWinPfnLockGpa(QWORD Gpa, WIN_PFN_LOCK **PfnLock)
Locks a guest physical address.
Definition: winpfn.c:820
#define INT_STATUS_NOT_NEEDED_HINT
Definition: introstatus.h:317
#define ERROR(fmt,...)
Definition: glue.h:62
#define ONE_MEGABYTE
Definition: introdefs.h:90
#define HpAllocWithTag(Len, Tag)
Definition: glue.h:516
#define PHYS_PAGE_MASK
Definition: pgtable.h:38
int INTSTATUS
The status data type.
Definition: introstatus.h:24
#define INT_STATUS_NOT_FOUND
Definition: introstatus.h:284
#define TRFLG_NONE
No special options.
Definition: introcore.h:82
BOOLEAN LargePage
True if Page is a large page.
Definition: winpfn.h:51
static PWIN_PFN_LOCK IntWinPfnFindByGva(QWORD GvaPage)
Finds a PFN lock by a guest virtual address.
Definition: winpfn.c:364
static INTSTATUS IntWinPfnModifyRefCount(QWORD PhysicalAddress, BOOLEAN Increment)
Modifies the in-guest reference count of a physical page.
Definition: winpfn.c:197
A page from the ModifiedNoWritePageList.
Definition: winpfn.h:75
#define LOG(fmt,...)
Definition: glue.h:61
INTSTATUS IntHookPtsSetHook(QWORD Cr3, QWORD VirtualAddress, PFUNC_SwapCallback Callback, void *Context, void *Parent, DWORD Flags, PHOOK_PTS *Hook)
Start monitoring translation modifications for the given VirtualAddress.
Definition: hook_pts.c:1535
void IntWinPfnUnInit(void)
Uninits the PFN locks.
Definition: winpfn.c:922
DWORD MappingsCount
The number of entries inside the MappingsTrace and MappingsEntries arrays.
Definition: introcore.h:123
INTSTATUS IntWinPfnLockGva(QWORD Gva, WIN_PFN_LOCK **PfnLock)
Locks a guest virtual address.
Definition: winpfn.c:799
void * SwapHook
The swap hook used for Page, is Page is a virtual address.
Definition: winpfn.h:36
#define _Inout_
Definition: intro_sal.h:20
QWORD Flags
The entry that maps VirtualAddress to PhysicalAddress, together with all the control bits...
Definition: introcore.h:119
#define _Out_opt_
Definition: intro_sal.h:30
#define WIN_PFN_INC_VALUE
The value used to increment the reference counter of a PFN database entry.
Definition: winpfn.h:11
#define INT_STATUS_NOT_INITIALIZED
Definition: introstatus.h:266
INTSTATUS IntKernVirtMemFetchQword(QWORD GuestVirtualAddress, QWORD *Data)
Reads 8 bytes from the guest kernel memory.
Definition: introcore.c:811
BOOLEAN PaeEnabled
True if Physical Address Extension is enabled.
Definition: guests.h:295
static BOOLEAN RemoveEntryList(LIST_ENTRY *Entry)
Definition: introlists.h:87
#define PT_P
Definition: pgtable.h:83
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 CLEAN_PHYS_ADDRESS32PAE(x)
Definition: pgtable.h:133
#define TRUE
Definition: intro_types.h:30
#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 HpFreeAndNullWithTag(Add, Tag)
Definition: glue.h:517
INTSTATUS IntHookPtsRemoveHook(HOOK_PTS **Hook, DWORD Flags)
Remove a PTS hook.
Definition: hook_pts.c:1944
#define INT_STATUS_INVALID_INTERNAL_STATE
Definition: introstatus.h:272
A PFN lock.
Definition: winpfn.h:19
QWORD KernelVa
The guest virtual address at which the kernel image.
Definition: guests.h:283
static void InsertTailList(LIST_ENTRY *ListHead, LIST_ENTRY *Entry)
Definition: introlists.h:135
static INTSTATUS IntWinPfnMoveLock(WIN_PFN_LOCK *PfnLock, QWORD NewGpa)
Moves a lock set for a guest virtual address when the page to which it translates to changes...
Definition: winpfn.c:390
INTSTATUS IntTranslateVirtualAddressEx(QWORD Gva, QWORD Cr3, DWORD Flags, VA_TRANSLATION *Translation)
Translates a guest virtual address to a guest physical address.
Definition: introcore.c:1863
LIST_ENTRY Link
Entry inside the gWinPfns list.
Definition: winpfn.h:22
#define WARNING(fmt,...)
Definition: glue.h:60
#define for_each_pfn_lock(_var_name)
Iterates the linked list in gWinPfns.
Definition: winpfn.c:20
#define PAGE_SIZE
Definition: common.h:70
#define UNREFERENCED_PARAMETER(P)
Definition: introdefs.h:29
INTSTATUS IntWinPfnRemoveLock(WIN_PFN_LOCK *PfnLock, BOOLEAN Force)
Removes a PFN lock.
Definition: winpfn.c:838
A page from the StandbyPageList.
Definition: winpfn.h:73
#define WIN_KM_FIELD(Structure, Field)
Macro used to access kernel mode fields inside the WIN_OPAQUE_FIELDS structure.
Definition: winguest.h:740
#define WIN_PFN_GET_STRUCT_VA(MmPfn, Gpa)
Get the address of a guest _MMPFN structure.
Definition: winpfn.h:60
uint32_t DWORD
Definition: intro_types.h:49
#define WIN_PFN_REF_MAX
The maximum value we allow a PFN reference counter to reach.
Definition: winpfn.h:14
int16_t INT16
Definition: intro_types.h:43
#define CLEAN_PHYS_ADDRESS32(x)
Definition: pgtable.h:126
static INTSTATUS IntWinPfnHandleTranslationChange(WIN_PFN_LOCK *Context, QWORD VirtualAddress, QWORD OldEntry, QWORD NewEntry, QWORD OldPageSize, QWORD NewPageSize)
Handles translation changes for locked guest virtual pages.
Definition: winpfn.c:531
void IntWinPfnDump(void)
Prints all the PFN locks.
Definition: winpfn.c:901
#define INT_STATUS_INVALID_OBJECT_TYPE
Definition: introstatus.h:145
__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
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 GpaPage
The guest physical page that is locked.
Definition: winpfn.h:31
#define FIX_GUEST_POINTER(is64, x)
Masks the unused part of a Windows guest virtual address.
Definition: wddefs.h:87
static PWIN_PFN_LOCK IntWinPfnFindByGpa(QWORD GpaPage)
Finds a PFN lock by a guest physical address.
Definition: winpfn.c:338
QWORD PageSize
The page size used for this translation.
Definition: introcore.h:121
QWORD MappingsTrace[MAX_TRANSLATION_DEPTH]
Contains the physical address of each entry within the translation tables.
Definition: introcore.h:111
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
#define LIST_HEAD_INIT(Name)
Definition: introlists.h:39
QWORD Page
The locked page.
Definition: winpfn.h:26
A page from the ModifiedPageList.
Definition: winpfn.h:74
Encapsulates information about a virtual to physical memory translation.
Definition: introcore.h:102
#define INT_STATUS_INVALID_PARAMETER_1
Definition: introstatus.h:62
#define INT_STATUS_NOT_SUPPORTED
Definition: introstatus.h:287
#define CRITICAL(fmt,...)
Definition: glue.h:63
#define PAGE_MASK
Definition: pgtable.h:35
#define PAGE_SIZE_4K
Definition: pgtable.h:10
#define INT_STATUS_INVALID_PARAMETER_2
Definition: introstatus.h:65
static INTSTATUS IntWinPfnLockAddress(QWORD Address, BOOLEAN IsPhysical, PWIN_PFN_LOCK *PfnLock)
Locks a guest page.
Definition: winpfn.c:636
static INTSTATUS IntWinPfnUnlockAddress(QWORD Address, BOOLEAN IsPhysical)
Unlocks a guest page.
Definition: winpfn.c:446
#define FALSE
Definition: intro_types.h:34
#define INT_STATUS_INSUFFICIENT_RESOURCES
Definition: introstatus.h:281