Press "Enter" to skip to content

Inside Windows Page Frame Number (PFN) – Part 2

Sinaei 0

Hey there,

In the previous part, I’d explained about Page Frame Number and its importance in the OSs architecture. In this part, I’ll trace PFN more practically. I strongly recommend to read the first part, to make sure you didn’t miss anything about basic concepts.

As I described in the previous part, the PFN database is located at nt!MmPFNDatabase, in the previous versions of Windows (<Windows 10) it was statically located at 0xFFFFFA8000000000 but in Windows 10, it’s subject to ASLR.

Converting Physical Address to Virtual Address and 

Virtual Address to Physical Address

  • MmGetVirtualForPhysical (PA -> VA)

One of the purposes of using PFN database is for converting physical address to virtual address but in Windows, you can simply call nt!MmGetVirtualForPhysical and convert your physical address to virtual address, you can see a complete list of Memory Manager Routines in MSDN.

MmGetVirtualForPhysical

The MmGetVirtualForPhysical is the opposite of MmGetPhysicalAddress as it converts virtual address to physical address and MmXXX before every function in Windows kernel means it’s memory-management routine.

If we decompile this function using IDA Pro:

MmGetVirtualForPhysical-IDA

 

So the source is simply like this:

  • MmGetPhysicalAddress (VA -> PA)

This function is responsible for converting Virtual Address to Physical address and will eventually call MiGetPhysicalAddress so we keep investigating and if you decompile MiGetPhysicalAddress you’ll see some interesting function: nt!MiVaToPfn.

MiGetPhysicalAddress

MiVaToPfn, as it seems, is used for converting the Virtual address to PFN.

Let’s track a special virtual address and find its corresponding PFN Record in PFN Database.

Tracking PFN Records

As I described in the first part, poi(nt!MmPfnDatabase)  can be used to get PFN Database Address:

Now, let choose a kernel location to see PFN Record e.g fffff80231612000 (the location where nt module is loaded). (If you want to find user-mode addresses then you should change your context (CR3) and perform the same thing based on your virtual address.)

I convert the fffff80231612000 to its corresponding physical address using !vtop 0 Address.

As you can see it maps to 2012000 and both results of physical and virtual addresses are the same.

We know that the default size of every page in Windows is 4086 Bytes so if we divide our physical address by 4096 (1000h) we can get our PFN Record Number.

In our case, (2012000h/1000h) is equal to 2012, so our PFN Record Number is 2012.

There is also another command called !pte which gives your PFN Number too. Let’s verify if we found the correct value.

PFN Record Number

Ok, !pte and our result are the same. Now let’s find its record. First, you should get the nt!_MmPfn‘s size.

From the above, we have the address of nt!MmPfnDatabase (fffff400`00000000) and every record is exactly 0x30 Bytes so the final command is something like this:

dt nt!_MmPfn (Address of MmPfnDatabase) + (PFN Record Number * _MmPfn size).

We can also use !pfn to get the details of our PFN Record directly.

You can see, we computed the correct value previously.

You can also traverse through all the PFN Entries, just remember to get the maximum allocated pages (Physical or Virtual).

These variables can help you get precise statistics about memory allocations.

  • MmAvailablePages: Total number of available pages on the system the sum of the pages on the zeroed, free, and standby lists
  • MmResidentAvailablePages: Total number of physical pages that would be available if every process were at its minimum working set size
  • MmNumberOfPhysicalPagesTotal number of physical pages available on the system

PFN Data Structures

This section is derived from here, which worth reading:

Free, Zero and Bad lists

We start off by discussing these states – they are the simplest to understand. Pages which can take on any of these states (A flag in the _MMPTE.u3.e1.PageLocation) are kept in their own lists of Free pages (ready to be used), Zero pages (already cleared) or Bad pages (will never be used).

Active Pages: PteAddress points at a hardware PTE.

If the PFN Type is set to Active, then the physical page is used by something. The most important thing to realize is that a valid physical page (frame) must be managed by a PTE.  Since that PTE record must also be accessible to the kernel, it must be mapped in the kernel’s virtual address space.
 
When the PFN is Active, it contains 3 important pieces of information:
  1. The virtual address of the PTE that is managing this physical page (in _MMPFN.PteAddress).
  2. The Page Frame (Physical page number) of the PTE that is managing this physical page (in _MMPFN.u4.PteFrame). Note these two values provide the virtual and physical address of the PTE.
  3. The OriginalPte value (usually the prototype PTE which controls this page). When Windows installs a hardware PTE from a prototype PTE, it will copy the original prototype PTE into this field.

If you want to know about prototype PTE, then there is a good article here.

From the first line, you should understand how to change page attributes for your physical memory.

Let’s see…

Convert page pfn to pte

The Page Table Entry should be converted to binary to see its attributes. First I get the PTE Adress values and then convert it to binary format using .formats.

Note that I choose the first one because its Physical-Page Base Address is equal to 2012 (our PFN Number), the lowest 12 bits are used for attributes while bits 12 to 52 are used to show Physical Address.

0x02012963 —–>  10000000010010(Used for Physical Address),100101100011(used for attributes.)

10000000010010 —–> 0x2012

The following image shows its bits position and meaning. (The last one is PTE.)

Paging Tables

Windbg !vm extension

As I mentioned in the previous part, there is also another extension, called !vm.

The Windbg documentation says:

!vm
The !vm extension displays summary information about virtual memory use statistics on the target system.

!vm [Flags]
Parameters

Flags
Specifies what information will be displayed in the output from this command. This can be any sum of the following bits. The default is 0, which causes the display to include system-wide virtual memory statistics as well as memory statistics for each process.

Bit 0 (0x1)
Causes the display to omit process-specific statistics.

Bit 1 (0x2)
Causes the display to include memory management thread stacks.

Bit 2 (0x4)
(Windows XP and later) Causes the display to include terminal server memory usage.

Bit 3 (0x8)
(Windows XP and later) Causes the display to include the page file write log.

Bit 4 (0x10)
(Windows XP and later) Causes the display to include working set owner thread stacks.

Bit 5 (0x20)
(Windows Vista and later) Causes the display to include kernel virtual address usage.

 

The above extension can be used to get some statistics about memory allocation and the most important field for us is PFN Array Commit.

 

That’s it guys, hope you enjoy reading this topic. If you have any question, then you can use the comments.

Have fun surveying Windows!

References

[Inside Windows Page Frame Number (PFN) – Part 1] (https://rayanfam.com/topics/inside-windows-page-frame-number-part1/)  

[Memory Manager Routines] (https://msdn.microsoft.com/en-us/library/windows/hardware/ff554435(v=vs.85).aspx)  

[Page Frame Number Database] (https://flylib.com/books/en/4.491.1.69/1/)

[Rekall and the windows PFN database] (http://blog.rekall-forensic.com/2016/05/rekall-and-windows-pfn-database.html)

[Prototype PTEs] (https://www.codemachine.com/article_protopte.html)

 

Leave a Reply

Your email address will not be published. Required fields are marked *