Windows如何保护进入内核模式的转换?

13

Windows是如何防止用户模式线程任意将CPU转换为内核模式的呢?

我了解以下几点:

  1. 当通过NTDLL进行系统调用时,用户模式线程确实会转换为内核模式。
  2. 转换到内核模式是通过处理器特定的指令完成的。

那么,这些通过NTDLL进行的系统调用有什么特殊之处?为什么用户模式线程不能伪造它并执行处理器特定的指令以转换到内核模式?我知道我在这里缺少了一些关键的Windows架构知识... 那是什么呢?


用户模式线程可以伪造它,但这与仅使用ntdll.dll有何不同?在问题中添加更多关于为什么这会引起您关注的信息。 - Tony Lee
2
我认为OP担心的是从ring 0过渡到运行任意非操作系统代码时所处的ring 0。 - ChrisW
1
Windows 对其暴露给用户模式的参数执行验证。你无法通过它不知道的方式传递无效数据。是的,有些角落被忽视了。只需查找本地特权提升漏洞,这种情况很多。 - unixman83
1
ntdll是你的进程,所以是的,你可以自己调用CPU sysenter。旧病毒曾经使用这些愚蠢的技巧来欺骗保护。 - unixman83
5个回答

18
你可能认为在用户模式下运行的线程正在调用Ring 0,但实际上并非如此。用户模式线程引发了一个异常,被Ring 0代码捕获。用户模式线程被暂停,CPU切换到内核/Ring 0线程,然后可以检查用户模式线程的上下文(例如调用堆栈和寄存器),以确定要做什么。在系统调用之前,确实是异常而不是专门用于调用Ring 0代码的特殊异常。
如果你采纳其他答案的建议并阅读Intel手册,你会发现syscall/sysenter不带任何参数-操作系统决定发生什么。你不能调用任意代码。WinNT使用函数编号将用户模式代码执行哪个内核模式函数(例如,在我的Windows XP机器上,NtOpenFile是fnc 75h(数字随时更改;NTDll的工作之一就是将函数调用映射到fnc号码,将其放入EAX中,将EDX指向传入的参数,然后调用sysenter)。

9

英特尔CPU使用所谓的“保护环”来强制执行安全性。

这里有4个保护环,从0到3编号。在0环中运行的代码拥有最高的权限;它(实际上)可以随心所欲地操作您的计算机。另一方面,在3环中运行的代码则受到严格限制;它只有有限的影响力。而1和2环目前没有任何用途。

在更高权限的环(如0环)中运行的线程可以随意转换到较低的权限环(如1、2或3环)。然而,反过来的转换是严格受控制的。这就是如何维护高权限资源(如内存)等的安全性。

自然地,您的用户模式代码(应用程序和所有内容)在3环中运行,而操作系统的代码在0环中运行。这确保了用户模式线程无法干扰操作系统的数据结构和其他关键资源。

如果想了解所有这些是如何实现的细节,您可以阅读this文章。此外,您可能还想查看Intel手册,特别是Vol 1和Vol 3A,您可以从here下载。

这是适用于Intel处理器的情况。我相信其他架构也有类似的情况发生。


2
大多数其他处理器只关注两个特权级别,基本上等效于x86环0和3。环1和2在当时可能看起来是个好主意,但事实证明它们增加的复杂性并没有增加任何价值。 - RBerteig
1
据我所知,Windows只使用2个环,因为这是RISC架构所支持的全部。由于他们想要用相同的基本架构支持RISC,因此只使用了极端的x86环。 - noctonura

7

我认为(但我可能错了),它用于转换的机制很简单:

  • 用户模式代码执行软件中断
  • 这个(中断)导致分支到中断描述符表(IDT)中指定的位置

防止用户模式代码篡改的方法如下:需要具备特权才能写入IDT;所以只有内核能够指定执行中断时发生什么。


大克里斯回答小克里斯。 - Wadih M.
这是过去的工作方式,但Tony Lee的答案更接近于Windows今天的工作方式。 - Ana Betts
1
我跳过了关于IDT / CPU如何知道从哪里开始执行的部分,所以这也是很有趣的内容可以包括进来。 - Tony Lee

4

运行在用户模式(Ring 3)的代码不能任意切换到内核模式(Ring 0)。它只能通过特殊路由——跳转门、中断和sysenter向量来执行。这些路由受到高度保护,输入数据会被清理,以防止坏数据引起不良行为。

所有这些都是由内核设置的,通常在启动时进行配置。它只能在内核模式下进行配置,因此用户模式代码无法修改它。


3

可以说它的实现方式与Linux的实现方式(相对而言)很相似。两种情况下都会是针对特定CPU的,但在x86上可能是通过INT指令的软中断或通过SYSENTER指令进行。

研究Linux的实现优势在于您可以在没有Windows源代码许可证的情况下进行。

用户空间源码在这里内核空间代码在这里 - 查看entry_32.S和entry_64.S

在x86上的Linux有三种不同的机制:int 0x80、syscall和sysenter。

内核在运行时构建了一个名为vdso的库,C库调用该库来实现syscall函数,具体取决于CPU和系统调用类型使用不同的机制。然后内核为这些机制(如果在特定CPU变体上存在)提供处理程序。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接