最近宝强同志不幸遭遇了一场事故,可谓是赔了夫人又折兵. 男人们看到这个 新闻,然后转头看看自己的另一半,眼神也不禁若有所思. 遭遇背叛是每个人都不愿遇到的, 那么我们作为目光短浅的凡人,该如何事先做好安全措施呢?看看操作系统实现的安全机制, 也许能给大家一点启发.
前言
我们每天使用的设备中,运行着不同的操作系统,比如windows,android,ios等.每个操作系统里 又运行着不同的程序,因此每个CPU每秒执行的指令数量有上亿条.其中不可避免地会有危险的指令, 比如任意内存访问等操作,如果操作系统或者CPU.没有相关的限制,那不免会经常崩溃. 为了解决这个问题,减少致命错误的发生几率,于是工程师们引进了一种分级的保护机制.
Protection Ring
这个保护机制,就是一种权限分级制度,学名为分级保护域(Hierarchical Protection Domains), 又叫保护环(Protection Ring).即我们经常听到的Ring0/Ring3.分级保护域是一种用来在发生故障时 保护数据和功能,提升容错度,避免恶意操作,提升计算机安全的设计方式.这是一种与 能力基础安全(capability-based security)完全相反的方式.
还是以宝强同志为例子, 想要不被伤害,不被欺骗,最好的办法是什么?就是不要相信任何人. 也许有人觉得这种观点极端了,但我不是说别人都对你图谋不轨,亲人朋友当然不太可能会对你有恶意, 但是他们有时候也只是没有辨别是非的能力, 说白了他们本身也是被害者. 如果感情用事 而去盲目相信对方, 最后损失扩大反而对对方的伤害更大.
在计算机系统中也是如此. 有时候用户根本就不知道自己在干什么, 也就是说不是谁都有 基础能力的. 因此最好的办法就是分级, 如下图所示:
ring
这是x86保护模式下的特权级别实例. 其中Ring3为用户环(用户态), Ring2-Ring1为驱动环, Ring0为内核环(内核态),Ring0有最高的权限, Ring3的权限最低. 操作系统(内核)工作在Ring0层, 可以访问所有层的数据; 而一些和硬件外设相关的程序如网卡,声卡驱动等就工作在Ring1和 Ring2层; 用户一般的应用程序则工作在Ring3层. 这样分级的好处是确保进程不会彼此之间对 系统重要组件产生影响.保护环对工作在环内的进程能够做什么,能够执行什么命令做出了严格的定义. 在内环执行的进程比在外环执行的进程有更高的权限,内核只允许最可信的组件和进程在其中执行.
外环的程序想要直接执行特权指令时,操作系统会将其阻止,并且返回诸如"非法指令"的错误信息. 当然, 在安全和方便间有时候也需要作出取舍,为了在必要的时候给用户态能够执行高级指令, 内核提供了一系列接口来提供给用户, 这些接口也被称为系统服务调用.
实现
一开始的时候, 保护环机制是用纯软件来实现的, 操作系统通过软件来捕获Rings的转换. 后来大部分的CPU都实现了保护环架构, 在指令集中提供指令或者硬件接口来进行Rings的 转换, 如果在低权限模式执行了特权指令, 会触发处理器的软中断, 从而使CPU不至于跑飞.
硬件上不同的CPU架构可能实现的保护环数量不同,但至少都会有两个. 操作系统在此基础上 一般只实现2个Ring级别(如Windows和Linux), 即Ring0和Ring3, 俗称内核态和用户态. 有的操作系统实现了3个Ring级别,如OS/2等,不过早就退出历史舞台了.
值得一提的是,有的人把内核态和用户态叫作内核空间和用户空间. 但后者其实是虚拟内存的概念, 前者是基于硬件的保护, 不可同一而论. 不过其出发点是类似的, 在Linux下, 内核将地址空间(假设为4G) 分为两部分, 高字节段(假设为1G)供内核使用,称为内核空间, 低字节段(假设为3G)供各个进程使用, 称为用户空间. 从结果上来看, 每个进程都有3G的私人空间 1G的共享空间(通过系统调用进入内核空间). 这种划分是为了内存访问保护的要求.
弊端
CPU对用户态和内核态的分离对我们操作系统的使用者来说是相对透明的, 也带来了不可估量的好处, 如果没有保护环, 那我们平时遇到程序崩溃的时候, 就不是Ctrl Alt Del结束进程或者kill -9 pid那么 简单了, 而是遭遇系统完全死机或者行为持续性异常. 但是,凡事各有利弊, 用户态虽然安全,但也是 需要代价的. 要记住, 用户态和内核态的切换开销非常昂贵. 这也是为什么软件抛出异常如此 之慢,因为异常意味着内核态的跃迁. 虽然说现在CPU的性能已经非常好,也不需要对那么点性能损失 较真,但在某些特殊场景下,模式的切换往往造成了性能的瓶颈.
虚拟化与Ring-1
Ring0-Ring3关于权限分级, 而Ring-1则主要关于虚拟化. 在X86仿真刚被提出来的时候,存在着一个 重大的障碍, 即宿主操作系统(host)和虚拟的操作系统(guest)都想要Ring0的访问权限,但这是不被 允许的. 于是为了解决这个问题,人们提出了几种方案. 其中一个方案是guest操作系统可以向虚拟机 管理器请求这些需要ring0权限的操作;另一种调用仿真的方案是由虚拟机管理器来虚拟一套指定的 硬件和ring0指令,然后捕捉guest操作系统的调用行为再代替其进行真正的ring0操作.
这些技术在调用操作方面付出了额外的开销,对性能也造成不小的影响. 为了减轻这种情况,人们提出 改进传统的保护环结构的想法,从而增加一个比Ring0更高权限的层级,称为Ring-1. 在Ring-1模式下, 可以有能力对指令进行仿真, 从而每个虚拟操作系统都能运行在Ring0之下,并且不影响其他的虚拟 系统(有意或无意的). 要支持这种需求则需要CPU在硬件结构上作出相应的改变. Ring-1模式同样也 包含了一些新的处理器指令.
后记
操作系统的分级保护, 对用户来说是一个非常有效的安全保障, 也说明了"能力越大,责任越大"的道理, 每个人都要管理好自己的安全边界, 在遭遇到不测时可以把损失降低到最小. 只可惜在日常中见过太多 人把权限当作儿戏,放任恶意软件进驻内核,不过那都是后话了.