详细的过程说明参考:(1)  https://www.cnblogs.com/Philip-Tell-Truth/p/5211248.html    (2)x86汇编:从实模式到保护模式

这里简化一下说说要点:

1、 生成并加载GDT表

  实模式下任何进程可以无限制读写任何内存,甚至os的内存,毫无安全性可言;需要对用户进程读写内存的地址做严格限制,衍生出了保护模式;保护模式将内存分成不同的段,段基址、limit、各种属性存放在GDT表;用户程序读写段内存时需要先通过段寄存器的selector在GDT找到段描述符,查看是否有权限、偏移地址是否超过limit等。如果一切ok,可以继续读写段内数据;

  cs、ds、ss段的描述符:

注意点:(1)用户程序运行在3环,是没有权限更改GDT的,所以这种方式完全可以限制3环程序对内存的读写;windows要想改GDT,要么连接windbg,要么写驱动;

(2)lgdt [cs:gdt_size],从操作数指向的地址取6字节,高4字节作为gdt的基址,低2字节作为gdt中描述符的个数,如下:

      

      这6字节早在编译时就确定了:

      

     (3)GDT的机制是CPU定的,操作系统负责运维和使用各段的数据

2、打开A20

8086下,地址线有20位,寻址范围从0x00000~0xfffff,超过0xfffff的地址会被cpu重新从0x00000开始,相当于丢掉进位(或把地址对0xfffff取模)。80286以后,地址总线扩展到24位,为了访问0x100000~0x10FFEF之间的内存,而不是象8086/8088那样回绕到0,需要开启A20地址线,方法如下:

   mov dx,0x92                        ;南桥ICH芯片内的端口0x92
in al,dx
or al,0x02
out dx,al ;打开A20

3、开启保护模式

CPU提供的控制寄存器CR0~CR3用于控制CPU的运行模式。CR0第一位(0位)是保护模式允许位(Protection Enable,PE),如果把这个位置为1,那么处理器将会进入保护模式,核心代码如下:

    cli                                ;关闭中断,但后面一直没打开

    mov eax,cr0
or eax,0x01
mov cr0,eax ;设置PE位,处理器进入保护模式

注意:要先调用cli把if置0,关闭中断,避免给CR0赋值时被打断;

4、清空段寄存器中的缓存和旧流水线指令

 (1)段寄存器实际上有96位,保护模式下的汇编指令只能操作位于低16位的selector,剩余80位中部分作为缓存(可用sreg命令查看段寄存器dh、dl的值,这些都是描述符的缓存),存储了段基址。只要段不改变,缓存就不会更新;但保护模式下不能直接用实模式的地址(权限不够、位数不对等),需要清空;

(2)cpu和内存的速度差异很大,为了提高效率,cpu会提前预测并执行某些分支指令(幽灵漏洞就是这么来的,细节可参考B站的一个科普视频:https://www.bilibili.com/video/BV1eW411i7ZM?from=search&seid=2138940898008952062,这就是所谓的乱序执行,进入保护模式后这些指令的逻辑结果可能是有问题的,也要马上清除;

清除各个段寄存器、乱序指令缓存的办法:马上调用jmp指令跳转,让cpu认为当前各种缓存已经失效,核心代码如下:

;保护模式
jmp 0x0008:flush-$$ ;现在是在16位保护模式下,0x0008依然是段的选择子,而flush则是偏移地址
[bits ]
flush: ;头部加了vstart=0x7c00,这里变成了0x7c85,所以上面的jmp中的偏移要减去开头的基址,得到偏移
mov cx,0x0010
mov ds,cx

5、完整代码:

;---------------------保护模式主引导扇区程序---------------------
SECTION protectModel vstart=0x7c00 align=
mov ax,0x00
mov ss,ax
mov sp,0x7c00 mov ax,[cs:gdt_base];ax=0x7e00
mov dx,[cs:gdt_base+0x02];dx=0x0000;
;mov ax,[cs:gdt_base+0x7c00];这段已经被加载到0x7c00,所以需要加上;ax=0x7e00
;mov dx,[cs:gdt_base+0x7c00+0x02];dx=0x0000;
mov bx,0x10
div bx mov ds,ax ;得到base基地址,ds=0x7e0
mov bx,dx ;得到偏移地址,这里是0x0000 ;---------------------安装描述符---------------------
;描述符0
mov dword [ebx+0x00],0x00 ;第一个描述符必须是0;这里默认是ds段,也就是gdt_base的基址
mov dword [ebx+0x04],0x00 ;ebx是gdt表的偏移,ds是gdt的基址 ;描述符1
mov dword [ebx+0x08],0x7c0001FF
mov dword [ebx+0x0c],0x00409800 ;基地址0x00007c00,段界限0x001FF,粒度是字节,
;长度是512字节,在内存中的32位段,特权级为0,只能执行的代码段
;描述符2
mov dword [ebx+0x10],0x8000FFFF
mov dword [ebx+0x14],0x0040920B ;基地址0x000B8000,段界限0x0FFFF,粒度是字节,
;长度是64KB,在内存中的32位段,特权级为0,可以读写的向上拓展的数据段
;描述符3
mov dword [ebx+0x18],0x00007A00
mov dword [ebx+0x1c],0x00409600 ;基地址0x00000000,段界限0x07A00,粒度是字节,
;在内存中的32位段,特权级为0,可以读写的向下拓展的栈段 mov word [cs:gdt_size],;写入GDT段界限,4个描述符是32个字节,所以界限就是31
lgdt [cs:gdt_size] ;load gdt
;mov word [cs:gdt_size+0x7c00],31;写入GDT段界限,4个描述符是32个字节,所以界限就是31
;lgdt [cs:gdt_size+0x7c00] ;load gdt mov dx,0x92 ;南桥ICH芯片内的端口0x92
in al,dx
or al,0x02
out dx,al ;打开A20 cli ;关闭中断,但后面一直没打开 mov eax,cr0
or eax,0x01
mov cr0,eax ;设置PE位,处理器进入保护模式 ;保护模式
jmp 0x0008:flush-$$ ;现在是在16位保护模式下,0x0008依然是段的选择子,而flush则是偏移地址
[bits ]
flush: ;头部加了vstart=0x7c00,这里变成了0x7c85,所以上面的jmp中的偏移要减去开头的基址,得到偏移
mov cx,0x0010
mov ds,cx ;以下在屏幕上显示"Protect mode OK."
mov byte [0x00],'P'
mov byte [0x02],'r'
mov byte [0x04],'o'
mov byte [0x06],'t'
mov byte [0x08],'e'
mov byte [0x0a],'c'
mov byte [0x0c],'t'
mov byte [0x0e],' '
mov byte [0x10],'m'
mov byte [0x12],'o'
mov byte [0x14],'d'
mov byte [0x16],'e'
mov byte [0x18],' '
mov byte [0x1a],'O'
mov byte [0x1c],'K' ;以下用简单的示例来帮助阐述32位保护模式下的堆栈操作
mov cx,00000000000_11_000B ;加载堆栈段选择子
mov ss,cx
mov esp,0x7c00 mov ebp,esp ;保存堆栈指针
push byte '.' ;压入立即数(字节) sub ebp,
cmp ebp,esp ;判断压入立即数时,ESP是否减4
jnz ghalt
pop eax
mov [0x1e],al ;显示句点 ghalt:
hlt ;已经禁止中断,将不会被唤醒 ;------------------------------------------------------------------------------- gdt_size dw
gdt_base dd 0x00007e00 ;GDT的物理地址,主引导扇区是512个字节,这个地址刚好在主引导扇区之后 times -($-$$) db
db 0x55,0xaa

说明:

(1)整段代码被加载到0x7c00处,所以在开头额外加vstart=0x7c00,让nasm从这个地址开始编译;

    紧接着这4行代码也要更改:去掉0x7c00,因为gdt_base和gdt_size已经相对0x7c00计算偏移

  mov ax,[cs:gdt_base+0x7c00]
mov dx,[cs:gdt_base+0x7c00+0x02]   mov word [cs:gdt_size+0x7c00],31;写入GDT段界限,4个描述符是32个字节,所以界限就是31
lgdt [cs:gdt_size+0x7c00] (2)flush也是相对0x7c00开始计算偏移的,具体偏移是0x7c85,远超代码段的limit,导致出错异常,又跳回biso启动处执行
解决办法也简单,直接改成jmp 0x0008:flush-$$ 即可,让后面的偏移值相对于段开始的地方,这次对了,如下:

 (3)81行代码:push byte '.'  ,但nasm还是编译成push 0x0000002e, 估计是为了内存对齐

(4)效果:在频幕上打印一行字

 

x86架构:从实模式进入保护模式的更多相关文章

  1. ASM:《X86汇编语言-从实模式到保护模式》第10章:32位x86处理器的编程架构

    ★PART1:32位的x86处理器执行方式和架构 1. 寄存器的拓展(IA-32) 从80386开始,处理器内的寄存器从16位拓展到32位,命名其实就是在前面加上e(Extend)就好了,8个通用寄存 ...

  2. IA32系统级架构总览(一) 实模式和保护模式

    应用程序的编写大部分的时候是不必关心系统级架构的,最多学习一下平台所给的API即可,也就是我们通常说的黑箱子.但是在学习操作系统的时候,系统级架构是要关心的. 系统级架构很难学习,其中一个很大的原因是 ...

  3. 存储器的保护(三)——《x86汇编语言:从实模式到保护模式》读书笔记20

    存储器的保护(三) 修改本章代码清单,使之可以检测1MB以上的内存空间(从地址0x0010_0000开始,不考虑高速缓存的影响).要求:对内存的读写按双字的长度进行,并在检测的同时显示已检测的内存数量 ...

  4. 存储器的保护(一)——《x86汇编语言:从实模式到保护模式》读书笔记18

    本文是原书第12章的学习笔记. 说句题外话,这篇博文是补写的,因为让我误删了,可恶的是CSDN的回收站里找不到! 好吧,那就再写一遍,我有坚强的意志.司马迁曰:“文王拘而演<周易>:仲尼厄 ...

  5. 进入保护模式(三)——《x86汇编语言:从实模式到保护模式》读书笔记17

    (十)保护模式下的栈 ;以下用简单的示例来帮助阐述32位保护模式下的堆栈操作 mov cx,00000000000_11_000B ;加载堆栈段选择子 mov ss,cx mov esp,0x7c00 ...

  6. 16位模式/32位模式下PUSH指令探究——《x86汇编语言:从实模式到保护模式》读书笔记16

    一.Intel 32 位处理器的工作模式 如上图所示,Intel 32 位处理器有3种工作模式. (1)实模式:工作方式相当于一个8086 (2)保护模式:提供支持多任务环境的工作方式,建立保护机制 ...

  7. 进入保护模式(二)——《x86汇编语言:从实模式到保护模式》读书笔记14

    首先来段题外话:之前我发现我贴出的代码都没有行号,给讲解带来不便.所以从现在起,我要给代码加上行号.我写博客用的这个插入代码的插件,确实不支持自动插入行号.我真的没有找到什么好方法,无奈之下,只能按照 ...

  8. 32位x86处理器编程导入——《x86汇编语言:从实模式到保护模式》读书笔记08

    在说正题之前,我们先看2个概念. 1.指令集架构(ISA) ISA 的全称是 instruction set architecture,中文就是指令集架构,是指对程序员实际"可见" ...

  9. 《X86汇编语言:从实模式到保护模式》读书笔记之引言

    有幸结识了<X86汇编语言:从实模式到保护模式>一书.我觉得这本书非常好,语言活泼,通俗易懂,源码丰富,受益匪浅.读罢一遍,意犹未尽.于是打算再读一遍,并把自己的读书所学总结成笔记,一来给 ...

随机推荐

  1. Instrction Arrangement UDH 4109 拓扑排序 or 最长路

    题目描述 Ali has taken the Computer Organization and Architecture course this term. He learned that ther ...

  2. unity position 记录

    localPosition为自身矩形中心点(Pivot)与其父节点矩形中心点(Pivot)的相对位置坐标,与自身锚点(Anchors)无关.anchoredPosition为矩形中心点(Pivot)与 ...

  3. day69 cookie与session

    目录 一.forms源码解析 二.cookie与session发展史 三.cookie操作 四.session操作 五.CBV如何添加装饰器 一.forms源码解析 # from组件的切入点是is_v ...

  4. python 迭代器(一):迭代器基础(一) 语言内部使用 iter(...) 内置函数处理可迭代对象的方式

    简介 在 Python 中,所有集合都可以迭代.在 Python 语言内部,迭代器用于支持: 1.for 循环2.构建和扩展集合类型3.逐行遍历文本文件4.列表推导.字典推导和集合推导5.元组拆包6. ...

  5. 数据可视化之分析篇(七)Power BI数据分析应用:水平分析法

    https://zhuanlan.zhihu.com/p/103264851 首先,以财务报表分析为例,介绍通用的分析方法论,整体架构如下图所示: (点击查看大图) 接下来我会围绕这五种不同的方法论, ...

  6. POJ 1063 Flip and Shift 最详细的解题报告

    题目来源:Flip and Shift 题目大意:一个椭圆形的环形容器中有黑色和白色两种盘子,问你是否可以将黑色的盘子连续的放在一起.你可以有以下两种操作: 1.顺时针旋转所有的盘子 2.顺时针旋转3 ...

  7. Mysql----左连接、右连接、内连接、全连接的区别

    最近,突然想起来数据库有好些时间没用到,所以,想把数据库有关的知识回顾一下,所以接下来这个月,基本上会以数据库的帖子来写为主,首先,很多同学都会有个错觉,觉得学习数据库会sql语句的增删改查就够了,其 ...

  8. spring oauth2获取当前登录用户信息。

    使用spring oauth2框架做授权鉴定.想获取当前用户信息怎么办? 我们知道spring oauth2是基于spring security的实现的. spring security可以通过Sec ...

  9. Java顺序查找、二分查找

    Java顺序查找.二分查找   查找算法中顺序查找算是最简单的了,无论是有序的还是无序的都可以,只需要一个个对比即可,但其实效率很低. 顺序查找 动图演示 详细代码 // 顺序查找 public st ...

  10. [CISCN2019 华东南赛区]Double Secret

    0x01 进入页面如下 提示我们寻找secret,再加上题目的提示,猜测这里有secret页面,我们尝试访问,结果如下 根据它这个话的意思,是让我们传参,然后它会给你加密,我们试一下 发现输入的1变成 ...