汇编基础之二 -- 寄存器和内存堆栈

寄存器

这里介绍8个通用寄存器和两个特殊的寄存器

其余寄存器https://blog.csdn.net/weixin_4427

8个通用寄存器

 

对于8个通用寄存器来说, 一个寄存器是一个4字节大小的物理单元,也就是数据宽度为DWORD,为了方便实现byte和word数据宽度的操作。又将这个寄存器再次进行划分。以EAX单元为例子。EAX是一个4字节的32位寄存器,为了方便对WORD数据宽度进行处理,将EAX低位的两个字节命名位AX寄存器,同样的。为了方便byte的操作。将AX的寄存器的高8位和低8位分别位AH和AL寄存器。如图:

 

 

根据这样的方式,8个寄存器再次被划分位多个子单元,得到了16和8位的寄存器。

32位16位8位
EAX: AX AH、AL
EBX BX BH、BL
ECX CX CH、CL
EDX DX DH、DL
ESI:使用MOVS指令复制数据时,ESI储存源数据的地址 SI  
EDI:使用MOVS指令复制数据时,EDI储存复制的目的地址 DI  
ESP:储存程序栈顶的内存地址 SP  
EBP:储存程序栈底的内存地址 BP  

如上表,只存在8个通用寄存器,然后被分成16位寄存器和8位寄存器,16位和8寄存器并没有专门分配硬件,而是与32位寄存器通用来实现。在32位CPU中,32位寄存器EAX、EBX、ECX和EDX可以用作传送数据、暂存数据,保存算术逻辑运算结果,同时也可以保存内存地址,即作为指针寄存器。

ESI, EDI有默认的用途,主要用于复制,两个寄存器中都存储内存中的地址,当使用MOVS指令进行复制时候,默认是将ESI内存地址处的数据复制到EDI保存的内存地址处,每次复制之后,ESI和EDI中保存的地址会对应的进行偏移,这样可以连续的复制内存中一串内存地址的数据。

ESP和EBP寄存器中记录的是当前程序的堆栈空间的地址。堆栈式计算机分配给程序的一段连续的内存块,例如分配给程序内存编号为1000 - 5000这段内存块,这个空间成为程序的堆栈,程序中的需要保存的数据暂存在这个空间中,并在ESP寄存器中记录内栈顶的地址(内存小编号),EBP中保存栈低的地址。

两个特殊的寄存器

EIP寄存器:储存的是CPU下一次执行的指令地址。当cpu执行完一条指令后,就会从EIP寄存器中获取下一次执行的指令,我们可以通过改变EIP寄存器中的值,从而实现程序按照我们指定的方式执行。

EFL寄存器:该寄存器中的每一位用于表示一组的状态标志,用于提供程序的状态及进行相应的控制。该寄存器不能用于存储其他值。其标志位含义如下:

  • CF [Carry flag] :若算术操作产生的结果在最高有效位发生进位或借位则将其置1,反之清零,最高位发生进位或借位意味着结果溢出。所以该位置的状态可以判断一个无符号数计算结果是否溢出。

  • PF [Parity flag] 如果结果的最低有效字节(least-significant byte)包含偶数个1位则该位置1,否则清零

  • AF [Adjust flag] 如果算术操作在结果的第3位发生进位或借位则将该标志置1,否则清零。这个标志在BCD(binary-code decimal)算术运算中被使用。

  • ZF [Zero flag] 若结果为0则将其置1,反之清零。

  • SF [Sign flag] 该标志被设置为有符号整型的最高有效位。(0指示结果为正,反之则为负)

  • OF [Overflow flag] 由于有符号数最高位为符号位,则第二高位置发生了进位或者借位即可判断该数溢出。如果运算后第二高位发生了借位或者进位,该标志位置为1,反之清零。这个标志用于标志带符号整型运算指示溢出状态。

  • DF [Direction flag]:控制串指令(MOVS, CMPS, SCAS, LODS以及STOS)每次执行后会自动将内存地址进行递增或递减。设置DF标志为1,控制串指令每次操作的内存地址会自动递减(从高地址向低地址方向处理字符串),设置为0则使得串指令每次操作的地址为自动递增。STD以及CLD指令分别用于设置以及清除DF标志。

  • TF [Trap flag]:将该位设置为1以允许单步调试模式,清零则禁用该模式。

  • IF[Interrupt enable flag]:该标志用于控制处理器对可屏蔽中断请求(maskable interrupt requests)的响应。置1以响应可屏蔽中断,反之则禁止可屏蔽中断。

  • RF [Resume flag]控制处理器对调试异常的响应。

内存

计算机中最小的储存单位为字节,内存也不例外,每个字节作为一个储存单元。这样就将内存划分成了上亿个内存单元,为了方便管理这些单元,计算机选择了对每一个内存单元进行编号,想要使用一个内存单元时,只需要指定该内存单元编号(即内存地址)即可。为了能编号尽量多的地址,计算机使用了一个4字节的数来编址内存,这样可以对2^32块内存进行编址,每个块为1byte,2^32 byte = 4Gb,可以编址的内存大小为4G大小。

堆栈

程序运行时候并不会立即分配所有的内存给程序使用,会初始化一定大小的内存区域给程序,这个内存空间中的内存块可以被程序使用,用来储存程序中的数据。这段空间是从大地址的内存块开始使用的,为了查看当前的栈使用到哪一块地址内存单元,在ESP寄存器中记录了当前使用的内存位置,如果添加一个数据,将会在该位置添加,然后ESP继续向前移动一个位置。所以ESP寄存器记录的当前位置,就是下一个数据插入时的位置。这个指针我们称为栈顶指针,该指针随着数据的添加(push)和取出(pop)移动。同样的,EBP中记录了一个栈开始的地址,被成为栈底指针,该地址通常不会改变,只有在开辟一个新的栈空间,例如执行一个函数调用时,需要为函数重新记录一段新的函数空间时,才会改变栈底的位置。但是在改变栈底指针时,会将原来的栈底位置入栈保存,便于将来恢复到原栈底位置。

堆栈提升

我们知道,在高级语言中,当我们执行一个函数调用,将会开辟一个新的栈空间,此时就需要进行堆栈提升,此时需要更改ESP和EBP寄存器中记录的栈底和栈底指针,这样函数中产生的值将不会影响原栈空间(黄色部分)的数据。右图为堆栈提升后的结果,此时push数据会从1005开始添加,pop也只能到1005这个地址的数据,无法再向下pop,这样将无法影响元堆栈的内容。

 

堆栈平衡

堆栈平衡实际上就是恢复堆栈内容,例如执行一次函数操作时,进行堆栈提升,开辟新的空间来供函数使用,当函数执行结束后,应该让栈底和栈顶指针恢复到原来的位置。

简述过程为

1.将ESP寄存器中的值,更改为EBP的值,这样栈顶到了目前栈底的位置。ESP=1005
2.将EBP寄存器指向的内存地址中的值取出,即1000,将EBP寄存器的值改为该值1000。这样EBP回到了1000的位置。

由此完成了堆栈的恢复,实现了堆栈平衡

数据宽度

汇编语言中有三种数据宽度,分别为

  • BYTE:一个字节宽度,占8位

  • WORD:字,两个字节宽度,占16位

  • DOUBLEWORD:双字,四个字节宽度,占32位。

大小端模式

内存是从小到大进行编址的,储存一个多字节的数据时候,如果内存的小地址存储多字节数据的高位,就是大端模式。也就是说,先取数据的高位进行储存,再依次取低位储存。例如一个字符串"abcd",a为数据的高位,d为低位,内存会取出四个连续的内存空间来储存这个字符串。如果这段连续内存的小地址上储存的时a然后依次为bcd,就是大端模式。如果小地址处储存的是低位数据,则为小端模式。

数据储存为大端或者小端模式是由编译器决定的。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2020-07-23 11:06  没有想象力  阅读(3889)  评论(0编辑  收藏  举报