匯編基礎之二 -- 寄存器和內存堆棧


寄存器

這里介紹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,就是大端模式。如果小地址處儲存的是低位數據,則為小端模式。

數據儲存為大端或者小端模式是由編譯器決定的。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM