-
-
[原创]IA-32e模式学习笔记
-
发表于: 2022-1-8 12:36 12539
-
支持Intel 64的64位的工作模式,曾经称为64位内存扩展技术,是IA-32 CPU支持64位的一种扩展技术,具有对现有32位程序的良好兼容性。IA-32e模式由两个子模式组成:64位模式和兼容模式。64位模式提供了64位的线性寻址模式,并且能够访问超过64GB的物理内存(32位下启用PAE功能后最多访问64GB的物理内存)。兼容模式用于执行现有32位应用程序,使它们不做任何改动就可以运行在64位操作系统上。对于运行在IA-32e模式下的64位操作系统,系统内核和内核态的驱动程序一定是64位的代码,工作在64位模式下,应用程序可以是32位的(在兼容模式下运行),也可以是64位的(在64位模式下运行)。
在支持IA-32e模式的系统上,提供扩展功能启用寄存器(IA32_EFER),该寄存器控制IA-32e模式的激活和其他IA-32e模式的操作。下图是该寄存器的布局:
EFER是MSR中的一员,它的地址是0xC0000080,它是x64提体系的基石。当EFER.LME=1时,表示要开起IA-32e模式。可是,此时并不代表已经进入了IA-32e模式,因为开启IA-32e模式后必须要开启分页内存管理机制,也就是说,当EFER.LME=1且CR0.PG=1时,处理器会将EFER.LMA置为1,当其成功置为1以后,才表示IA-32e模式处于激活状态。
处理器在上电开始运行时或复位后是处于实地址模式,CR0控制寄存器的PE标志用来控制处理器处于实地址模式还是保护模式。标志寄存器(EFLAGS)的VM标志用来控制处理器是在虚拟8086模式还是普通保护模式下,EFER寄存器的LME用来启用IA-32e模式
在Intel64架构的IA-32e模式下,分段的效果取决于处理器是以兼容模式还是64位模式运行。在兼容模式下,分段功能就像使用遗留的16位或32位保护模式语义。
在64位模式下,分段通常(但不是完全)被禁用,从而创建一个平坦的64位线性地址空间。处理器将CS、DS、ES、SS的段基址视为零,创建一个等于有效地址的线性地址。FS段和GS段都是例外。这些段寄存器(保持段基)可用作线性地址计算中的附加基寄存器。它们有助于处理本地数据和某些操作系统数据结构。
处理器在64位模式下运行时不执行段限制检查
在IA-32e模式下,Intel64处理器使用上述步骤将逻辑地址转换为线性地址。在64位模式下,段的偏移量和基地址是64位,而不是32位。线性地址格式也是64位宽,并受规范形式的要求。
在IA-32模式下,段寄存器的base地址部分被扩展到64为,limit固定为32位,Attribute和Selector依然是16位宽,在兼容模式下依然使用base的低32位值。因此,和在保护模式下不同的是,此时的不可见部分扩展到了112位,段寄存器整体被扩展到了128位。
因为ES、DS和SS段寄存器不在64位模式中使用,所以它们在段描述符寄存器中的字段(基本、限制和属性)将被忽略。某些形式的段加载指令也无效(例如,LDS、POP ES)。引用ES、DS或SS段的地址计算的时候,段基址被视为为零。
处理器检查所有线性地址引用是否都是规范形式,而不是执行限制检查。模式切换不会改变段寄存器或相关的描述符寄存器的内容。这些寄存器在64位模式执行期间也不会更改,除非执行显式段加载。
为了设置应用程序的兼容性模式,段加载指令(MOV Sreg,POP Sreg)在64位模式下正常工作。从系统描述符表(GDT或LDT)中读取一个条目,并被加载到线段寄存器的隐藏部分中。描述符寄存器基础、限制和属性字段都被加载。但是,数据和堆栈段选择器和描述符寄存器的内容将被忽略。
当在64位模式下使用FS和GS段覆盖时,它们各自的基本地址被用于线性地址计算:(FS或GS).base+索引+位移。FS.base和GS.base然后扩展到实现所支持的完全线性地址大小。所得到的有效地址计算可以跨越正地址和负地址;所得到的线性地址必须是规范的。
在64位模式下,使用fs段和gs段覆盖的内存访问既不检查运行时限制,也不进行属性检查。正常的段加载(MOV Sreg和POP Sreg)到FS和GS中,在段寄存器的隐藏部分加载一个标准的32位基值。在标准32位以上的基地址位被清除为0,以允许使用小于64位的实现的一致性。
FS.base和GS.base的隐藏描述符寄存器字段被物理地映射到msr,以便加载由64位实现支持的所有地址位。使用CPL=0(特权软件)的软件可以使用WRMSR将所有支持的线性地址位加载到FS.base或GS.base中。写入64位FS.base和GS.base寄存器的地址必须是规范形式。试图将这些寄存器写入非规范地址的WRMSR指令会导致#GP错误。
当在兼容模式下,FS和GS覆盖按照32位模式行为定义的方式操作,而不管加载到隐藏描述符寄存器基字段的上32线性地址位的值。兼容性模式在计算有效地址时忽略了上32位。
一个新的64位模式指令,SWAPGS,可用于加载GS base。SWAPGS会将GS寄存器中的内容和IA32_KERNEL_GS_BASE MSR的保存的数据进行交换。然后,内核可以在普通内存引用上使用GS前缀来访问内核数据结构。如果通过WRMSR将非规范值写入IA32_KERNEL_GS_BASE MSR会导致#GP故障。
在IA-32e模式下,段描述符表最多可以包含8192个8字节的描述符。段描述符表中的一个条目可以为8个字节。系统描述符扩展为16字节(占用两个条目的空间)。
GDTR和LDTR寄存器的基地址被扩展为64位,因此,此时的相应寄存器被扩展到下图的80位
在IA-32e模式下,非系统段的段描述符依然是8字节64位宽。但是,系统段的段描述符已经扩展到了16字节128位宽。
64位模式下的数据段描述符格式和保护模式下的数据段描述符格式是一样的,只是绝大部分域已经无效了。
对于ES, SS, DS段来说Base域是无效,被忽略的。然而对于FS和GS段是有效的,可是从数据段描述符里只能加载到FS和GS段的低32位。
FS段和GS段完整的64位Base地址需要在相应的MSR里设置,FS的基地址寄存器是IA32_FS_BASE,地址在0xC0000100。GS的基地址寄存器是IA32_GS_BASE,地址在0xC0000101上,这些值必须在ring 0权限下使用wrmsr指令进行设置。
Data段描述符S标志必须设为1,Code/Data标志位必须设置位0,否则会产生#GP异常。W标志位对于DS, ES, FS和GS寄存器没有映像,被忽略。当被加载到SS寄存器时,处理器会检查W标志位是否位1(可写的),否则会产生#GP异常。
在IA-32e模式下,L标志位用来指示该代码段是在64位模式下的代码段还是兼容模式下的代码段。如果是兼容模式的代码段则格式作用和保护模式下的代码段一致,如果是64位模式下的代码段,此时代码段的格式如下:
在64位模式下,由于段的base和段的limit都无效被忽略,它强制所有段的base为0,limit为0xFFFFFFFF,只有FS和GS寄存器可以使用非0值的段base。
在64位模式下,L位为1,D位必须为0,此时指明代码段对应的目标代码是64位代码。如果此时D位为1,就会是一个无效的组合。
IA-32e模式下的系统段描述符和保护模式下差异较大。首先在IA-32e模式下,系统段描述符是用16字节来表示的,且Type域的很多位都被保留起来。
如下图所示,IA-32e模式下调用门被扩展到了16字节。
由于段描述符依然是8字节,所以调用门实际上是在段描述符表中占用两个段描述符的空间。为了避免高8字节的段描述符被作为非系统段描述符解析,其S域和Type域都要置为0。
目标代码的offset值扩展到了64位,该值必须符合64位系统的地址值,否则会产生#GP异常。
其中的Segment Selector所指向的段描述符必须是64位的代码段(L=1且D=0)。
在IA-32e模式下并不支持TSS任务切换机制,因此任务门在次模式下是不存在的。但是TSS段描述符和TSS段都有比较大的变化。下图是TSS段在次模式下的格式:
在次模式下的TSS段只保留了3个权限级别的RSP指针,增加了7个IST指针,所有的域都是64位宽。由于次模式下不支持处理器提供的任务切换机制,因此Previous task link域已经不再存在,所有处理器的状态域也都被移去。TSS段的主要作用是为栈的切换提供各级权限的栈指针。当发送栈切换的时候,处理器从TSS段里获得相应权限级别的RSP值,加载到RSP寄存器中。
下图是IA-32e模式下TSS段描述符,该描述符也被扩展到了16字节:
其中高8字节的S域和Type域都为0,理由和调用门一样。
如下图所示,IA-32e模式下的中断门和陷阱门被扩展到了16字节:
此时的偏移和调用门一样被扩展到了64字节。
中断门和陷阱门描述符新增了一个IST域,共3比特宽,在低8字节的高32位中的0-2位。该域的值大小从0-7,当值为0的时候,表示不适用IST机制,发生中断或异常切换到中断处理程序的时候,将从64位TSS段里相应的RSP0, RSP1, RSP2域获取RSP寄存器的值。如果是1-7的某个值,则分别对应TSS段中的IST1-IST7,发生中断或异常切换到中断处理程序的时候,将会从64位TSS段中取出对应的IST值,该值将会赋值给RSP寄存器。
下图是IA-32e模式下的页转换路径图:
从图中可以看出,在次模式下使用了4个页表结构来实现转换。
PML4:这是IA-32e模式新增的页转换表,每个表4K字节,包含512个PML4E结构
PDPT:每个表4K字节,包含512个PDPTE结构
PDT:每个表4K字节,包含512个PDE结构
PT:每个表4K字节,包含512个PTE结构
IA-32e的分页模式是在PAE分页模式的基础上新增了一级页表,虚拟地址可以使用48位宽。当当前的x64体系中,处理器64位线性地址空间只实现了48位,高16位被用作Bit 47位的符号扩展位,要么全是0,要么全是1.
每个页表项结构都是8个字节64位宽,而虚拟地址中的每个页表项索引值都是9位,因此每个页表都是512 * 8 = 4K字节。
下图是IA-32e模式下CR3以及各页表的结构:
接下来通过一个例子来验证上述内容。下面这段代码申请了一块内存,并在内存中写入了数据,接下来就通过其虚拟地址来找到物理地址。
下图是程序的输出:
申请的内存的地址是0x160000,因此可以得到索引和偏移分别是0x0, 0x0, 0x0, 0x160, 0x000。
使用WinDbg获取其CR3为0x1A3E0000
根据CR3和索引获取物理页
根据偏移获取相应的物理地址并查看其中保存的数值
比特 | 名称 | 描述 |
---|---|---|
0 | SYSCALL Enable:IA32_EFER.SCE(R/W) | 在64位模式下启动SYSCALL/SYSRET指令 |
8 | IA-32e Mode Enable:IA32_EFER.LME(R/W) | 启用IA-32e模式 |
10 | IA-32e Mode Active: IA32_EFER.LMA(R) | 标识设置时IA-32e模式处于活动状态 |
11 | Execute Disable Bit Enable: IA32_EFER.NXE(R/W) | 该位为1时,表示可以使用Execution disable功能,给某些页定义为不可执行的页面 |
赞赏
- [原创]CVE-2022-21882提权漏洞学习笔记 16402
- [原创]CVE-2021-1732提权漏洞学习笔记 19506
- [原创]CVE-2014-1767提权漏洞学习笔记 15200
- [原创]CVE-2018-8453提权漏洞学习笔记 18538
- [原创]CVE-2020-1054提权漏洞学习笔记 13550