在操作系统中,Ring 0和Ring 3是什么? [英] What are Ring 0 and Ring 3 in the context of operating systems?
问题描述
我一直在学习有关Windows驱动程序开发的基础知识,我不断找到术语 Ring 0 和 Ring 3 .这些指的是什么?它们是否与内核模式和用户模式相同?
I've been learning basics about driver development in Windows I keep finding the terms Ring 0 and Ring 3. What do these refer to? Are they the same thing as kernel mode and user mode?
推荐答案
Linux x86环使用概述
了解环在Linux中的用法将使您对环的用途有所了解.
Understanding how rings are used in Linux will give you a good idea of what they are designed for.
在x86保护模式下,CPU始终处于4个振铃之一. Linux内核仅使用0和3:
In x86 protected mode, the CPU is always in one of 4 rings. The Linux kernel only uses 0 and 3:
- 0表示内核
- 3个用户
这是内核vs用户态最硬,最快的定义.
This is the most hard and fast definition of kernel vs userland.
为什么Linux不使用环1和2: CPU特权环:为什么不使用环1和2?
Why Linux does not use rings 1 and 2: CPU Privilege Rings: Why rings 1 and 2 aren't used?
如何确定当前铃声?
当前铃声是通过以下方式选择的:
The current ring is selected by a combination of:
-
全局描述符表:GDT条目的内存表,每个条目都有一个字段
Privl
,该字段对环进行编码.
global descriptor table: a in-memory table of GDT entries, and each entry has a field
Privl
which encodes the ring.
LGDT指令将地址设置为当前描述符表.
The LGDT instruction sets the address to the current descriptor table.
另请参见: http://wiki.osdev.org/Global_Descriptor_Table
该段寄存器CS,DS等指向GDT中某个条目的索引.
the segment registers CS, DS, etc., which point to the index of an entry in the GDT.
例如,CS = 0
表示GDT的第一个条目当前对于执行代码是活动的.
For example, CS = 0
means the first entry of the GDT is currently active for the executing code.
每个环能做什么?
CPU芯片的物理结构如下:
The CPU chip is physically built so that:
-
环0可以做任何事情
ring 0 can do anything
ring 3不能运行多个指令并写入多个寄存器,最值得注意的是:
ring 3 cannot run several instructions and write to several registers, most notably:
-
无法更改自己的铃声!否则,它可能会将自己设置为环0,并且环将无用.
cannot change its own ring! Otherwise, it could set itself to ring 0 and rings would be useless.
换句话说,不能修改当前段描述符,它确定了当前环.
In other words, cannot modify the current segment descriptor, which determines the current ring.
无法修改页表: x86分页如何工作?
换句话说,不能修改CR3寄存器,分页本身会阻止页表的修改.
In other words, cannot modify the CR3 register, and paging itself prevents modification of the page tables.
这出于安全性/易于编程的原因而阻止一个进程查看其他进程的内存.
This prevents one process from seeing the memory of other processes for security / ease of programming reasons.
无法注册中断处理程序.这些是通过写入内存位置来配置的,这也可以通过分页来防止.
cannot register interrupt handlers. Those are configured by writing to memory locations, which is also prevented by paging.
处理程序在环0中运行,并且会破坏安全模型.
Handlers run in ring 0, and would break the security model.
换句话说,不能使用LGDT和LIDT指令.
In other words, cannot use the LGDT and LIDT instructions.
无法执行in
和out
之类的IO指令,因此可以进行任意硬件访问.
cannot do IO instructions like in
and out
, and thus have arbitrary hardware accesses.
例如,否则,如果任何程序都可以直接从磁盘读取文件权限,则文件权限将毫无用处.
Otherwise, for example, file permissions would be useless if any program could directly read from disk.
更确切地说,要感谢任务状态段.
More precisely thanks to Michael Petch: it is actually possible for the OS to allow IO instructions on ring 3, this is actually controlled by the Task state segment.
如果环3最初没有它,则不可能授予自己这样做的权限.
What is not possible is for ring 3 to give itself permission to do so if it didn't have it in the first place.
Linux始终禁止使用它.另请参见:为什么Linux不使用通过TSS进行硬件上下文切换?
Linux always disallows it. See also: Why doesn't Linux use the hardware context switch via the TSS?
程序和操作系统如何在环之间转换?
-
当打开CPU时,它会在环0中开始运行初始程序(虽然不错,但是很不错).您可以认为此初始程序是内核(但通常为当用户级进程希望内核为其执行某些操作(例如写入文件)时,它会使用生成中断的指令,例如
when a userland process wants the kernel to do something for it like write to a file, it uses an instruction that generates an interrupt such as
int 0x80
orsyscall
to signal the kernel. x86-64 Linux syscall hello world example:.data hello_world: .ascii "hello world\n" hello_world_len = . - hello_world .text .global _start _start: /* write */ mov $1, %rax mov $1, %rdi mov $hello_world, %rsi mov $hello_world_len, %rdx syscall /* exit */ mov $60, %rax mov $0, %rdi syscall
编译并运行:
as -o hello_world.o hello_world.S ld -o hello_world.out hello_world.o ./hello_world.out
发生这种情况时,CPU会调用内核在启动时注册的中断回调处理程序.这是一个注册处理程序并使用的具体裸机示例它.
When this happens, the CPU calls an interrupt callback handler which the kernel registered at boot time. Here is a concrete baremetal example that registers a handler and uses it.
此处理程序在环0中运行,该环决定内核是否允许该操作,执行该操作并在环3中重新启动userland程序.x86_64
This handler runs in ring 0, which decides if the kernel will allow this action, do the action, and restart the userland program in ring 3. x86_64
使用
exec
系统调用时(或内核准备新的userland进程的寄存器和内存,然后跳转到入口点并将CPU切换到第3环when the
exec
system call is used (or when the kernel will start/init
), the kernel prepares the registers and memory of the new userland process, then it jumps to the entry point and switches the CPU to ring 3如果程序试图做一些顽皮的事情,例如写入禁止的寄存器或内存地址(由于页面调度),那么CPU还会在环0中调用某些内核回调处理程序.
If the program tries to do something naughty like write to a forbidden register or memory address (because of paging), the CPU also calls some kernel callback handler in ring 0.
但是由于用户空间很顽皮,内核这次可能会终止该进程,或者发出警告并带有信号.
But since the userland was naughty, the kernel might kill the process this time, or give it a warning with a signal.
当内核启动时,它将设置一个固定频率的硬件时钟,该时钟会定期生成中断.
When the kernel boots, it setups a hardware clock with some fixed frequency, which generates interrupts periodically.
此硬件时钟生成运行于环0的中断,并允许它安排要唤醒的用户区进程.
This hardware clock generates interrupts that run ring 0, and allow it to schedule which userland processes to wake up.
这样,即使进程没有进行任何系统调用,调度也可能发生.
This way, scheduling can happen even if the processes are not making any system calls.
多环有什么意义?
分离内核和用户域有两个主要优点:
There are two major advantages of separating kernel and userland:
- 制作程序更容易,因为您可以确定一个程序不会干扰另一个程序.例如,一个用户域进程不必担心由于页面调度而覆盖另一程序的内存,也不必担心将硬件置于另一进程的无效状态.
- 它更安全.例如.文件权限和内存分离可能会阻止黑客应用读取您的银行数据.当然,这假定您信任内核.
如何使用它?
I've created a bare metal setup that should be a good way to manipulate rings directly: https://github.com/cirosantilli/x86-bare-metal-examples
不幸的是,我没有耐心举一个用户区示例,但是我确实进行了页面设置,因此用户区应该是可行的.我希望看到请求请求.
I didn't have the patience to make a userland example unfortunately, but I did go as far as paging setup, so userland should be feasible. I'd love to see a pull request.
或者,Linux内核模块在环0中运行,因此您可以使用它们来尝试特权操作,例如读取控制寄存器:
Alternatively, Linux kernel modules run in ring 0, so you can use them to try out privileged operations, e.g. read the control registers: How to access the control registers cr0,cr2,cr3 from a program? Getting segmentation fault
这是一个方便的QEMU + Buildroot设置即可尝试,而不会杀死主机.
Here is a convenient QEMU + Buildroot setup to try it out without killing your host.
内核模块的缺点是其他kthreads正在运行,并且可能会干扰您的实验.但是从理论上讲,您可以使用内核模块来接管所有的中断处理程序并拥有系统,这实际上是一个有趣的项目.
The downside of kernel modules is that other kthreads are running and could interfere with your experiments. But in theory you can take over all interrupt handlers with your kernel module and own the system, that would be an interesting project actually.
负环
虽然英特尔手册中并未实际提及负环,但实际上有一些CPU模式具有比环0本身更多的功能,因此非常适合负环"名称.
While negative rings are not actually referenced in the Intel manual, there are actually CPU modes which have further capabilities than ring 0 itself, and so are a good fit for the "negative ring" name.
一个示例是虚拟化中使用的管理程序模式.
One example is the hypervisor mode used in virtualization.
有关更多详细信息,请参见:
For further details see:
- https://security.stackexchange.com/questions/129098/what-is -protection-ring-1
- https://security.stackexchange.com/questions /216527/ring-3-利用并存在其他环
- https://security.stackexchange.com/questions/129098/what-is-protection-ring-1
- https://security.stackexchange.com/questions/216527/ring-3-exploits-and-existence-of-other-rings
ARM
在ARM中,这些环被称为异常级别",但主要思想保持不变.
In ARM, the rings are called Exception Levels instead, but the main ideas remain the same.
ARMv8中存在4个异常级别,通常用作:
There exist 4 exception levels in ARMv8, commonly used as:
-
EL0:用户区
EL0: userland
EL1:内核(ARM术语中的主管").
EL1: kernel ("supervisor" in ARM terminology).
使用
svc
指令(SuperVisor调用)输入,以前称为swi
Entered with the
svc
instruction (SuperVisor Call), previously known asswi
before unified assembly, which is the instruction used to make Linux system calls. Hello world ARMv8 example:hello.S
.text .global _start _start: /* write */ mov x0, 1 ldr x1, =msg ldr x2, =len mov x8, 64 svc 0 /* exit */ mov x0, 0 mov x8, 93 svc 0 msg: .ascii "hello syscall v8\n" len = . - msg
在Ubuntu 16.04上使用QEMU进行测试:
Test it out with QEMU on Ubuntu 16.04:
sudo apt-get install qemu-user gcc-arm-linux-gnueabihf arm-linux-gnueabihf-as -o hello.o hello.S arm-linux-gnueabihf-ld -o hello hello.o qemu-arm hello
这是一个具体的裸机示例,其中注册SVC处理程序并进行SVC调用.
Here is a concrete baremetal example that registers an SVC handler and does an SVC call.
EL2:管理程序,例如使用
hvc
指令输入(HyperVisor调用).Entered with the
hvc
instruction (HyperVisor Call).虚拟机监控程序是针对操作系统的,就像操作系统是针对用户态的.
A hypervisor is to an OS, what an OS is to userland.
例如,Xen允许您在同一系统上同时运行多个操作系统,例如Linux或Windows,并且可以将操作系统彼此隔离,以确保安全性和调试便利性,就像Linux在用户级程序中所做的那样.
For example, Xen allows you to run multiple OSes such as Linux or Windows on the same system at the same time, and it isolates the OSes from one another for security and ease of debug, just like Linux does for userland programs.
管理程序是当今云基础架构的关键部分:它们允许多个服务器在单个硬件上运行,使硬件使用率始终接近100%,并节省了大量资金.
Hypervisors are a key part of today's cloud infrastructure: they allow multiple servers to run on a single hardware, keeping hardware usage always close to 100% and saving a lot of money.
AWS使用Xen直到2017年,当时向KVM转移新闻.
AWS for example used Xen until 2017 when its move to KVM made the news.
EL3:再上一层. TODO示例.
EL3: yet another level. TODO example.
使用
smc
指令(安全模式调用)输入Entered with the
smc
instruction (Secure Mode Call)ARMv8体系结构参考模型DDI 0487C.a -D1章-AArch64系统级程序员模型-图D1-1很好地说明了这一点:
The ARMv8 Architecture Reference Model DDI 0487C.a - Chapter D1 - The AArch64 System Level Programmer's Model - Figure D1-1 illustrates this beautifully:
随着 ARMv8.1虚拟化主机扩展(VHE).此扩展使内核可以在EL2中高效运行:
The ARM situation changed a bit with the advent of ARMv8.1 Virtualization Host Extensions (VHE). This extension allows the kernel to run in EL2 efficiently:
创建VHE的原因是,Linux内核虚拟化解决方案(例如KVM)已经超过Xen(例如,AWS向上述KVM的迁移),因为大多数客户端仅需要Linux VM,并且可以想象,在单个项目中,KVM比Xen更简单,效率更高.因此,现在在这些情况下,主机Linux内核将充当管理程序.
VHE was created because in-Linux-kernel virtualization solutions such as KVM have gained ground over Xen (see e.g. AWS' move to KVM mentioned above), because most clients only need Linux VMs, and as you can imagine, being all in a single project, KVM is simpler and potentially more efficient than Xen. So now the host Linux kernel acts as the hypervisor in those cases.
请注意,也许是由于事后观察的好处,ARM相对于x86具有更好的特权级别命名约定,而不需要负数级别:0为最低级别,3为最高级别.较高的级别往往比较低的级别创建频率更高.
Note how ARM, maybe due to the benefit of hindsight, has a better naming convention for the privilege levels than x86, without the need for negative levels: 0 being the lower and 3 highest. Higher levels tend to be created more often than lower ones.
The current EL can be queried with the
MRS
instruction: what is the current execution mode/exception level, etc?ARM不需要提供所有异常级别,以实现不需要该功能以节省芯片面积的实现. ARMv8异常级别"说:
ARM does not require all exception levels to be present to allow for implementations that don't need the feature to save chip area. ARMv8 "Exception levels" says:
实现可能不包括所有的异常级别.所有实现都必须包括EL0和EL1. EL2和EL3是可选的.
An implementation might not include all of the Exception levels. All implementations must include EL0 and EL1. EL2 and EL3 are optional.
例如,
QEMU默认为EL1,但是可以使用命令行选项启用EL2和EL3:代码段已在Ubuntu 18.10上进行了测试.
Code snippets tested on Ubuntu 18.10.
这篇关于在操作系统中,Ring 0和Ring 3是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!