slinkin
slinkin

Reputation: 405

How to translate Virtual to Physical Address (WinDbg)?

It seems I don't understand something.

I'm trying to translate VA to PA on Windows 10 (x86) under VirtualBox. I use Microsoft manual for that. I set up a local kernel debugger (bcedit) and launched CFF Explorer as a tested application. Then I started WinDbg, connected to the kernel and get active processes:

!process 0 0

Found my test application:

    PROCESS a6bd7900  SessionId: 1  Cid: 0988    Peb: 7ffd9000  ParentCid: 0840
    DirBase: ba9ac3c0  ObjectTable: acaeedc0  HandleCount: <Data Not Accessible>
    Image: CFF Explorer.exe

Then get PEB:

.process /p a6bd7900; !peb 7ffd9000

Implicit process is now a6bd7900
PEB at 7ffd9000
    ...
    ImageBaseAddress:         00400000
    ...
    Ldr                       76f99aa0
    Ldr.Initialized:          Yes
    Ldr.InInitializationOrderModuleList: 00881658 . 00887c00
    Ldr.InLoadOrderModuleList:           00881728 . 00887bf0
    Ldr.InMemoryOrderModuleList:         00881730 . 00887bf8
    Base TimeStamp                     Module
    400000 50a8fbd6 Nov 18 18:16:38 2012 C:\Program Files\NTCore\Explorer Suite\CFF Explorer.exe
    76e90000 580ee2c9 Oct 25 07:42:49 2016 C:\WINDOWS\SYSTEM32\ntdll.dll
    74970000 57cf8f7a Sep 07 06:54:34 2016 C:\WINDOWS\system32\KERNEL32.DLL
    ...

I typed "!r" command to print all registers:

cr0 Value: 00720054
cr2 Value: 00720054
cr3 Value: 00720054
cr4 Value: 00720054

cr4 in bin: 00000000 00001010 11111100 10110110 The 5th bit is true what means that PAE is enabled.

Then I opened the Memory windows and typed 400000 address to check I have the header of CFF Explorer.exe in Virtual memory.

enter image description here

Then I tried to get page frame number (PFN) via PTE extension (by the manual):

lkd> !pte 00400000
                    VA 00400000
PDE at C0600010            PTE at C0002000
contains 0000000000000000
contains 0000000000000000
not valid

I've got not a not valid address. At the same time, when I tried to get PFN of kernel32.dll I've got valid address:

lkd> !pte 74970000 
                    VA 74970000
PDE at C0601D20            PTE at C03A4B80
contains 000000000121B867  contains 800000006F1CE005
pfn 121b      ---DA--UWEV  pfn 6f1ce     -------UR-V

And then successfully got the header by physical address via "!dc 6f1ce000".

Then I checked windbg.exe itself and noticed that kernel32.dll has the same base address as CFF Explorer.exe. I always think that each process has own mapping of the dependent module to his own memory, but now it seems not so.

My questions:

  1. Why do I get "not valid" when trying to translate 0x00400000 address?
  2. Please, clear the situation with kernel32.dll and my doubts about mapping the module to each process.

UPDATE 0: I don't know why, but when I debug the kernel as local - I see the same value in ALL registers. I've tried to remote debug the kernel, and now I see the different values for each register:

cr0 Value: 80010033
cr2 Value: 909a301c
cr3 Value: 001a8000
cr4 Value: 000406e9

And now, I can't get either kernel32.dll or the other modules translation. The main questions are opened.

Upvotes: 2

Views: 2464

Answers (1)

Lewis Kelsey
Lewis Kelsey

Reputation: 4677

!pte may not work without the capital /P when setting the process context, because !pte reads the contents of the page table entries via virtual address, starting with nt!MmPteBase (FFFFF6FB7DBED000 in my case) – this is a kernel address – remember that the page tables are in kernel virtual memory meaning the PTs/PDs/PTPTs/PML4 themselves have kernel virtual addresses, so enabling the user mode address bypass will not stop kernel addresses from still being translated in hardware.

Without /P, the debugger will naturally use the page table of the current process in the logical core to access the data at this virtual address using translation in hardware on the CPU, which will work fine for non–process-unique virtual addresses because the same physical page is mapped into all page tables so it doesn't matter what ones currently in the core, but it will not work for any user virtual memory as all user memory is unique to the process (where a virtual page maps to a physical page unique to the process) and neither will it work for any kernel virtual memory that is unique to the process. An example of kernel virtual memory that is unique to the process is the page for user addresses, and the page tables for kernel addresses that contain the page tables

/p and /P are used in order to bypass this, and the debugger accesses the correct dirbase in software and walks the page table in software. /p only bypasses for all user mode addresses and /P also bypasses for all kernel mode addresses.

lkd> !process 0 0 calc.exe
PROCESS fffffa805d954b10
    SessionId: 1  Cid: 3294    Peb: 7fffffdb000  ParentCid: 10f8
    DirBase: 27a385000  ObjectTable: fffff8a02a766e60  HandleCount:  81.
    Image: calc.exe


lkd> .process /p fffffa805d954b10
Implicit process is now fffffa80`5d954b10
lkd> !pte 0`ffbe0000
                                           VA 00000000ffbe0000
PXE at FFFFF6FB7DBED000    PPE at FFFFF6FB7DA00018    PDE at FFFFF6FB40003FE8    PTE at FFFFF680007FDF00
contains 00C0000263E77867  contains 0000000000000000
pfn 263e77    ---DA--UWEV  not valid

----------------------------------------------------------------------------------------

lkd> .process /P fffffa805d954b10
Implicit process is now fffffa80`5d954b10
lkd> !pte 0`ffbe0000
                                           VA 00000000ffbe0000
PXE at FFFFF6FB7DBED000    PPE at FFFFF6FB7DA00018    PDE at FFFFF6FB40003FE8    PTE at FFFFF680007FDF00
contains 00C000000B023867  contains 00D0000759124867  contains 00E0000792FA5867  contains 80F000004D7DD025
pfn b023      ---DA--UWEV  pfn 759124    ---DA--UWEV  pfn 792fa5    ---DA--UWEV  pfn 4d7dd     ----A--UR-V

!vtop 0 ffbe0000 will work without /p or /P because it gets the dirbase PML4 physical address from the EPROCESS structure (EPROCESS is in non–⁠process-unique kernel memory that it can use any page table to access), and then it maps in the PML4 physical page of the correct page table by physical address, showing their physical addresses, and mapping in each resulting physical address of the next entry in the hierarchy into virtual memory so it can read it and continue the walk.

!pte fffffa80`5d954b10 (the EPROCESS address) will work without /p or /P because the EPROCESS physical page block happens to be mapped into all page tables at the same virtual address, so it doesn't matter if the translation is being bypassed by the debugger or if it is being done in hardware with whatever page table is currently in the core.

It appears to me that you only need to do /p or /P once for the whole debug session, and in order to reset it you have to .cache nodecodeptes, which you can't do in a local debugging session for some reason:

lkd> .process /P fffffa805d954b10
Implicit process is now fffffa80`5d954b10
lkd> !pte 10000
                                           VA 0000000000010000
PXE at FFFFF6FB7DBED000    PPE at FFFFF6FB7DA00000    PDE at FFFFF6FB40000000    PTE at FFFFF68000000080
contains 00C000000B023867  contains 01300001F5CA7867  contains 014000030E728867  contains 8D400001A4654867
pfn b023      ---DA--UWEV  pfn 1f5ca7    ---DA--UWEV  pfn 30e728    ---DA--UWEV  pfn 1a4654    ---DA--UW-V

------------------------------------------

lkd> .process fffffa8027653b10
Implicit process is now fffffa80`27653b10
lkd> !pte 10000
                                           VA 0000000000010000
PXE at FFFFF6FB7DBED000    PPE at FFFFF6FB7DA00000    PDE at FFFFF6FB40000000    PTE at FFFFF68000000080
contains 12B0000195039867  contains 036000016A13C867  contains 01400001730BD867  contains FFFFFFFF00000480
pfn 195039    ---DA--UWEV  pfn 16a13c    ---DA--UWEV  pfn 1730bd    ---DA--UWEV  not valid
                                                                                  Proto: VAD
                                                                                  Protect: 4 - ReadWrite

I mean it does say that the behaviour of /p and /P is the same as .cache forcedcodeuser and .cache forcedecodeptes respectively. Omitting both /p and /P does not perform .cache nodecodeptes but leaves it as it is, so once you've set /p on one process it applies to all processes (despite what msdn says, which I think is wrong), and then you can toggle to /P on a new process, and then /P will apply to all processes. When you start the session, the current state is .cache nodecodeptes, and in that state, it depends on the page tables that are actually in the logical core of the processor at the time, which for a local debug will be kd.exe, and for remote debug it will be the process of whatever thread has broken into the debugger.

Upvotes: 1

Related Questions