进入32位保护模式
指令前缀的添加
在前面的章节中,我们介绍了指令前缀 0x66 表示反转默认的操作数大小
[bits 16]
mov ds, ax ; 8E D8
[bits 32]
mov ds, ax ; 66 8E D8
所以在上述这段代码中,32位下的代码生成的机器指令会带有 0x66 的前缀
这里有一个问题,那就是 对于有前缀的指令,处理器在执行时会多花一个额外的时钟周期,一旦这样的指令用得频繁,会导致处理器执行速度下降
当然,我们在32位下使用eax而不是ax就不会添加指令前缀了
不过,在NASM编译器中,不管处理器模式和指令形式如何变化,编译的结果都一样哦
[bits 16]
mov ds, ax ; 8E D8
mov ds, eax ; 8E D8
[bits 32]
mov ds, ax ; 8E D8
mov ds, eax ; 8E D8
有同学可能会有疑问,eax是32位,ds是16位,为什么能将eax的内容传送到ds中呢
我是这样想的,在16位模式下,我们基本不会考虑eax的使用吧,然后如果使用的话,应该是只有低16位有效;在32位模式下,ds中存储的是16位的选择子,所以你使用传送指令传送的是选择子才对吧,不然ds也没法存其他东西了
创建GDT并安装描述符
上一章gdt的大小和线性基地址我们是这么写的
gdt_size dw 0
gdt_base dd 0x00007e00
这次我们换成以下这种方法
pgdt dw 0
dd 0x00007e00
两种方法都一个意思
修改段寄存器的保护
处理器在变更段寄存器以及隐藏的描述符高速缓存器的内容时,要检查其代入值的合法性
- 确认选择子正确
检查索引号,即描述符要在GDT的范围内,索引号x8+7 <= 边界
- 确认描述符正确
简单来说就是检查描述符的类别,类别与段寄存器间要适配
另外,需要注意的是,ds、es、fs和gs的选择器允许加载数值为0的选择子,cs和ss不允许
地址变换时的保护
段界限的计算:
- G=0时,即粒度为字节时,实际使用的段界限就是 描述符中记载的段界限
- G=1时,即粒度为4KB时,实际使用的段界限是 描述符中的段界限值x0x1000+0xFFF(0x1000就是4KB)
代码段执行时的保护
当描述符被加载到段寄存器的描述符高速缓存器时,则处理器取指令和执行指令时,将不再访问描述符表,而是直接从高速缓存器中取得的基地址在同 EIP 中的内容相加而获得的物理地址中获得指令
代码段是向上(高地址方向)扩展的,所以 实际使用的段界限就是段内最后一个允许访问的偏移地址
指令的执行必须满足 0<=(EIP+指令长度-1)<=实际使用的段界限
栈操作时的保护
栈段是向下(低地址方向)扩展的,每当往栈中压入数据时,ESP的内容要减去操作数的长度,所以栈段 实际使用的段界限就是段内不允许访问的最低端偏移地址,最高端无限制
所以进行栈操作时,必须满足 实际使用的段界限+1<=(ESP-操作数的长度)<=0xFFFFFFFF
数据访问时的保护
数据段可以向上扩展或向下扩展
当处理器访问向上扩展的数据段时,必须满足 0<=(EA+操作数打下-1)<=实际使用的段界限 (EA表示有效地址,即偏移地址)
对向上扩展的数据段的检查和代码段的检查基本一致,不同的点在于,代码段取决于指令的长度,数据段取决于操作数的尺寸
使用别名访问代码段
当两个以上的描述符都描述和指向同一个段时,把另外的描述符称为别名
当我们想要访问代码段内的数据时,可以重新为该段安装一个新的描述符,并将其定义为可读可写的数据段,这样就可以通过这个数据段来访问了
别名的应用不仅仅于此,它还可以用在多个程序共享同一个内存区域等等
xchg指令
xchg指令用于交换两个操作数的内容,两个操作数可以是寄存器或者内存单元(也可以同时都是),但不允许同时为内存地址
标签:32,存储器,mov,代码段,描述符,指令,chapter12,------,ds From: https://www.cnblogs.com/winter-z/p/18361164