Hardware supported NX is dependent on PAE (Windows Internals chapter 9. Memory). But why would that be?
The AMD64 Architecture Programmer’s Manual (Volume 2: System Programming) mentions this:
No Execute (NX) Bit. Bit 63. This bit is present in the translation-table entries defined for PAE paging, with the exception that the legacy-mode PDPE does not contain this bit. This bit is not supported by non-PAE paging.
Again from Windows Internals we know that PTEs with PAE enabled are 64 bits long; without PAE they are only 32 bits long. There simply is no bit 63 for non-PAE paging. We can see it in the debugger.
Here is a call stack from the non-PAE crash dump:
0: kd> k
8292dc0c 968a1160 nt!KeBugCheckEx+0x1e
8292dc3c 968a1768 i8042prt!I8xProcessCrashDump+0x251
8292dc88 82840d4d i8042prt!I8042KeyboardInterruptService+0x2ce
8292dc88 8286449a nt!KiInterruptDispatch+0x6d
8292dd24 00000000 nt!KiIdleLoop+0x1a
If we dump out the the ChildEBP for the 2nd frame:
0: kd> !pte 8292dc3c
PDE at C0300828 PTE at C020A4B4
contains 001BF063 contains 0292D963
pfn 1bf —DA–KWEV pfn 292d -G-DA—KWEV
You see that the PTE contains a 32 bit value (0292D963).
The same exercise on a PAE enabled system gives us this call stack:
1: kd> k
807e1c0c 928bd160 nt!KeBugCheckEx+0x1e
807e1c3c 928bd768 i8042prt!I8xProcessCrashDump+0x251
807e1c88 828587cd i8042prt!I8042KeyboardInterruptService+0x2ce
807e1c88 8288101a nt!KiInterruptDispatch+0x6d
807e1d24 00000000 nt!KiIdleLoop+0x1a
The PTE here is 64 bit:
1: kd> !pte 807e1c3c
PDE at C0602018 PTE at C0403F08
contains 000000007A986863 contains 800000002D21F963
pfn 7a986 —DA–KWEV pfn 2d21f -G-DA–KW-V
We would expect bit 63 on the ChildEBP to be set (EBP is on the stack and we would not want to execute any code from the stack: NX should be 1).
1: kd> .formats 800000002D21F963
Binary: 10000000 00000000 00000000 00000000 00101101 00100001 11111001 01100011
How about the return address of that same frame?
1: kd> !pte 928bd768
PDE at C06024A0 PTE at C04945E8
contains 0000000023615863 contains 000000007B9CD121
pfn 23615 —DA–KWEV pfn 7b9cd -G–A—KREV
There the bit is not set:
1: kd> .formats 000000007B9CD121
Binary: 01111011 10011100 11010001 00100001
Note that .formats truncates the preceding 0s.