保护模式
1、什么是保护模式?
x86 CPU的3个模式:实模式、保护模式和虚拟8086模式。
2、为什么不直接讲x64的?
x86是由Intel推出的一种复杂指令集,能够生产支持这种指令集CPU公司主要是Intel和AMD.
AMD在1999年的时候,拓展了这套指令集,称为x86-64,后改名为AMD64,Intel也兼容了这个产品,称为Intel 64.但AMD64和Intel64
几乎是一样的,所以在很多资料中统称为x64.
这套指令集是对x86的拓展,向下兼容的.
3、保护模式有什么特点?
段的机制
页的机制
4、学习保护模式有什么用?
真正理解内核是如何运作的
段寄存器
要点回顾:
保护模式的2种重要机制:段和页
段的机制非常复杂,想了解段机制要先了解段寄存器
1、什么是段寄存器?有哪些?
当我们用汇编读写某一个地址时:
mov dword ptr ds:[0x123456],eax
我们真正读写的地址是:ds.base + 0x123456
ES CS SS DS FS GS LDTR TR 共8个
2、段寄存器的结构
3、段寄存器的读写
读段寄存器
比如:MOV AX,ES 只能读16位的可见部分
读写LDTR的指令为:SLDT/LLDT
读写TR的指令为:STR/LTR
写段寄存器
比如:MOV DS,AX 写时是写96位
段寄存器属性探测
1、段寄存器成员简介
2、探测Attribute
int var = 0;
__asm
{
mov ax,ss //cs不行 cs是可读 可执行 但不可写
mov ds,ax
mov dword ptr ds:[var],eax
}
3、探测Base
int var = 1;
__asm
{
mov ax,fs
mov gs,ax
mov eax,gs:[0] //不要用DS 否则编译不过去
mov dword ptr ds:[var],eax
//mov edx,dword ptr ds:[0x7FFDF000]
}
4、探测Limit
int var = 1;
__asm
{
mov ax,fs
mov gs,ax
mov eax,gs:[0] //不要用DS 否则编译不过去
mov dword ptr ds:[var],eax
//mov edx,dword ptr ds:[0x7FFDF000]
}
课后练习:
写段寄存器时,只给了16位,剩下的80位填什么?数据从哪里来?
段描述符与段选择子
要点回顾:
上一节课我们讲过,当写一个段寄存器的时候,只给了一个16位的数,但段寄存器有96位,那剩下的80位从哪里来的?这个16位的数是随便写的吗?
1、GDT(全局描述符表) LDT(局部描述符表)
当我们执行类似MOV DS,AX指令时,CPU会查表,根据AX的值来决定
查找GDT还是LDT,查找表的什么位置,查出多少数据.
2、段描述符
3、段选择子
段选择子是一个16位的段描述符,该描述符指向了定义该段的段描述符.
RPL:请求特权级别。TI:TI=0 查GDT表,TI=1 查LDT表。Index:处理器将索引值乘以8(每个描述符占8字节),再加上GDT或者LDT的基地址,就是要加载的段描述符
4、加载段描述符至段寄存器
除了MOV指令,我们还可以使用LES、LSS、LDS、LFS、LGS指令修改寄存器.
CS不能通过上述的指令进行修改,CS为代码段,CS的改变会导致EIP的改变,要改CS,必须要保证CS与EIP一起改,后面会讲.
char buffer[6];
__asm
{
les ecx,fword ptr ds:[buffer] //高2个字节给es,低四个字节给ecx
}
注意:RPL<=DPL(在数值上)
课后练习:
1、记住段描述符与段选择子的结构
2、使用LES、LDS等指令修改段寄存器
思考题:
段描述符共有64位,但需要填充的是80位,怎么填?
段描述符属性:P位 G位
要点回顾:
段寄存器的值是通过段描述符填充的,但段描述符只有64位,如何从64位变成80位呢?
1、段描述符
2、P位
P = 1 段描述符有效,P = 0 段描述符无效
3、段描述符与段寄存器的对应关系
WORD Selector; //16位
WORD Atrribute; //16位
DWORD Base; //32位
DWORD Limit; //32位 FFFFFFFF
课后练习:
分析段选择子为0x1B、0x23对应的段描述符,并将内容填写到段寄存器结构体中.
注意:
FS对应的段描述符比较特殊,查分后的值与段寄存器中的值不符合,讲到操作系统(线程)的时候会解释.
段描述符属性:S位 TYPE域
1、段描述符
2、S位
S = 1 代码段或者数据段描述符,S = 0 系统段描述符
3、TYPE域
4、分析举例
5、数据段说明
A 访问位,表示该位最后一次被操作系统清零后,该段是否被访问过.每当处理器将该段选择符置入某个段寄存器时,就将该位置1.
W 是否可写
E 扩展方向
6、代码段说明
A 访问位
R 可读位
C 一致位,C = 1 一致代码段 ,C = 0 非一致代码段
7、系统段描述符
当S=0时,该段描述符为系统描述符.系统描述符有分为以下类型:
课后练习:
1、找出所有数据段,并分析该段属性:只读、已访问、可读可写、拓展方向
2、找出所有代码段,并分析该段属性:只执行、可读可执行、已访问、一致代码
3、找出所有系统段,并分析属性.
段描述符属性:DB位
情况一:对CS段的影响
D = 1 采用32位寻址方式
D = 0 采用16位寻址方式
汇编指令加前缀0x67改变寻址方式
情况二:对SS段的影响
D = 1 隐式堆栈访问指令(如:PUSH POP CALL) 使用32位堆栈指针寄存器ESP
D = 0 隐式堆栈访问指令(如:PUSH POP CALL) 使用16位堆栈指针寄存器SP
情况三:向下拓展的数据段
D = 1 段上线为4GB
D = 0 段上线为64KB
段权限检查
1、CPU分级
2、如何查看程序处于几环?
CPL(Current Privilege Level) :当前特权级。
CS和SS中存储的段选择子后2位.
3、DPL(Descriptor Privilege Level) 描述符特权级别
DPL存储在段描述符中,规定了访问该段所需要的特权级别是什么.
通俗的理解:如果你想访问我,那么你应该具备什么特权.
举例说明:
mov DS,AX
如果AX指向的段DPL = 0 但当前程序的CPL = 3 这行指令是不会成功的!
4、RPL(Request Privilege Level) 请求特权级别
RPL是针对段选择子而言的,每个段的选择子都有自己的RPL。
举例说明:
Mov ax,0008
Mov ds,ax
与
Mov ax,000B
Mov ds,ax
指向的是同一个段描述符,但RPL是不一样的.
5、数据段的权限检查
参考如下代码:
比如当前程序处于0环,也就是说CPL=0
Mov ax,000B //B是1011,RPL = 3
Mov ds,ax //ax指向的段描述符的DPL = 0
数据段的权限检查:CPL <= DPL 并且 RPL <= DPL (数值上的比较)
注意:代码段和系统段描述符中的检查方式并不一样,具体参见后面.
6、总结:
CPL:CPU当前的权限级别
DPL: 如果你想访问我,你应该具备什么样的权限
RPL: 用什么权限去访问一个段
为啥要有RPL?
我们本可以用“读 写”的权限去打开一个文件,但为了避免出错,有些时候我们使用“只读”的权限去打开。
代码跨段(本质就是修改CS段寄存器)
要点回顾
段寄存器:ES,CS,SS,DS,FS,GS,LDTR,TR
段寄存器读写:除CS外,其他的段寄存器都可以通过MOV,LES,LSS,LDS,LFS,LGS指令进行修改
CS为什么不可以直接修改:CS的改变意味着EIP的改变,改变CS的同时必须修改EIP,所以我们无法使用上面的指令来进行修改.
1、代码间的跳转(段间跳转 非调用门之类的)
段间跳转,有2种情况,即要跳转的段是一致代码段还是非一致代码段(如何区分参见之前视频)
同时修改CS与EIP的指令有JMP FAR / CALL FAR / RETF / INT /IRETED
注意:只改变EIP的指令有JMP / CALL / JCC / RET
2、代码间的跳转(段间跳转 非调用门之类的) 执行流程
分析JMP 0x20:0x004183D7 CPU如何执行这行代码?
(1) 段选择子拆分
0x20 对应二进制形式 0000 0000 0010 0000
RPL = 00 ,TI = 0,Index = 4
(2) 查表得到段描述符
TI = 0 所以查GDT表,Index = 4 找到对应的段描述符
四种情况可以跳转:代码段、调用门、TSS任务段、任务门
(3) 权限检查
如果是非一致代码段,要求:CPL == DPL 并且 RPL <= DPL
如果是一致代码段,要求:CPL >= DPL
(4) 加载段描述符
通过上面的权限检查后,CPU会将段描述符加载到CS段寄存器中.
(5) 代码执行
CPU将 CS.Base + Offset 的值写入EIP 然后执行CS:EIP处的代码,段间跳转结束.
3、总结:
对于一致代码段:也就是共享的段
特权级高的程序不允许访问特权级低的数据:核心态不允许访问用户态的数据
特权级低的程序可以访问到特权级高的数据,但特权级不会改变:用户态还是用户态
对于普通代码段:也就是非一致代码段
只允许同级访问
绝对禁止不同级别的访问:核心态不是用户态,用户态也不是核心态.
直接对代码段进行JMP 或者 CALL的操作,无论目标是一致代码段还是非一致代码段,CPL都不会发生改变.如果要提升CPL的权限,只能通过调用门.