进入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 <= 边界
    1
  • 确认描述符正确
    简单来说就是检查描述符的类别,类别与段寄存器间要适配
    2

另外,需要注意的是,ds、es、fs和gs的选择器允许加载数值为0的选择子,cs和ss不允许

地址变换时的保护

段界限的计算:

  • G=0时,即粒度为字节时,实际使用的段界限就是 描述符中记载的段界限
  • G=1时,即粒度为4KB时,实际使用的段界限是 描述符中的段界限值x0x1000+0xFFF(0x1000就是4KB)

代码段执行时的保护

当描述符被加载到段寄存器的描述符高速缓存器时,则处理器取指令和执行指令时,将不再访问描述符表,而是直接从高速缓存器中取得的基地址在同 EIP 中的内容相加而获得的物理地址中获得指令

代码段是向上(高地址方向)扩展的,所以 实际使用的段界限就是段内最后一个允许访问的偏移地址

指令的执行必须满足 0<=(EIP+指令长度-1)<=实际使用的段界限
3

栈操作时的保护

栈段是向下(低地址方向)扩展的,每当往栈中压入数据时,ESP的内容要减去操作数的长度,所以栈段 实际使用的段界限就是段内不允许访问的最低端偏移地址,最高端无限制

所以进行栈操作时,必须满足 实际使用的段界限+1<=(ESP-操作数的长度)<=0xFFFFFFFF

数据访问时的保护

数据段可以向上扩展或向下扩展

当处理器访问向上扩展的数据段时,必须满足 0<=(EA+操作数打下-1)<=实际使用的段界限 (EA表示有效地址,即偏移地址)

对向上扩展的数据段的检查和代码段的检查基本一致,不同的点在于,代码段取决于指令的长度,数据段取决于操作数的尺寸

使用别名访问代码段

当两个以上的描述符都描述和指向同一个段时,把另外的描述符称为别名

当我们想要访问代码段内的数据时,可以重新为该段安装一个新的描述符,并将其定义为可读可写的数据段,这样就可以通过这个数据段来访问了

别名的应用不仅仅于此,它还可以用在多个程序共享同一个内存区域等等

xchg指令

xchg指令用于交换两个操作数的内容,两个操作数可以是寄存器或者内存单元(也可以同时都是),但不允许同时为内存地址

 posted on 2024-08-15 17:06  Dylaris  阅读(48)  评论(0)    收藏  举报