保护模式
我们刚开机时候进入的都是实模式,对硬件访问没有任何的保护措施,随意修改里面的程序,及其不安全。所以我们之后进入保护模式。
保护模式:cpu扩展、寻址扩展、运行模式扩展、运行模式反转、指令扩展
CPU扩展:
实模式是使用的8086的CPU的16位,保护模式的运行环境变为了32位。所以开机时32位的CPU先处于16位的状态,再转为32位。但是16位也可以访问32位的资源。16位模式下默认操作数位16位,32位模式下默认操作数位32位
寄存器扩展:
除了段寄存器外,通用寄存器、指令指针寄存器、标志寄存器都从原来的16位扩展到32位。AX、BX、CX、DX、SI、DI、BP、SP-->EAX、EBX、ECX、EDX、ESI、EDI、EBP、ESP,FLAGS-->ELAGS,IP-->EIP。
寻址扩展:
实模式下的寻址方式:基址寄存器只能是bx、bp,变址寄存器只能是si、di,bx的默认寄存器是ds、bp的默认寄存器是ss,但是保护模式下,基址寄存器变为所有的32位通用寄存器,变址寄存器变为了除了esp之外的所有32位通用寄存器,偏移量由实模式的16位变为了32位。并且变址寄存器可以乘以一个比例因子,比例因子只能是1、2、4、8。
运行模式反转:
当CPU处于实模式下的16位,依然可以使用32位下的资源,eg:[bits 16] mov eax,0x1234,其中eax是32位的资源,按照常理,CPU操作时候,因为默认16位,所以只会将0x1234读给ax,对eax的前两个字节不管不顾( 保持原值),但是我们的目的是要求CPU也照顾到eax的前两个字节(置0),所以编译器要生成机器码,这需要我们告诉它生成样的机器码,是否要加上前缀来提醒CPU考虑前两个字节。16位下的机器码和32位的机器码是不同的,有时候一个寄存器表示的机器码都不同。所以我们的代码刚开始是在实模式下运行的(默认的),编译器看到32位的资源也会前缀反转去使用它。但是之后我们要进入保护模式32位,关键是还是同一个程序,我们要通知编译器我们进入32位了,看到32资源不需要前缀反转了,所以如下:
[bits 16]是告诉编译器,下面的代码将我编译成16位的机器码,使CPU只考虑16位的资源(寄存器资源、立即数等)。
[bits 32]]是告诉编译器,下面的代码将我编译成32位的机器码,使CPU只考虑32位的资源(寄存器资源、立即数等)。
指令格式:
操作数反转前缀:指令前缀0x66
16位和32位模式之间可以互相使用资源,16位模式下默认操作数位16位,32位模式下默认操作数位32位。但是有时候我们的要求是不一样的,比如要在16位下使用32位的操作数,所以要在机器指令之前加上指令前缀0x66让cpu来识别应该将立即数翻译成0x1234,还是0x0000 1234.
机器码前加上0x66后,假设当前运行模式是16位实模式,操作数大小将变为32位
机器码前加上0x66后,假设当前运行模式是32位保护模式,操作数大小将变为16位
[bits 16] mov eax,0x1234 ;16位模式下按照常规CPU要将0x1234翻译成3412(小端),但eax的32位资源,我们的意思是要考虑eax,所以编译器需要将0x1234反转为32位的34120000。
[bits 32] mov ax,0x1234 ;32位模式下按照常规cpu将0x1234翻译成34120000,但ax是16位资源,我们的意思是只需考虑ax,编译器就翻译为0x3412
寻址方式反转前缀:指令前缀0x67
[bits 16] mov word[eax],0x1234 ;16位模式下,ax是不允许加入到基址寻址中的,编译器将会在机器码前加上67表示段基址加上eax中值将是我们将要访问的内存值。word为伪指令,表示在内存开始出连续写入两个字节大小的数据。
[bits 16] mov dword[eax],0x1234 ;16为模式下,ax是不允许的,所以加上67,但是dword伪指令要在所示内存出连续写入四个字节大小的数据,16位下0x1234默认只翻译位3412,所以还要将3412 0000,所以还要加上66。
[bits 32]mov word [eax],0x1234 ;32位下CPU默认将立即数翻译为32位,但是我们的要求是伪指令word,只要一个字即可,所以加66
[bits 32]mov dword [bx],0x1234 ;32位下CPU默认使用ebx,但是我们的意思是只是用bx,不要使用它的前两个字节,所以加上67。
指令扩展
loop:
实模式下用cx来储存循环次数,保护模式下要用ecx。每操作一次循环体后cx减1,然后执行loop指令前要拿ecx和0比较(循环条件),等于0则停止循环,不等于0则执行loop指令。相当于for
mul:
实模式下:
如果乘数是8位,则al当作另一个乘数,结果是16位,结果在AX中。
如果乘数是16位,则ax当作另一个乘数,结果是32位,存入高16位在DX中,低16位在AX中。
如果乘数是32位,则eax当作另一个乘数,结果是64位,存入edx:eax中,其中edx是高32位,eax是低32位。
保护模式下:
如果乘数是8位,则al当作另一个乘数,结果是16位,存入ax中。
如果乘数是16位,则ax当作另一个乘数,结果是32位,存入eax中。
如果乘数是32位,则eax当作另一个乘数,结果是64位,存入edx:eax中,其中edx是高32位,eax是低32位。
div:
如果除数是8位,被除数就是16位,位于ax中,结果的商在al,余数在ah
如果除数是16位,被除数就是32位:高16位位于dx,低16位位于ax中,结果的商在ax,余数在dx
如果除数是32位,被除数就是64位:高32位位于edx,低32位位于eax中,结果的商在eax,余数在edx
push:
不管在实模式还是保护模式都可以同时处理16位和32位的数据。
push 立即数
push 寄存器
push 内存
push 立即数:
实模式下:
压入8位立即数,因为默认操作数是16位,CPU位将其扩展为16位后,将其入栈。sp-2
压入16位立即数,cpu会将其直接入栈。sp-2
压入32位立即数,cpu会将其直接入栈。sp-4
保护模式下:
压入8位立即数,因为默认操作数是32位,CPU位将其扩展为32位后,将其入栈,sp-4
压入16位立即数,cpu会将其直接入栈,sp-2
压入32位立即数,cpu会将其直接入栈,sp-4
push 寄存器:
实模式下:
压入段寄存器:cs、ds、es、fs、gs、ss,,按照当前默认操作数大小压入,sp-2
压入通用寄存器,如果压入的16位的,sp-2,如果压入32位的,sp-4<