0

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

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

分页结构

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

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

.process /f /i ffffa9841d9952c0现在我使用来自主机的 Windbg 暂停来宾机器并进行有效翻译,使用和将显式进程更改为目标进程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.

上图中,第 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 ................

转换 67 e8 16 33 00 00 00 0a (01100111 11101000 00010110 00110011 00000000 00000000 00000000 0000101 0 )

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

(请注意粗体部分。)

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

转换 67 f8 0e 34 00 00 00 0a (01100111 11111000 00001110 00110100 00000000 00000000 00000000 0000101 0 )

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

PDE 是:

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

转换 67 78 de 31 00 00 00 0a (01100111 01111000 11011110 00110001 00000000 00000000 00000000 0000101 0 )

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

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。

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

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

所以我有以下问题:

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

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

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

4

1 回答 1

0

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

您应该修补 PTE 的 NX 位(这是最重要的位)。

我只是将一些代码修补到 notepad.exe 中,设置执行该代码的入口点(nops 后跟 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

然后在用户区执行堆栈没问题:)

在此处输入图像描述

我听说 GDT 也有类似 NX-Bit 的东西来阻止堆栈执行,GDT 执行阻止和 NX-Bit 在分页级别有什么区别?

嗯,不,GDT 中没有诸如 NX 位之类的东西......此外,您在 x64 中没有任何用于 CS、DS、SS、ES 的段描述符(只有 GS 和 FS)。

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

完全正确:) 这可能会产生有趣的副作用,例如将 PML4E 的 U/S 位从 S(主管)更改为 U(用户),使用户可以访问此条目引用的所有页面...

于 2018-03-16T18:03:06.177 回答