Windows内核学习笔记-保护模式

保护模式

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的权限,只能通过调用门.

猜你喜欢

转载自www.cnblogs.com/wf751620780/p/10462437.html