直接从分页级结构更改 nx 位

Changing nx-bit directly from paging-level structures

我正在研究 Windows 10 x64 中的虚拟地址转换。我还阅读了有关 PML4、PDP、PDE 和 PTE 的内容,现在我正在尝试更改堆栈的 nx 位,以便通过修改分页结构在堆栈中执行代码。

我看到下图描述了PML4E、PDPE、PDE和PTE中的nx-bit :

Paging Structure

假设我们已经使用 VMWare 创建了一个内核调试 Windbg,并且在来宾计算机中,我们正在使用 xdbg64 调试一个简单的应用程序。

使用 xdbg64 附加到目标进程后,现在 rsp 指向 00000089F06FF848。 我更改程序流程并执行 jmp rsp,现在,rip 指向 rsp 但它给出了 access_violation exception 因为 DEP 所以我无法执行堆栈中的任何代码. (之前我用 xor rax,rax 之类的东西更改了堆栈,以便在那里有一个有效的汇编代码)。

现在我使用来自主机的 Windbg 暂停来宾机器并进行有效的翻译,使用 .process /f /i ffffa9841d9952c0 将显式进程更改为目标进程并在按下 g 之后,现在我们的 cr3翻译有效。

然后我使用以下命令来获取 PML4E、PDPE、PDE、PTE 物理地址:

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.

从above picture开始,第63位是NX-Bit 然后我获取所有条目(PML4E、PDPE、PDE、PTE)以查看其中的内容。

如果是 PML4E,则为:

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)

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

(请注意粗体部分。)

PDPE 是:

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)

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

偏微分方程是:

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)

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

而PTE是:

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 ................

在这种情况下,我没有修改任何东西,因为 67 b8 a6 68 00 00 00 81 等于 01100111 10111000 10100110 01101000 00000000 00000000 00000000 10000001 因为它的最后一位是 1。][=36

在最后一步中,我再次 运行 !vtop 以查看它是否仍转换为相同的物理地址,我发现它是正确的(指向相同的位置。)

然后我按 g 以测试它是否可以执行堆栈内容,但我看到它仍然给出相同的错误 (access_violation) 并且无法执行该地址 ( rsp).

所以我有以下问题:

  1. 我的修改有什么问题没有任何影响?

  2. 我听说GDT也有NX-Bit之类的东西可以防止堆栈执行,GDT执行防止和NX-Bit在分页级别有什么区别?

  3. 为什么有4个级别定义了nx位?仅更改上述条目之一(如 pml4e)会影响所有其他条目吗?

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

你应该已经修补了 PTE 的 NX 位(最高位)。

我刚刚将一些代码修补到 notepad.exe,设置入口点以执行该代码(nop 后跟 RET):

查找记事本:

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

将上下文切换到它:

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

这是RSP(0x64a9b5fdd8)的内容,只是为了确定:

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

检查虚拟到物理:

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.

再次检查物理地址:

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

正在考PTE:

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

将 PTE 转换为二进制文件:

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

设置了最高有效位,这意味着设置了NX位,所以我们不能在堆栈上执行。

我们只想删除那个位,所以从技术上讲,只需删除半字节的高位部分(而不是 0x82,我们将有 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

重写PTE:

kd> !eb 501bfaf8+7 02

重写的PTE:

kd> !db 501bfaf8
#501bfaf8 67 f8 fc 39 00 00 b0 02-

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

执行:

kd> g

然后在user-land中执行堆栈没有问题:)

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?

嗯,不,在 GDT 中没有 NX 位这样的东西...此外,在 x64 中,您没有任何用于 CS、DS、SS、ES 的段描述符(只有 GS 和 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 ?

确实 :) 这可能会产生有趣的副作用,例如,将 PML4E 的 U/S 位从 S(主管)更改为 U(用户)会提供 user 访问权限此条目引用的所有页面...