有两种方式访问硬件:
如果用户模式进程想要在不使用系统调用的情况下直接访问 I/O,并且它知道某个特定的硬件,则它无法通过内存映射 I/O 访问它,因为它不在其地址空间中,因此将会发生分段错误。但是使用 I/O 端口来实现这一点又如何呢?由于I/O端口不在内存地址空间中,而是由处理器直接执行的指令访问,那么会发生什么情况,进程是否可以访问它们?
精简版
不。例如,在 x86 架构中,
in
和 out
是特权指令,只能在 Ring 0 中运行。由于用户模式应用程序在 Ring 3 中运行,因此 CPU 在尝试执行该指令时会出现故障。
长版
一般来说,在强制内存保护和进程分离的操作系统中(例如Windows、Linux、Mac OS,但不是DOS),用户模式应用程序无法直接访问硬件,而是使用系统调用来询问硬件内核代表它们执行所需的操作。然后内核可以决定应用程序是否有权执行所述操作、该操作是否安全等。
如果用户模式应用程序能够执行通常只能由内核完成的任何操作,那么它就是内核(或者有时是 CPU 本身)中的错误/后门和巨大的安全漏洞。
在 x86 架构上,I/O 端口指令在除实模式之外的所有操作模式下都具有特权。尝试执行任何特权指令都会生成故障(具体来说,
#GP
,一般保护故障,对应于 INT 0xD
)。
为了允许用户模式驱动程序等进程工作,操作系统能够通过两种方式覆盖 I/O 指令的特权状态:
IOPL
寄存器(位 12 和 13)中的 FLAGS
(I/O 权限级别)字段。这是一个从 0 到 3 的数字,指定可以运行 I/O 指令的最低特权环。将 IOPL
设置为 3 将允许所有进程访问任何 I/O 端口,这是不安全的,并且违背了保护内存等其他所有内容的目的。因此,操作系统通常将此字段设置为 0。IOPB
(I/O 权限位图)位字段。这只是 65536 位的集合,每个位指定是否可以访问 I/O 端口 (0
) 或不可以 (1
)。如果用户模式驱动程序需要访问端口 0xAB
,那么操作系统可以清除 TSS 中的相应位,并允许用户模式代码仅访问该端口,即使是 IOPL=0
。如果操作系统不包含所有 65536 位,则 CPU 会假定缺少的部分具有特权,因此无法访问。当然。访问 I/O 端口的权限位于标志寄存器的两位中。由于每个任务都有自己唯一的标志寄存器副本,因此每个任务可以具有不同的访问权限或 I/O 权限级别 (
IOPL
)。主要功能是执行 I/O(内核模式或用户模式驱动程序)的任务可以从 IOPL
的 3
中受益,从而允许任务的所有过程执行 I/O。其他任务通常将 IOPL 设置为 0
或 1
,保留为最具特权的过程执行 I/O 指令的权利。因此,如果您想访问特定任务的 I/O 端口,您应该为此任务设置 IOPL=3
。
在 Windows XP 等旧版 Windows 中,可以通过调用
NtSetInformationProcess
并将 ProcessInformationClass.ProcessUserModeIOPL
设置为 3
来完成。
不知道新版本。
这里有一个示例。