windbgpagingvirtual-address-spacedep

Changing nx-bit directly from paging-level structures


I'm studying about Virtual Address Translation in Windows 10 x64. I also read about PML4,PDP,PDE and PTE and now I'm trying to change the stack's nx-bit in order to execute code in stack by modifying the paging structures.

I saw the following image which describes about nx-bit in PML4E,PDPE,PDE and PTE :

Paging Structure

Let's assume we have created a kernel debug Windbg using VMWare and in the guest machine, we are debugging a simple application using xdbg64.

After attaching to the target process using xdbg64, now rsp points to 00000089F06FF848. I change the program flow and execute jmp rsp and now, rip points to rsp but it gives an access_violation exception because of DEP so I can't execute any code in the stack. (previously I changed the stack with something like xor rax,rax to have a valid assembly code there).

Now I pause the guest machine using Windbg from the host and to make a valid translation, change the explicit process to the target process using .process /f /i ffffa9841d9952c0 and after pressing g ,now our cr3 is valid for translation.

Then I use the following command in order to get PML4E,PDPE,PDE,PTE physical address :

kd> !vtop 0 00000089F06FF848
Amd64VtoP: Virt 00000089`f06ff848, pagedir 34848000
Amd64VtoP: PML4E 34848008
Amd64VtoP: PDPE 3316e138
Amd64VtoP: PDE 340efc18
Amd64VtoP: PTE 31de77f8
Amd64VtoP: Mapped phys 68a6b848
Virtual address 89f06ff848 translates to physical address 68a6b848.

From the above picture , the 63th bit is for NX-Bit then I get all the entries(PML4E, PDPE, PDE, PTE) to see what is there.

In the case of PML4E, it is :

kd> !db 34848008
#34848008 67 e8 16 33 00 00 00 0a-00 00 00 00 00 00 00 00 g..3............
#34848018 67 88 77 55 00 00 00 0a-00 00 00 00 00 00 00 00 g.wU............
#34848028 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
#34848038 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
#34848048 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
#34848058 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
#34848068 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
#34848078 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

Converting 67 e8 16 33 00 00 00 0a (01100111 11101000 00010110 00110011 00000000 00000000 00000000 00001010)

to

67 e8 16 33 00 00 00 0b (01100111 11101000 00010110 00110011 00000000 00000000 00000000 00001011)

(Please note the bold bit.)

PDPE is :

kd> !db 3316e138
#3316e138 67 f8 0e 34 00 00 00 0a-00 00 00 00 00 00 00 00 g..4............
#3316e148 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
#3316e158 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
#3316e168 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
#3316e178 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
#3316e188 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
#3316e198 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
#3316e1a8 00 00 00 00 00 00 00 00-67 f8 0e 34 00 00 00 0a00 00 00 00 00 00 00 00 ................

Converting 67 f8 0e 34 00 00 00 0a (01100111 11111000 00001110 00110100 00000000 00000000 00000000 00001010)

to

67 f8 0e 34 00 00 00 0b (01100111 11111000 00001110 00110100 00000000 00000000 00000000 00001011)

PDE is :

kd> !db 340efc18
#340efc18 67 78 de 31 00 00 00 0a-00 00 00 00 00 00 00 00 gx.1............
#340efc28 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
#340efc38 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
#340efc48 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
#340efc58 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
#340efc68 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
#340efc78 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
#340efc88 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

Converting 67 78 de 31 00 00 00 0a (01100111 01111000 11011110 00110001 00000000 00000000 00000000 00001010)

to

67 78 de 31 00 00 00 0b (01100111 01111000 11011110 00110001 00000000 00000000 00000000 00001011)

And PTE is :

kd> !db 31de77f8
#31de77f8 67 b8 a6 68 00 00 00 81-00 00 00 00 00 00 00 00 g..h............
#31de7808 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
#31de7818 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
#31de7828 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
#31de7838 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
#31de7848 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
#31de7858 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
#31de7868 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

And in this case I didn't modify anything because 67 b8 a6 68 00 00 00 81 is equals 01100111 10111000 10100110 01101000 00000000 00000000 00000000 10000001 since its last bit is 1.

In the last step I run the !vtop again to see if it still translates to the same physical address and I see that it's correct (Points to the same location.)

Then I press g in order test it to see if it can execute the stack contents or not but I see it still gives the same error (access_violation) and cannot execute that address (rsp).

So I have the following questions :

  1. Whats wrong with my modification that doesn't have any affect ?

  2. I heared that GDT also have somthing like NX-Bit that prevents the stack execution, What are diffrences between GDT execution prevention and NX-Bit in paging-level?

  3. Why there are 4-levels that define nx-bit? Is changing just one of the above entries like pml4e affect all the other entries ?


Solution

  • Whats wrong with my modification that doesn't have any affect ?

    You should have patched the NX Bit of the PTE (which is the most significant bit).

    I just patched some code into notepad.exe, set the entry point to execute that code (nops followed by RET):

    Notepad with some code

    Find notepad:

    kd> !process 0 0 notepad.exe PROCESS ffffe000852c5840
        SessionId: 1  Cid: 0108    Peb: 7ff689a4e000  ParentCid: 0d74 FreezeCount 1
        DirBase: 1b1b9000  ObjectTable: ffffc000a6b08280  HandleCount: <Data Not Accessible>
        Image: notepad.exe
    

    Switch context to it:

    kd> .process /p /i ffffe000852c5840
    You need to continue execution (press 'g' <enter>) 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!DbgBreakPointWithStatus:
    fffff803`f63d1300 cc              int     3
    

    This is the content of RSP (0x64a9b5fdd8), just to be sure:

    kd> db 64a9b5fdd8 L10
    00000064`a9b5fdd8  90 90 90 90 90 90 90 90-c3 c3 c3 c3 c3 c3 c3 c3  ................
    

    Check virtual to physical:

    kd> !vtop 0 00000064a9b5fdd8
    Amd64VtoP: Virt 00000064a9b5fdd8, pagedir 000000001b1b9000
    Amd64VtoP: PML4E 000000001b1b9000
    Amd64VtoP: PDPE 0000000019bbdc90
    Amd64VtoP: PDE 00000000416bea68
    Amd64VtoP: PTE 00000000501bfaf8
    Amd64VtoP: Mapped phys 0000000039fcfdd8
    Virtual address 64a9b5fdd8 translates to physical address 39fcfdd8.
    

    Double checking the physical address:

    kd> !db 0000000039fcfdd8 L10
    #39fcfdd8 90 90 90 90 90 90 90 90-c3 c3 c3 c3 c3 c3 c3 c3 ................
    

    Now checking the PTE:

    kd> !dq 00000000501bfaf8 L1
    #501bfaf8 82b00000`39fcf867 
    

    Converting the PTE to binary:

    kd> .formats 82b00000`39fcf867
    Evaluate expression:
      Hex:     82b00000`39fcf867
      Decimal: -9029717251904964505
      Octal:   1012600000007177174147
      Binary:  10000010 10110000 00000000 00000000 00111001 11111100 11111000 01100111
      Chars:   ....9..g
      Time:    ***** Invalid FILETIME
      Float:   low 0.000482503 high -2.58609e-037
      Double:  -9.78598e-296
    

    The most significant bit is set, which means the NX bit is set, so we can't execute on the stack.

    We just want to remove that bit, so technically, just remove the high part of the nibble (instead of 0x82, we'll have 0x02):

    kd> .formats 02b00000`39fcf867
    Evaluate expression:
      Hex:     02b00000`39fcf867
      Decimal: 193654784949811303
      Octal:   0012600000007177174147
      Binary:  00000010 10110000 00000000 00000000 00111001 11111100 11111000 01100111
      Chars:   ....9..g
      Time:    Fri Sep  2 12:34:54.981 2214 (UTC + 1:00)
      Float:   low 0.000482503 high 2.58609e-037
      Double:  9.78598e-296
    

    Rewrite the PTE:

    kd> !eb 501bfaf8+7 02
    

    The rewritten PTE:

    kd> !db 501bfaf8
    #501bfaf8 67 f8 fc 39 00 00 b0 02-
    
    kd> !dq 501bfaf8 L1
    #501bfaf8 02b00000`39fcf867
    

    Execute:

    kd> g
    

    Then no problem executing the stack in user-land :)

    enter image description here

    I heared that GDT also have somthing like NX-Bit that prevents the stack execution, What are diffrences between GDT execution prevention and NX-Bit in paging-level?

    Hmm nope, no such things as NX bit in the GDT... Moreover you don't have any segment descriptors for CS, DS, SS, ES in x64 (only GS and FS).

    Why there are 4-levels that define nx-bit? Is changing just one of the above entries like pml4e affect all the other entries ?

    Exactly :) This can have funny side effects, e.g changing the U/S bit of a PML4E from S (Supervisor) to U (User) gives user access to all pages referenced by this entry...