** 问题1:为什么会有保护模式**
注意:实模式不是32位CPU,变成了16位
为了让一个寄存器就能访问4GB空间,需要寄存器宽度提升到32位
寄存器要保持向下兼容,不能推翻之前的方案从头再来,必须在原有的基础上扩展,各寄存器在原有16位的基础上,再次向高位扩展了16位,成为了32位寄存器
偏移地址还在和实模式下的一样,但段基址可不是简单的一个地址的事了,为了更加安全,怎么也得多添加点约束条件才靠谱,这些"约束条件"便是对内存段的描述信息,由于信息比较多,所以专门找了个数据结构——全局描述符表,既然叫表,就说明里面有表项,表中至少有一个表项,其中每一个表项称为段描述符,其大小为64字节,用来描述各个内存段的起始地址,大小,权限等信息
段寄存器保存的再也不是段基址了,里面保存的内容叫"选择子",该选择子其实就是个数,用这个数来索引全局描述符表中的段描述符
访问段描述符非常耗费时间,所以在80286的保护模式下,为了提高获取段信息的效率,对段寄存器率先应用了缓存技术,将段信息用一个寄存器来缓存,这就是段描述符缓冲寄存器,对程序员而言它是不可见的,CPU每次将获取到的内存段信息,整理后,存入段描述符缓冲寄存器,以后每次访问相同的段时,就直接读取该段寄存器对应的段描述符缓冲寄存器
进入保护模式后,寻址方式也有了很大进步,基址,变址,寻址变得更加灵活了
实模式下对于内存寻址来说,其中的基址寻址,变址寻址,基址变址寻址,这三种形式中的基址寄存器只能是bx,bp,变址寄存器只能是si,di,其中bx默认的段寄存器是ds,它经常用于访问数据段,bp默认的段寄存器是ss,它经常用于访问栈
在保护模式下,同样是内存寻址中,基址寄存器不再只是bx,bp,而是所有32位的通用寄存器,变址寄存器不再只是si,di,而是除esp之外的所有32位通用寄存器,偏移量由实模式的16位变成了32位,还可以对变址寄存器乘以一个比例因子,比例因子,只能是1,2,4,8
保护模式下,内存段不再是简单地用段寄存器记载一下段基址就能用了,段的信息增加了很多,需要提前把段定义好才能使用
全局描述符表是保护模式下内存段的登记表,这是不同于实模式的显著特征之一
对于IA32架构的处理器,访问内存采用"段基址:段内偏移量”形式,即使到了保护模式,也绕不开这个限制
现在为了安全性,需要给内存段添加一些额外的安全属性,这些安全属性存放在内存当中
** 问题2:需要添加哪些属性来描述内存段**
问题三:什么是系统段
各种称为"门"的结构便是系统段,也就是硬件系统需要的结构,非软件使用的,如调度门,任务门,简而言之,门的元素就是入口,它通往一段程序
type字段,该字段共4位,用来表示内存段或门的子类型
表中A位表示Accessed位,这是CPU来设置的,没当该段被CPU访问过后,CPU就将此位置1,所以,创建一个新段描述符时,应该将此位置0,在调试时,根据该为便能判断该描述符是否可用了
C表示一致性代码段,C为1时表示该段时一致性代码段,C为0时表示该段为非一致性代码段
R表示可读,R为1表示可读,R为0表示不可读
X表示该段是否可执行
E是用来标识段的扩展方向,E为0表示向上扩展,E为1表示向下扩展
W是指段是否可写,W为1表示可写,通常用于数据段,W为0
段描述符的第13-14位是DPL字段,即描述符特权级
这两位能表示4种特权级,分别是0,1,2,3,级特权,数字越小,特权级越大
段描述符的第15位是P字段,即段是否存在,如果段存在于内存中,P为1,否则P为0
一个段描述符只用来定义一个内存段
这些描述符放在全局描述表中,全局描述表GDT相当于是描述符的数组,数组中的每个元素都是8字节的描述符
全局描述符表位于内存中,需要用专门的寄存器指向它,这个专门的寄存器便是GDTR,专门用来存储GDT的内存地址及大小
段描述符有了,描述符表也有了,我们该如何使用它,下面我们引出新的概念:段的选择子
段寄存器CS,DS,ES,FS,GS,SS,在实模式下,段中存储的是段基地址,即内存段的起始地址,而在保护模式下,由于段基址已经存入到段描述符中,所以段寄存器中再放段基址没有意义,在段基址中存入的是一个叫作选择子的东西——selector
由于段寄存器是16位,所以选择子也是16位,在其低2位即第0-1位,用于存储RPL,即请求特权级,可以表示0,1,2,3四种特权级,在选择子的第2位是TI位,用来指示选择子是在GDT中,还是在LDT中索引描述符,1表示在LDT中索引描述符,0表示在GDT中缩影描述符,选择子的高13位是描述符的索引值,用此值在GDT中索引描述符,前面说过GDT相当于一个描述符数组,所以此选择子中的索引值就是GDT中的下标
选择子的作用主要是确定段描述符,确定段描述的目的,一是为了特权级,权限等安全考虑,最主要的还是确认段的基地址
段基址在段描述符中,用给出的选择子缩影到描述符后,CPU自动从段描述符中取出段基址,这样再加上段内偏移地址,便凑成了段基址:段内偏移地址的形式
如图展示段描述符与内存段的关系
选择子的结构如图·所示:
需要注意的是GDT的第0个段描述符时不可用的,原因是定义在GDT中的段描述符是要用选择子来访问的,如果使用的选择子忘记初始化,选择子的值便会是0,这便会访问到第0个段描述符,为了避免出现这种因忘记初始化选择子而选择到第0个段描述符的情况,GDT中的第0个段描述符不可用
为了突破1MB内存的束缚,IBM在键盘控制器上的一些输出线来控制第21根地址(A20)的有效性,故被称为A20Gate
打开A20Gate的方式是及其简单的,将端口0x92的第一位置1就可以了
控制寄存器是CPU的窗口,既可以用来展示CPU的内部控制,也可用于控制CPU的运行机制,我们将用到CR0寄存器的第0位,即PE位,此位用于启动保护模式,是保护模式的开关,当打开此位后,CPU才真正进入保护模式
boot.inc文件,里面是一些配置信息,loader.S中用到的配置都是定义在boot.inc中的符号
;------------- loader和kernel ----------LOADER_BASE_ADDR equ 0x900
LOADER_START_SECTOR equ 0x2;-------------- gdt描述符属性 -------------
DESC_G_4K equ 1_00000000000000000000000b
DESC_D_32 equ 1_0000000000000000000000b
DESC_L equ 0_000000000000000000000b ; 64位代码标记,此处标记为0便可。
DESC_AVL equ 0_00000000000000000000b ; cpu不用此位,暂置为0
DESC_LIMIT_CODE2 equ 1111_0000000000000000b
DESC_LIMIT_DATA2 equ DESC_LIMIT_CODE2
DESC_LIMIT_VIDEO2 equ 0000_000000000000000b
DESC_P equ 1_000000000000000b
DESC_DPL_0 equ 00_0000000000000b
DESC_DPL_1 equ 01_0000000000000b
DESC_DPL_2 equ 10_0000000000000b
DESC_DPL_3 equ 11_0000000000000b
DESC_S_CODE equ 1_000000000000b
DESC_S_DATA equ DESC_S_CODE
DESC_S_sys equ 0_000000000000b
DESC_TYPE_CODE equ 1000_00000000b ;x=1,c=0,r=0,a=0 代码段是可执行的,非依从的,不可读的,已访问位a清0.
DESC_TYPE_DATA equ 0010_00000000b ;x=0,e=0,w=1,a=0 数据段是不可执行的,向上扩展的,可写的,已访问位a清0.DESC_CODE_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_CODE2 + DESC_P + DESC_DPL_0 + DESC_S_CODE + DESC_TYPE_CODE + 0x00
DESC_DATA_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_DATA2 + DESC_P + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x00
DESC_VIDEO_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_VIDEO2 + DESC_P + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x0b;-------------- 选择子属性 ---------------
RPL0 equ 00b
RPL1 equ 01b
RPL2 equ 10b
RPL3 equ 11b
TI_GDT equ 000b
TI_LDT equ 100b
loader.S文件
%include "boot.inc"section loader vstart=LOADER_BASE_ADDRLOADER_STACK_TOP equ LOADER_BASE_ADDRjmp near loader_start ; 此处的物理地址是:;构建gdt及其内部的描述符GDT_BASE: dd 0x00000000 dd 0x00000000CODE_DESC: dd 0x0000FFFF dd DESC_CODE_HIGH4DATA_STACK_DESC: dd 0x0000FFFFdd DESC_DATA_HIGH4VIDEO_DESC: dd 0x80000007 ;limit=(0xbffff-0xb8000)/4k=0x7dd DESC_VIDEO_HIGH4 ; 此时dpl已改为0GDT_SIZE equ $ - GDT_BASEGDT_LIMIT equ GDT_SIZE - 1 dq 50 dup(0) ;不能预留 60个 ,60 * 8B 太大了(只有1M内存),50可以运行正常。; times 60 dq 0 ; 此处预留60个描述符的slotSELECTOR_CODE equ (0x0001<<3) + TI_GDT + RPL0 ; 相当于(CODE_DESC - GDT_BASE)/8 + TI_GDT + RPL0SELECTOR_DATA equ (0x0002<<3) + TI_GDT + RPL0 ; 同上SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0 ; 同上 ;以下是定义gdt的指针,前2字节是gdt界限,后4字节是gdt起始地址gdt_ptr dw GDT_LIMIT dd GDT_BASEloadermsg db '2 loader in real.'loader_start:;------------------------------------------------------------
;INT 0x10 功能号:0x13 功能描述:打印字符串
;------------------------------------------------------------
;输入:
;AH 子功能号=13H
;BH = 页码
;BL = 属性(若AL=00H或01H)
;CX=字符串长度
;(DH、DL)=坐标(行、列)
;ES:BP=字符串地址
;AL=显示输出方式
; 0——字符串中只含显示字符,其显示属性在BL中。显示后,光标位置不变
; 1——字符串中只含显示字符,其显示属性在BL中。显示后,光标位置改变
; 2——字符串中含显示字符和显示属性。显示后,光标位置不变
; 3——字符串中含显示字符和显示属性。显示后,光标位置改变
;无返回值mov sp, LOADER_BASE_ADDRmov bp, loadermsg ; ES:BP = 字符串地址mov cx, 17 ; CX = 字符串长度mov ax, 0x1301 ; AH = 13, AL = 01hmov bx, 0x001f ; 页号为0(BH = 0) 蓝底粉红字(BL = 1fh)mov dx, 0x1800 ;int 0x10 ; 10h 号中断;---------------------------------------- 准备进入保护模式 ------------------------------------------;1 打开A20;2 加载gdt;3 将cr0的pe位置1;----------------- 打开A20 ----------------in al,0x92or al,0000_0010Bout 0x92,al;----------------- 加载GDT ----------------lgdt [gdt_ptr];----------------- cr0第0位置1 ----------------mov eax, cr0or eax, 0x00000001mov cr0, eax;jmp dword SELECTOR_CODE:p_mode_start ; 刷新流水线,避免分支预测的影响,这种cpu优化策略,最怕jmp跳转,jmp dword SELECTOR_CODE:p_mode_start ; 刷新流水线,避免分支预测的影响,这种cpu优化策略,最怕jmp跳转,; 这将导致之前做的预测失效,从而起到了刷新的作用。[bits 32]
p_mode_start:mov ax, SELECTOR_DATAmov ds, axmov es, axmov ss, axmov esp,LOADER_STACK_TOPmov ax, SELECTOR_VIDEO
; mov gs, axmov byte [gs:160], 'P'jmp $
编译:
nasm -I include/ -o mbr.bin mbr.S
nasm -I include/ -o loader.bin loader.S
复制到硬盘:
dd if=./mbr.bin of=hd60M.img bs=512 count=1 conv=notrunc
dd if=./loader.bin of=hd60M.img bs=512 count=4 seek=2 conv=notrunc
最后的效果为:
上一篇:干货!手把手教你穿透内网
下一篇:混淆视听