Win32汇编--Windows 的特权保护
Windows 的特权保护和处理器硬件的支持是分不开的。
优先级的划分、指令的权限检查和超出权限访问的异常处理等是构成特权保护的基础。
这一讲我们将试图讲过讲解为大家解决两大问题:
Win32 汇编中为什么找不到中断指令的应用?
Windows 错误的“蓝屏”是从哪里来的?
小甲鱼解释中断和异常
鱼C故事会:
假设某一天你正在兴致勃勃、兴高采烈地看一部爱情动作片,但是突然妈妈在外边猛敲你的房门,因为她发觉家里的酱油没了……
这时候没办法,还是老妈的命令重要,因此,我们暂停了视频,然后去打酱油……
打完酱油回来,你又接着往下看,意兴珊阑处,停电了……
过了N久,好不容易来电了,一开机,它蓝屏了!
官方解释 -- 什么是中断
中断指当程序执行过程中有更重要的事情需要实时处理时(如串口中有数据到达,不及时处理数据会丢失,串行控制器就提交一个中断信号给处理器要求处理),硬件通过中断控制器通知处理器。
接到命令后,处理器暂时挂起当前运行的程序,转移到中断处理程序中。
当中断处理程序处理完毕后,通过iret指令回到原先被打断的程序中继续执行。
官方解释 -- 什么是异常
异常指指令执行中发生不可忽略的错误时(如遇到无效的指令编码,除法指令除零等),处理器用和中断处理相同的操作方法挂起当前运行的程序转移到异常处理程序中。
异常处理程序决定在修正错误后是否回到原来的地方继续执行。
注意:中断和异常处理的方式是相同的!!
实模式下的中断或异常处理:
实模式下的中断和异常服务程序地址存放在中断向量表中。
中断向量表位于物理内存中,每个中断向量是一个xxxx:yyyy格式的地址,占用4字节。
当发生n号异常或n号中断,或者执行到int n指令的时候,CPU首先到内存n×4的地方取出服务程序的地址aaaa:bbbb
然后将标志寄存器、中断时的CS和IP压入堆栈,接着转移到aaaa:bbbb处执行
保护模式下的中断或异常处理
保护模式下,中断或异常处理往往从用户代码切换到操作系统代码中执行。
由于保护模式下的代码有优先级之分,因此出现了从优先级低的应用程序转移到优先级高的系统代码中的问题,如果优先级低的代码能够任意调用优先级高的代码,就相当于拥有了高优先级代码的权限。
为了使高优先级的代码能够安全地被低优先级的代码调用,保护模式下增加了“门”的概念。
“门”指向某个优先级高的程序所规定的入口点,所有优先级低的程序调用优先级高的程序只能通过门重定向,进入门所规定的入口点。
这样可以避免低级别的程序代码从任意位置进入优先级高的程序的问题。
保护模式下的中断和异常等服务程序也要从“门”进入,80386的门分为中断门、自陷门和任务门几种。
保护模式下把所有的中断描述符放在一起组成"中断描述符表IDT"。为此80386处理器引入了一个新的48位寄存器IDTR。IDTR的高32位指定了IDT在内存中的基址(线性地址),低16位指定了IDT的长度,相当于指定了可以支持的中断数量。
保护模式下发生异常或中断时,处理器先根据IDTR寄存器得到中断描述符的地址,然后取出n号中断/异常的门描述符,再从描述符中得到中断服务程序的地址xxxx:yyyyyyyy,经过段地址转换后得到服务程序的32位线性地址并转移后执行。
在Windows中,操作系统使用动态链接库来代替中断服务程序提供系统功能,所以 Win32汇编中int指令也就失去了存在的意义。这就是在Win32汇编源代码中看不到int指令的原因。其实那些调用API的指令原本是用int指令实现的。
80386的保护机制
80286之前的处理器只支持单任务,操作系统并没有什么安全性可言,计算机的全部资源包括操作系统的内部资源都可以任凭程序员调用。
但对于多任务的操作系统,某个想捣乱的程序为所欲为令使所有程序都无法运行。
所以80286及以上的处理器引入了优先级的概念。80386处理器共设置4个优先级(0~3):
0级是最高级(特权级)3级是最低级(用户级)
保护机制主要由下列几方面组成
段的类型检查
段的类型是由段描述符指定的,主要属性有是否可执行,是否可读和是否可写等。
CS,DS和SS等段选择器是否能装入某种类型的段描述符是有限制的。如不可执行的段不能装入CS;不可读的段不能装入DS与ES等数据段寄存器;不可写的段不能装入SS等。
如果段类型检查通不过,则处理器会产生一般性保护异常或堆栈异常。
页的类型检查
除了可以在段级别上指定整个段是否可读写外,在页表中也可以为每个页指定是否可写。
对于特权级下的执行代码,所有的页都是可写的。
但对于1,2和3级的代码,还要根据页表中的R/W项决定是否可写,企图对只读的页进行写操作会产生页异常。
访问数据时的级别检查
优先级低的代码不能访问优先级高的数据段。80386的段描述符中有一个DPL域(描述符优先级),表示这个段可以被访问的最低优先级。
而段选择器中含有RPL域(请求优先级),表示当前执行代码的优先级。
只有DPL在数值上大于或等于RPL值的时候,该段才是可以访问的,否则会产生一般性保护异常。
控制转移的检查
在处理器中,有很多指令可以实现控制转移,如jmp,call,ret,int和iret等指令。
但优先级低的代码不能随意转移到优先级高的代码中,所以遇到这些指令的时候,处理器要检查转移的目的位置是否合法。
指令集的检查
有两类指令可以影响保护机制。
第一类是改变GDT,LDT,IDT以及控制寄存器等关键寄存器的指令,称为特权指令
第二类是操作I/O端口的指令以及cli和sti等改变中断允许的指令,称为敏感指令。
特权指令只能在优先级0上才能运行,而敏感指令取决于eflags寄存器中的IOPL位。只有IOPL位表示的优先级高于等于当前代码段的优先级时,指令才能执行。
I/O操作的保护
I/O地址也是受保护的对象。因为通过I/O操作可以绕过系统对很多硬件进行控制。
80386可以单独为I/O空间提供保护,每个任务有个TSS(任务状态段)来记录任务切换的信息。
TSS中有个I/O允许位图,用来表示对应的I/O端口是否可以操作。
Windows 的保护机制
以上是Windows规定的“保护条例”,如果某个程序违反了,那么会引发保护异常,处理器会毫不犹豫地把控制权转移到对应的异常处理程序中去。
Windows会在处理程序中用一个很酷的“非法操作”对话框把用户的程序判死刑,没有一点回旋的余地!经常时候系统会用一个“蓝屏”来通知用户程序试图访问不存在的内存页。
至此,本章节的讲解到终于到一段落。
小甲鱼此时此刻的心情出了哀怨作者外剩下的就是释然。
因为本章节可以说是全书最难懂、最让人抓狂的一章节,相信从第二章开始,我们的学习会变得格外轻松和愉快!
加油!!
鱼C教学 -- 让编程学习充满欢乐!