PAEne in the ass (redirected)

(continued from here: http://www.osronline.com/showthread.cfm?link=194969, but now my current question is more appropriate to this list)

I forgot I had not yet used the nuke-u-ler option…

.process /i 821d35a8

g

kd> r cr3
cr3=02700280

???

OK… is there some sort of alternative special way to see the full 64 bit CR3 in WinDbg PAE mode (so I can tell if this is actually 0xA02700280), ala the idtl and idtr which break up the 48 bit idtr register?

BA

CR3 is 32bit, even in PAE mode.

As I just said on NTDEV, stop using an alias and I’ll provide more details.
Otherwise take the question elsewhere.

-scott

Scott Noone
Consulting Associate
OSR Open Systems Resources, Inc.
http://www.osronline.com

wrote in message news:xxxxx@windbg…

(continued from here: http://www.osronline.com/showthread.cfm?link=194969,
but now my current question is more appropriate to this list)

I forgot I had not yet used the nuke-u-ler option…

.process /i 821d35a8

g

kd> r cr3
cr3=02700280

???

OK… is there some sort of alternative special way to see the full 64 bit
CR3 in WinDbg PAE mode (so I can tell if this is actually 0xA02700280), ala
the idtl and idtr which break up the 48 bit idtr register?

BA

It is I! Roland McQuine! I have confirmed that no one has ever posted as Roland to this list, so I won’t be mistaken for anyone else :wink: I really had to fight myself to not pick “Scott Someone”…but that’s a story for another day. In the mean time, I believe you said something about providing more details? :slight_smile:

Roland McQuine

OK, hold up Scott, I think I may have figured it out. I was always under the impression that microsoft didn’t use all those page-directory-pointer-tables and such, but I will try that interpretation and see if that’s right… The new intel manuals explain it much better than the old one I was using.

Roland McQuine

>I really had to fight myself to not pick “Scott Someone”

Good choice. Generally it’s a good idea to not piss off the people who, you
know, actually contribute technical information to the community.

-scott

Scott Noone
Consulting Associate
OSR Open Systems Resources, Inc.
http://www.osronline.com

wrote in message news:xxxxx@windbg…

It is I! Roland McQuine! I have confirmed that no one has ever posted as
Roland to this list, so I won’t be mistaken for anyone else :wink: I really had
to fight myself to not pick “Scott Someone”…but that’s a story for another
day. In the mean time, I believe you said something about providing more
details? :slight_smile:

Roland McQuine

See, but Scott Someone would be an homage, not mocking…but oh well.

What I was thinking might work would be that if I interpreted the address according to the section 4.4.1 PDPTE registers in the latest intel manual 3a, which says “When PAE paging is used, CR3 references the base of a 32-Byte page-directory- pointer table. Table 4-7 illustrates how CR3 is used with PAE paging.” and the table says bits “31:5” are “Physical address of the 32-Byte aligned page-directory-pointer table used for linear-address translation”

OK, so if my CR3 is 0x027004c0, then the PFN is (0x027004c0>>5)>>C = 0x138. And this physical address should map to the page directory pointer table which is an array of 4 64 bit PDPTEs interpreted according to table 4.8.

Doing the same hack as before:

kd> !pte f9f79000
VA f9f79000
PDE at 00000000C0603E78 PTE at 00000000C07CFBC8

kd> dq 00000000C07CFBC8 L4
c07cfbc8 000000000e5c88c2 00000000001388c2
c07cfbd8 0000000000000000 0000000000000000
(with my hack installed in the second qword)

Now I can hopefully see PDPT at f9f7a000

kd> dq f9f7a000 L4
f9f7a000 0000062800420000 0000001400000000
f9f7a010 000062dc00000000 0000000000520000

That doesn’t look like a PDPT to me :frowning: So where did I go wrong?

Roland McQuine

Oh, but I should say that yes my fundamental confusion was that based on some old notes I had taken (not directly reading the latest intel manual), I was thinking the CR3 should be interpreted per a PDPTE, which is 64 bits.

Roland McQuine

OK, well one thing I obviously did wrong is that if bit 5 is the first bit of the physical address, then I should be shifting by 4 instead of 5! That still just yields a PNF of 0x270. When I map that, I just see zeros at the f9f7a000 address I mapped it to :-/

Roland McQuine

mmm…no…if the manual says “bit positions 4:0 ignored” then that means bits 0, 1, 2, 3, 4 are ignored so the 5 bit shift was appropriate, since bit [5[ is the 6th bit.

Roland McQuine

I don’t follow what you’re doing here. When you’re in PAE mode, assuming
that we’re using 4K pages the breakdown of the virtual address is:

[PDPTE#][PDE#][PTE#][Offset]

With the bit breakdown being 2-9-9-12.

So, for example:

1: kd> !pte 8f2f0000
VA 8f2f0000
PDE at C06023C8 PTE at C0479780
contains 000000002201E863 contains 000000003F635963
pfn 2201e —DA–KWEV pfn 3f635 -G-DA–KWEV

1: kd> .formats 8f2f0000
Evaluate expression:

Binary: 10001111 00101111 00000000 00000000

Which gives us:

[10][001111001][011110000][000000000000]

Or:

[0x2][0x79][0xF0][0x0]

Which gives us everything we need to translate:

1: kd> !dd @cr3
#3fed4500 34794801 00000000 36795801 00000000
#3fed4510 36716801 00000000 34757801 00000000

First, dump the PDPTE at entry 2:

1: kd> dt -p nt!_hardware_pte 3fed4510
+0x000 Valid : 0y1
+0x000 Write : 0y0
+0x000 Owner : 0y0
+0x000 WriteThrough : 0y0
+0x000 CacheDisable : 0y0
+0x000 Accessed : 0y0
+0x000 Dirty : 0y0
+0x000 LargePage : 0y0
+0x000 Global : 0y0
+0x000 CopyOnWrite : 0y0
+0x000 Prototype : 0y0
+0x000 reserved0 : 0y1
+0x000 PageFrameNumber : 0y00000000110110011100010110 (0x36716)
+0x000 reserved1 : 0y00000000000000000000000000 (0)
+0x000 LowPart : 0x36716801
+0x004 HighPart : 0

Now we can find the PDE by getting the page address of the PDE and then
adding the PDE entry offset:

1: kd> dt -p nt!_hardware_pte
((0x36716*@$pagesize)+(0x79*@@(sizeof(nt!_hardware_pte)))
+0x000 Valid : 0y1
+0x000 Write : 0y1
+0x000 Owner : 0y0
+0x000 WriteThrough : 0y0
+0x000 CacheDisable : 0y0
+0x000 Accessed : 0y1
+0x000 Dirty : 0y1
+0x000 LargePage : 0y0
+0x000 Global : 0y0
+0x000 CopyOnWrite : 0y0
+0x000 Prototype : 0y0
+0x000 reserved0 : 0y1
+0x000 PageFrameNumber : 0y00000000100010000000011110 (0x2201e)
+0x000 reserved1 : 0y00000000000000000000000000 (0)
+0x000 LowPart : 0x2201e863
+0x004 HighPart : 0

Now the same thing for the PTE:

1: kd> dt -p nt!_hardware_pte
((0x2201e*@$pagesize)+(0xF0*@@(sizeof(nt!_hardware_pte)))
+0x000 Valid : 0y1
+0x000 Write : 0y1
+0x000 Owner : 0y0
+0x000 WriteThrough : 0y0
+0x000 CacheDisable : 0y0
+0x000 Accessed : 0y1
+0x000 Dirty : 0y1
+0x000 LargePage : 0y0
+0x000 Global : 0y1
+0x000 CopyOnWrite : 0y0
+0x000 Prototype : 0y0
+0x000 reserved0 : 0y1
+0x000 PageFrameNumber : 0y00000000111111011000110101 (0x3f635)
+0x000 reserved1 : 0y00000000000000000000000000 (0)
+0x000 LowPart : 0x3f635963
+0x004 HighPart : 0

And that gives us our page, which matches the !pte output:

1: kd> !pte 8f2f0000
VA 8f2f0000
PDE at C06023C8 PTE at C0479780
contains 000000002201E863 contains 000000003F635963
pfn 2201e —DA–KWEV pfn 3f635 -G-DA–KWEV

So, like I said I didn’t quite follow what you’re doing but the translation
is fairly straightforward.

-scott

Scott Noone
Consulting Associate
OSR Open Systems Resources, Inc.
http://www.osronline.com

OK, so here’s the thing which I think is being missed: I want to translate virtual addresses to physical addresses, so that I understand how the context switching is and what it’s doing with the CR3 when it’s switching between processes. When you did “1: kd> !dd @cr3” that means you’re in your current context, which is probably the main kernel context.

If you do “!process 0 0” to get the list of other processes, you might see something like I see from the original thread:

!process 0 0

PROCESS 821bdca8 SessionId: 0 Cid: 073c Peb: 7ffd4000 ParentCid: 02b8
DirBase: 027002e0 ObjectTable: e23af508 HandleCount: 94.
Image: VMUpgradeHelper.exe

PROCESS 82030858 SessionId: 0 Cid: 079c Peb: 7ffd5000 ParentCid: 0424
DirBase: 02700300 ObjectTable: e23c3ee8 HandleCount: 144.
Image: wuauclt.exe

PROCESS 821d35a8 SessionId: 0 Cid: 01d8 Peb: 7ffde000 ParentCid: 02b8
DirBase: 02700280 ObjectTable: e2421100 HandleCount: 101.
Image: alg.exe

The issue here is that the DirBase (aka EPROCESS.KPROCESS.DirectoryTableBase[0]) for all of these processes must no longer be a simple physical address where I can just shift by 12 to get a PFN.

for example, if I do this in the first context I am broken in:

kd> !dq @cr3
# 321000 0000000000322001 0000000000323001
# 321010 0000000000324001 0000000000325001

That makes sense to me. Those look like 4 PDPTEs, and they seem to have very clear PFNs if I assume value->PFN translation is shift right by 12 (then my CR3 is PFN 321, and the entries are PFNs 322, 323, 324, 325).

if I then change my context by doing something like this:

.process /i 821d35a8
(to force the context to be that of alg.exe in the above example, though I’m just faking it since my debugged VM no longer looks like that)

kd> !dq @cr3
# 2700280 0000000009efb801 0000000009bbc801
# 2700290 0000000009f3d801 0000000009c3a801

By my previous shift 12 to the right calculation, those should be pfns = 9efb, 9bbc, 9f3d, 9c3a which I suppose could be reasonable.

But the issue is, telling the debugger to swap in the entire different context, how can I understand what it means to swap in that context? I want to understand where the physical frame is that has those 4 qwords for the PDPT at the beginning. So that should be the physical frame which is pointed to by CR3, right? and CR3 currently == 2700280 once I did .process /i ? But how do I convert that 2700280 to a physical address, so that I can just put it in a PTE in order to “this physical address, if mapped to some virtual address, has qwords starting with 00000000`09efb801 at the beginning, which is the PDPT”.

The problem is even earlier in my description though. I said I will assume by first CR3 = 321000 = PFN 0x321. But that’s not how it works in PAE according to intel. Intel Vol 3b (http://www.intel.com/Assets/ja_JP/PDF/manual/253668.pdf) page 4-16 section 4.4.1 says “When PAE paging is used, CR3 references the base of a 32-Byte page-directory- pointer table. Table 4-7 illustrates how CR3 is used with PAE paging.” and the table 4-7 says bits “31:5” are “Physical address of the 32-Byte aligned page-directory-pointer table used for linear-address translation”

So to me that means bits 5 to 31 of 2700280 are a physical address, right? Not just drop bottom 12 bits of CR3. So 2700280 >> 5 = 0x138014. And should assume the PFN is just right shift by 0xC? If so, then PFN == 0x138, right?

The problem is, if I take PFN 0x138, and I put it into a PTE, and I go look at the virtual address that PTE points at, it does show the 4 qwords which the !dq @cr3 command shows. That means, I do not know the correct way to translate the CR3 which is used in some other process context, and which is stored in DirectoryTableBase[0], into a physical address. (Otherwise I could map that physical address to a virtual address to confirm the data is there that I expect.)

So again, to recap: I understand the breaking up of bits to find a PTE for a given virtual address . What I’m interested in is, how does the CR3 for those weird values (weird compared to non-PAE) in the DirectoryTableBase[0], translate into the physical address which holds the PDPT. Because right now, I can never find a way to look at the PDPT for any given EPROCESS. And that means I don’t understand how the context switching is actually working. :frowning:

Roland McQuine

>What I’m interested in is, how does the CR3 for those weird values (weird

compared to non-PAE) in the DirectoryTableBase[0], >translate into the
physical address which holds the PDPT. Because right now, I can never find
a way to look at the PDPT for any given >EPROCESS. And that means I don’t
understand how the context switching is actually working. :frowning:

When the processor translates a virtual address, it starts with whatever
physical address is in CR3. So, in order to change virtual memory context
all the O/S needs to do is change CR3.

Each KPROCESS has a DirectoryTableBase value, which is the base *physical*
address of the virtual memory tables for that process. Whenever a thread
from that process is going to execute, the O/S takes the value of
DirectoryTableBase and stuffs it into CR3. Voila, context changed.

Not sure why this wouldn’t match what you’re seeing, but it matches what I
see:

0: kd> r @cr3
cr3=00185000
0: kd> dt nt!_kprocess @$proc DirectoryTableBase
+0x018 DirectoryTableBase : 0x185000
0: kd> !process 0 0

PROCESS 910c85d8 SessionId: 0 Cid: 0a10 Peb: 7ffd6000 ParentCid: 0214
DirBase: 3fed73e0 ObjectTable: 90f86af0 HandleCount: 625.
Image: SearchIndexer.exe

0: kd> .PROCESS /i 910c85d8
You need to continue execution (press ‘g’ ) for the context
to be switched. When the debugger breaks in again, you will be in
the new process context.
0: kd> g
Break instruction exception - code 80000003 (first chance)
nt!RtlpBreakWithStatusInstruction:
82675394 int 3
0: kd> r @cr3
cr3=3fed73e0
0: kd> dt nt!_kprocess @$proc DirectoryTableBase
+0x018 DirectoryTableBase : 0x3fed73e0

-scott

Scott Noone
Consulting Associate
OSR Open Systems Resources, Inc.
http://www.osronline.com

I think I finally figure this out by guess and test…and even so, while I can see what’s happening, I THINK I understand what’s going on now…

I will show this now in the context of my current debug session:

!process 0 0

PROCESS 821e65f8 SessionId: 0 Cid: 079c Peb: 7ffd7000 ParentCid: 051c
DirBase: 02700480 ObjectTable: e234a128 HandleCount: 164.
Image: VMwareUser.exe

PROCESS 81f76928 SessionId: 0 Cid: 0514 Peb: 7ffd5000 ParentCid: 051c
DirBase: 02700400 ObjectTable: e2757100 HandleCount: 76.
Image: ctfmon.exe

PROCESS 81e745d0 SessionId: 0 Cid: 00cc Peb: 7ffda000 ParentCid: 051c
DirBase: 02700380 ObjectTable: e2470af8 HandleCount: 174.
Image: wscript.exe

f9efb000 is just some virtual address which I know is used, but which I suspect f9efC000 is unused.

kd> !pte f9efb000
VA f9efb000
PDE at 00000000C0603E78 PTE at 00000000C07CF7D8
contains 00000000017A6963 contains 00000000003ED963

kd> dq 00000000C07CF7D8 L4
c07cf7d8 00000000003ed963 0000000002700963
c07cf7e8 0000000000000000 0000000000000000
(so here we see I hacked in PFN 0x2700, NOT 0x138…the second qword would have been 0, so that confirmed f9efC000 was unused. Now I will think I am mapping PFN 0x2700 to f9efC000)

kd> dq f9efC000 L4
f9efc000 0000000000000000 0000007f00002700
f9efc010 0000000000000000 0000000000000000

Now this is where I stopped before, because this does NOT look like a PDPT with 4 PTPTEs. BUT, I decided to play a guess. I said, what is at f9efc000+0x380? Because for instance the EPROCESS for wscript.exe above says “DirBase: 02700380”…so what if 0x380 is not just flags, but offset?

kd> dq f9efC000+0x380 L4
f9efc380 0000000009efb801 0000000009bbc801
f9efc390 0000000009f3d801 0000000009c3a801

wow! that looks like those values I got before, but I was pretending were in another context. So to use my current debug context, what this means is, if I force the context to EPROCESS for wscript.exe, then I should get those 4 qwords as the PDPT at CR3 maybe?

kd> .process /i 81e745d0
You need to continue execution (press ‘g’ ) for the context
to be switched. When the debugger breaks in again, you will be in
the new process context.
kd> g
Break instruction exception - code 80000003 (first chance)
nt!RtlpBreakWithStatusInstruction:
8052b60c cc int 3
kd> !dq @cr3 L4
# 2700380 0000000009efb801 0000000009bbc801
# 2700390 0000000009f3d801 0000000009c3a801

yay! OK, but let’s see if my understanding is correct, and switch context to EPROCESS for ctfmon.exe…

PROCESS 81f76928 SessionId: 0 Cid: 0514 Peb: 7ffd5000 ParentCid: 051c
DirBase: 02700400 ObjectTable: e2757100 HandleCount: 76.
Image: ctfmon.exe

kd> .process /i 81f76928
You need to continue execution (press ‘g’ ) for the context
to be switched. When the debugger breaks in again, you will be in
the new process context.
kd> g
Break instruction exception - code 80000003 (first chance)
nt!RtlpBreakWithStatusInstruction:
8052b60c cc int 3
kd> !dq @cr3
# 2700400 00000000085bc801 0000000008afd801
# 2700410 0000000008a7e801 0000000008afb801

and if my theory is correct, these 4 qwords should be at f9efC000+0x400, since 02700400 = offset 0x400.

kd> dq f9efC000+0x400 L4
f9efc400 00000000085bc801 0000000008afd801
f9efc410 0000000008a7e801 0000000008afb801

Confirmed! :slight_smile:

What have we learned about how Microsoft does things? Rather than have the PDPT at the beginning of its own frame (which would be wasteful of space), all of the PDPTs are in an array on (apparently) the same frame. The CR3 literally is the physical address, and it works because it’s not assumed to be a page aligned address, it’s a page aligned address 0x2700XXX + offset 0x400 = 0x2700400.

So what was my stupidness? Thinking of PDPTs like they be at the beginning of a page-aligned address (like CR3s are in non-PAE). Every mistake is obvious when looking backwards. But thanks for trying to help.

Roland McQuine

“Not sure why this wouldn’t match what you’re seeing, but it matches what I see:”

Yeah, so that was why .process /i was eventually helpful for making me believe that the value in DirectoryTableBase[0] was a real CR3 value (I’m still kind of curious on what DirectoryTableBase[1] is?). But like I just said (I was writing while you were apparently), my wrong thinking was about the PDPT needing to be page-aligned, like CR3 when in non-PAE (and also the fact that the intel manual’s description of the bits 5-31 being the actual physical address…that threw me off too, but I’ll go with what works over what’s in the manual.)

Roland McQuine