保护模式之页相关的笔记

版权声明:欢迎转载,注明出处 https://blog.csdn.net/youyou519/article/details/82419163

保护模式之页相关的笔记

分页

物理地址

如下指令:
MOV eax,dword ptr ds:[0x12345678]
其中,0x12345678 是有效地址
ds.Base + 0x12345678 是线性地址

物理地址在哪里?

CR3寄存器是所有寄存器里面,唯一一个存储物理地址的寄存器,其他都是线性地址。
每个进程都有一个CR3,(准确的说是都一个CR3的值,CR3本身是个寄存器,一个核,只有一套寄存器),CR3指向一个物理页,一共4096字节(4KB),如图:

10-10-12分页

31到22 21到12 11到0
10 10 12
目录 目录 当前这个物理页的哪个位置

设置分页方式
boot里的noexecute 改成 execute

PDE与PTE

分页机制

分析为什么是10-10-12

因为每个单元4字节,后3位是属性,倒着来看,比如12的原因,2的12次方是4K,所以12才能寻找所有的物理页,第二个10,每个PTE成员4字节,整个表4kB,所以有2的10次方个单元,所以有10位,第一个10位也是相同原因。

PTE特征

  • PTE既可以指向物理页,也可以没有物理页。
  • 多个PTE可以指向同一个物理页。
  • 一个PTE只能指向一个物理页。

物理页的属性

物理页的属性 = PDE属性 & PTE属性
物理页属性

P位

线性地址0 为什么不能访问呢?
没有指定物理页,指定物理页就一定能访问吗?
先看PDE与PTE的P位 P=1 才是有效的物理页

R/W 位

R/W = 0 只读
R/W = 1 可读可写

U/S 位

U/S = 0 特权用户
U/S = 1 普通用户

P/S位(PageSize)

对PDE有意义,PS == PageSize的意思 当PS==1的时候 PDE直接指向物理页无PTE,低22位全是页内偏移。因为后面的偏移,线性地址只能拆成2段:大小为4MB,俗称“大页”

A 位

是否被访问(读或者写)过访问过置1,即使只访问一个字节也会导致PDE PTE置1

D 位

脏位,是否被写过,0没有被写过,1被写过

在上一节课课后题中我们提到过,如果系统要保证某个线性地址是有效的,那么必须为其填充正确的PDE与PTE,如果我们想填充PDE与PTE那么必须能够访问PDT与PTT,那么存在2个问题:
1、一定已经有“人”为我们访问PDT与PTT挂好了PDE与PTE,我们只有找到这个线性地址就可以了。
2、这个为我们挂好PDE与PTE的“人”是谁?

拆分C0300000
结论:C0300000存储的值就是PDT
如果我们要访问第N个PDE,那么有如下公式:
0xC0300000 + N*4
段页

PDT总结:
1、通过0xC0300000找到的物理页就是页目录表

2、这个物理页即是页目录表本身也是页表

3、页目录表是一张特殊的页表,每一项PTE指向的不是普通的物理页,而是指向其他的页表.

4、如果我们要访问第N个PDE,那么有如下公式:
0xC0300000 + N*4

PTT总结:
1、页表被映射到了从0xC0000000到0xC03FFFFF的4M地址空间(一个页表是4KB,一共1024)
2、在这1024个表中有一张特殊的表:页目录表
3、页目录被映射到了0xC0300000开始处的4K地址空间

有了0xC0300000和0xC0000000能做什么?

掌握了这两个地址,就掌握了一个进程所有的物理内存读写权限。

公式总结:
1、什么是PDI与PTI
10-10-12

2、访问页目录表的公式:
0xC0300000 + PDI*4
3、访问页表的公式
0xC0000000 + PDI*4096 + PTI*4

首先要明白一点,PAE和非PAE指的是物理寻址扩不扩大,也就是PTE的位数扩不扩大,原来的内存大小是4G,因为首先PTE是寻物理地址的,记录了寻址时物理地址的起始地址。PTE的大小是4字节32位寻址范围是2的32次方,寻址范围就是4G,因为线性地址是32位,所以是10-10-12(10是因为4k大小能存2的10次方个,12是页内偏移,因为不管PAE还是不PAE页大小都是4K),PAE技术就是把PTE变为8个字节(64位),所以寻址理论上就能变成2的64次方(但是这个太大,一般用不了那么多)。所以是2-9-9-12。

2-9-9-12分页

10-10-12分页方式,在这种分页方式下物理地址最多可达4GB。但随着硬件发展,4GB的物理地址范围已经无法满足要求,Intel在1996年就已经意识到这个问题了,所以设计了新的分页方式.也就是我们本节课要讲的2-9-9-12分页,又称为PAE(物理地址扩展)分页.

为什么是10-10-12

  • 先确定了页的大小4K,所以后面的12位的功能就确定了,(一个页4K是4096个字节,所以想让每个页内偏移的字节都能寻址到,就需要4096个地址,即2的12次方个,即12位)
  • 当初的物理内存比较小,所以4个字节的PTE就够了,加上页的尺寸是4K,所以一个页能存储1024个PTE, 也就是2的10次方,第二个10也就确定了.
  • 剩下的10为PDI 10+10+12刚好32位

为什么是2-9-9-12

  • 页的大小是确定的,4KB不能随便改,所以12确定了。
  • 如果想增大物理内存的访问范围,就需要增大PTE,增大多少了呢?考虑对齐的因素,增加到8个字节,所以4K/8=512,一个页PTE就是512个,如下图。
  • 同理PDI也是2的9次方 32 - 9 - 9 - 12 还差2位 所以就再做一级,叫PDPI
    PAE技术

2-9-9-12分页结构(PAE,物理地址扩展)

CR3此时指向PDPTE
2-9-9-12分页

如何开启PAE模式

将C:\boot.ini文件中的execute改为noexecute 重启

Page-Directory-Point-Table Entry

Page-Directory-Point-Table Entry

  • PDPTE共有四项(第一个2)。
  • 35-12 存储的是页目录表的基址,低12位补0,共36位,即页目录基址。
    PAE的PDE结构

特别说明:
- 当PS=1时(第7位)是大页,35-21位是大页的物理地址,这样36位的物理地址的低21位为0,这就意味着页的大小为2MB,且都是2MB对齐。
- 当PS=0时,35-12位是页表基址,低12位补0,共36位。

特别说明

  • PTE中35-12是物理页基址,24位,低12位补0
  • 物理页基址+12位的页内偏移指向具体数据

XD标志位(AMD中称为NX,即No Excetion )

PDE/PTE结构

X 保留 35-12 物理地址 低12位(属性)

段的属性有可读、可写和可执行
页的属性有可读、可写
当RET执行返回的时候,如果我修改堆栈里面的数据指向一个我提前准备好的数据(把数据当作代码来执行,漏洞都是依赖这点,比如SQL注入也是)
所以,Intel就做了硬件保护,做了一个不可执行位,XD=1时。那么你的软件溢出了也没有关系,即使你的EIP蹦到了危险的“数据区”,也是不可以执行的!
在PAE分页模式下,PDE与PTE的最高位为XD/NX位.

TLB

地址解析

  1. 通过一个线性地址访问一个物理页。比如:一个DWORD,其实未必真正读的是4个字节,我们先读的PDE再读PTE 最后才读的4个字节的页。
  2. 在2-9-9-12会读24个字节 如果跨页可能更多。

为了提高效率,只能做记录。
CPU内部做了一个表,来记录这些东西,这个表格是CPU内部的,和寄存器一样快,这个表格:TLB(Translation Lookaside Buffer)。

TLB结构

LA(线性地址) PA(物理地址) ATTR(属性) LRU(统计)

说明:
1. ATTR(属性):属性是PDPE,PDE,PTE三个属性AND起来的. 如果是10-10-12,就是PDE and PTE
2. 不同的CPU 这个表的大小不一样.
3. 只要Cr3变了,TLB立马刷新,一核一套TLB.

操作系统的高2G映射基本不变,如果Cr3改了,TLB刷新 重建高2G以上很浪费。所以PDE和PTE中有个G标志位(PDE是大页时有效),如果G位为1刷新TLB时将不会刷新PDE/PTE的G位为1的页,当TLB满了,根据统计信息将不常用的地址废弃,最近最常用的保留.

TLB种类

TLB在X86体系的CPU里的实际应用最早是从Intel的486CPU开始的,在X86体系的CPU里边,一般都设有如下4组TLB:
第一组:缓存一般页表(4K字节页面)的指令页表缓存(Instruction-TLB);
第二组:缓存一般页表(4K字节页面)的数据页表缓存(Data-TLB);
第三组:缓存大尺寸页表(2M/4M字节页面)的指令页表缓存(Instruction-TLB);
第四组:缓存大尺寸页表(2M/4M字节页面)的数据页表缓存(Data-TLB)

中断与异常

什么是中断

  1. 中断通常是由CPU外部的输入输出设备(硬件)所触发的,供外部设备通知CPU“有事情需要处理”,因此又叫中断请求(Interrupt Request).
  2. 中断请求的目的是希望CPU暂时停止执行当前正在执行的程序,转去执行中断请求所对应的中断处理例程(中断处理程序在哪有IDT表决定)
  3. 80x86有两条中断请求线:
    非屏蔽中断线,称为NMI(NonMaskable Interrupt)
    可屏蔽中断线,称为INTR(Interrupt Require

非可屏蔽中断如何处理

(固定IDT第二个门)

(IDT表)中断号 NMI 说明
0x2 不可屏蔽中断 80x86中固定为0x2

特别说明:
当非可屏蔽中断产生时,CPU在执行完当前指令后会里面进入中断处理程序
非可屏蔽中断不受EFLAG寄存器中IF位的影响,一旦发生,CPU必须处理
非可屏蔽中断处理程序位于IDT表中的2号位置

可屏蔽中断

在硬件级,可屏蔽中断是由一块专门的芯片来管理的,通常称为中断控制器.它负责分配中断资源和管理各个中断源发出的中断请求.为了便于标识各个中断请求,中断管理器通常用IRQ(Interrupt Request)后面加上数字来表示不同的中断.

比如:在Windows中时钟中断的IRQ编号为0,也就是:IRQ0

中断的实质是改变程序执行流程

可屏蔽中断如何处理?

(IDT表)中断号 IRQ 说明
0x30 IRQ0 时钟中断
0x31-0x3F IRQ1-IRQ15 其他硬件设备的中断

特别说明:
1. 如果自己的程序执行时不希望CPU去处理这些中断,可以
用CLI指令清空EFLAG寄存器中的IF位
用STI指令设置EFLAG寄存器中的IF位
2. 硬件中断与IDT表中的对应关系并非固定不变的,
参见:APIC(高级可编程中断控制器)

异常

异常通常是CPU在执行指令时检测到的某些错误,比如除0、访问无效页面等。

中断与异常的区别:

  1. 中断来自于外部设备,是中断源(比如键盘)发起的,CPU是被动的.
  2. 异常来自于CPU本身,是CPU主动产生的.
  3. INT N虽然被称为“软件中断”,但其本质是异常。EFLAG的IF位对INT N无效。

异常处理

无论是由硬件设备触发的中断请求还是由CPU产生的异常,处理程序都在IDT表。

错误类型 (IDT表)中断号
页错误 0xE
段错误 0xD
除0错误 0x0
双重 0x8

缺页异常

缺页异常的产生:
1、当PDE/PTE的P=0时
2、当PDE/PTE的属性为只读但程序试图写入的时
一旦发生缺页异常,CPU会执行IDT表中的0xE号中断处理程序,由操作系统来接管。

控制寄存器

控制寄存器用于控制和确定CPU的操作模式。
Cr0 Cr1 Cr2 Cr3 Cr4
Cr1 保留
Cr3 页目录表基址

Cr0寄存器

CR0寄存器

说明:
PE:CR0的位0是启用保护(Protection Enable)标志。
PE=1保护模式,PE=0实地址模式,这个标志仅开启段级保护,而并没有启用分页机制。若要启用分页机制,那么PE和PG标志都要置位。
PG:当设置该位时即开启了分页机制。在开启这个标志之前必须已经或者同时开启PE标志。
PG=0且PE=0,处理器工作在实地址模式下
PG=0且PE=1,处理器工作在没有开启分页机制的保护模式下
PG=1且PE=0,在PE没有开启的情况下,无法开启PG
PG=1且PE=1,处理器工作在开启了分页机制的保护模式下

WP:对于Intel 80486或以上的CPU,CR0的位16是写保护(Write Proctect)标志当设置该标志时,处理器会禁止超级用户程序(例如特权级0的程序)向用户级只读页
面执行写操作;
当CPL<3的时候:
如果 WP=0 可以读写任意用户级物理页,只要线性地址有效.
如果 WP=1 可以读取任意用户级物理页,但对于只读的物理页,则不能写.

Cr2寄存器

当CPU访问某个无效页面时,会产生缺页异常,此时,CPU会将引起异常的线性地址存放在CR2中。

Cr4寄存器

Cr4寄存器

PWT/PCD

CPU缓存

1) CPU缓存是位于CPU与物理内存之间的临时存储器,它的容量比内存小的多但是交换速度却比内存要快得多。
2) CPU缓存可以做的很大,有几K、几十K、几百K甚至上M的也有。
CPU缓存与TLB的区别:
TLB:
线性地址 <—–> 物理地址
CPU缓存:
物理地址 <—–> 内容

关于PWT/PCD属性

PWT:Page Write Through

PWT =  1 时 写Cache的时候也要将数据写入内存中。

PCD:Page Cache Disable

PCD = 1时,禁止某个页写入缓存,直接写内存。

比如,做页表用的页,已经存储在TLB中了,可能不需要再缓存了。

参考资料:

[1]:滴水视频

猜你喜欢

转载自blog.csdn.net/youyou519/article/details/82419163