匯編語(yǔ)言的準備知識--給初次接觸匯編者(2)
匯編指令的操作數可以是內存中的數據, 如何讓程序從內存中正確取得所需要的數據就是對內存的尋址.
INTEL 的CPU 可以工作在兩種尋址模式:實(shí)模式和保護模式. 前者已經(jīng)過(guò)時(shí),就不講了, WINDOWS 現在是32位保護模式的系統, PE 文件就基本是運行在一個(gè)32位線(xiàn)性地址空間, 所以這里就只介紹32位線(xiàn)性空間的尋址方式.
其實(shí)線(xiàn)性地址的概念是很直觀(guān)的, 就想象一系列字節排成一長(cháng)隊,第一個(gè)字節編號為0, 第二個(gè)編號位1, .... 一直到4294967295(十六進(jìn)制FFFFFFFF,這是32位二進(jìn)制數所能表達的最大值了). 這已經(jīng)有4GB的容量! 足夠容納一個(gè)程序所有的代碼和數據. 當然, 這并不表示你的機器有那么多內存. 物理內存的管理和分配是很復雜的內容, 初學(xué)者不必在意, 總之, 從程序本身的角度看, 就好象是在那么大的內存中.
在INTEL系統中, 內存地址總是由"段選擇符:有效地址"的方式給出.段選擇符(SELECTOR)存放在某一個(gè)段寄存器中, 有效地址則可由不同的方式給出. 段選擇符通過(guò)檢索段描述符確定段的起始地址, 長(cháng)度(又稱(chēng)段限制), 粒度, 存取權限, 訪(fǎng)問(wèn)性質(zhì)等. 先不用深究這些, 只要知道段選擇符可以確定段的性質(zhì)就行了. 一旦由選擇符確定了段, 有效地址相對于段的基地址開(kāi)始算. 比如由選擇符1A7選擇的數據段, 其基地址是400000, 把1A7 裝入DS中, 就確定使用該數據段. DS:0 就指向線(xiàn)性地址400000. DS:1F5278 就指向線(xiàn)性地址5E5278. 我們在一般情況下, 看不到也不需要看到段的起始地址, 只需要關(guān)心在該段中的有效地址就行了. 在32位系統中, 有效地址也是由32位數字表示, 就是說(shuō), 只要有一個(gè)段就足以涵蓋4GB線(xiàn)性地址空間, 為什么還要有不同的段選擇符呢? 正如前面所說(shuō)的, 這是為了對數據進(jìn)行不同性質(zhì)的訪(fǎng)問(wèn). 非法的訪(fǎng)問(wèn)將產(chǎn)生異常中斷, 而這正是保護模式的核心內容, 是構造優(yōu)先級和多任務(wù)系統的基礎. 這里有涉及到很多深層的東西, 初學(xué)者先可不必理會(huì ).
有效地址的計算方式是: 基址+間址*比例因子+偏移量. 這些量都是指段內的相對于段起始地址的量度, 和段的起始地址沒(méi)有關(guān)系. 比如, 基址=100000, 間址=400, 比例因子=4, 偏移量=20000, 則有效地址為:
100000+400*4+20000=100000+1000+20000=121000. 對應的線(xiàn)性地址是400000+121000=521000. (注意, 都是十六進(jìn)制數).
基址可以放在任何32位通用寄存器中, 間址也可以放在除ESP外的任何一個(gè)通用寄存器中. 比例因子可以是1, 2, 4 或8. 偏移量是立即數. 如: [EBP+EDX*8+200]就是一個(gè)有效的有效地址表達式. 當然, 多數情況下用不著(zhù)這么復雜, 間址,比例因子和偏移量不一定要出現.
內存的基本單位是字節(BYTE). 每個(gè)字節是8個(gè)二進(jìn)制位, 所以每個(gè)字節能表示的最大的數是11111111, 即十進(jìn)制的255. 一般來(lái)說(shuō), 用十六進(jìn)制比較方便, 因為每4個(gè)二進(jìn)制位剛好等于1個(gè)十六進(jìn)制位, 11111111b = 0xFF. 內存中的字節是連續存放的, 兩個(gè)字節構成一個(gè)字(WORD), 兩個(gè)字構成一個(gè)雙字(DWORD). 在INTEL架構中, 采用small endian格式, 即在內存中,高位字節在低位字節后面. 舉例說(shuō)明:十六進(jìn)制數803E7D0C, 每?jì)晌皇且粋€(gè)字節, 在內存中的形式是: 0C 7D 3E 80. 在32位寄存器中則是正常形式,如在EAX就是803E7D0C. 當我們的形式地址指向這個(gè)數的時(shí)候,實(shí)際上是指向第一個(gè)字節,即0C. 我們可以指定訪(fǎng)問(wèn)長(cháng)度是字節, 字或者雙字. 假設DS
EDX]指向第一個(gè)字節0C:
mov AL, byte ptr DS
EDX] ;把字節0C存入AL
mov AX, word ptr DS
EDX] ;把字7D0C存入AX
mov EAX, dword ptr DS
EDX] ;把雙字803E7D0C存入EAX
在段的屬性中,有一個(gè)就是缺省訪(fǎng)問(wèn)寬度.如果缺省訪(fǎng)問(wèn)寬度為雙字(在32位系統中經(jīng)常如此),那么要進(jìn)行字節或字的訪(fǎng)問(wèn),就必須用byte/word ptr顯式地指明.
缺省段選擇:如果指令中只有作為段內偏移的有效地址,而沒(méi)有指明在哪一個(gè)段里的時(shí)候,有如下規則:
如果用ebp和esp作為基址或間址,則認為是在SS確定的段中;
其他情況,都認為是在DS確定的段中。
如果想打破這個(gè)規則,就必須使用段超越前綴。舉例如下:
mov eax, dword ptr [edx] ;缺省使用DS,把DS
EDX]指向的雙字送入eax
mov ebx, dword ptr ES
EDX] ;使用ES:段超越前綴,把ES
EDX]指向的雙字送入ebx
堆棧:
堆棧是一種數據結構,嚴格地應該叫做“棧”。“堆”是另一種類(lèi)似但不同的結構。SS 和 ESP 是INTEL對棧這種數據結構的硬件支持。push/pop指令是專(zhuān)門(mén)針對棧結構的特定操作。SS指定一個(gè)段為棧段,ESP則指出當前的棧頂。push xxx 指令作如下操作:
把ESP的值減去4;
把xxx存入SS
ESP]指向的內存單元。
這樣,esp的值減小了4,并且SS
ESP]指向新壓入的xxx. 所以棧是“倒著(zhù)長(cháng)”的,從高地址向低地址方向擴展。pop yyy 指令做相反的操作,把SS
ESP]指向的雙字送到yyy指定的寄存器或內存單元,然后把esp的值加上4。這時(shí),認為該值已被彈出,不再在棧上了,因為它雖然還暫時(shí)存在在原來(lái)的棧頂位置,但下一個(gè)push操作就會(huì )把它覆蓋。因此,在棧段中地址低于esp的內存單元中的數據均被認為是未定義的。
最后,有一個(gè)要注意的事實(shí)是,匯編語(yǔ)言是面向機器的,指令和機器碼基本上是一一對應的,所以它們的實(shí)現取決于硬件.有些看似合理的指令實(shí)際上是不存在的,比如:
mov DS
edx], ds
ecx] ;內存單元之間不能直接傳送
mov DS, 1A7 ;段寄存器不能直接由立即數賦值
mov EIP, 3D4E7 ;不能對指令指針直接操作.
匯編指令的操作數可以是內存中的數據, 如何讓程序從內存中正確取得所需要的數據就是對內存的尋址.
INTEL 的CPU 可以工作在兩種尋址模式:實(shí)模式和保護模式. 前者已經(jīng)過(guò)時(shí),就不講了, WINDOWS 現在是32位保護模式的系統, PE 文件就基本是運行在一個(gè)32位線(xiàn)性地址空間, 所以這里就只介紹32位線(xiàn)性空間的尋址方式.
其實(shí)線(xiàn)性地址的概念是很直觀(guān)的, 就想象一系列字節排成一長(cháng)隊,第一個(gè)字節編號為0, 第二個(gè)編號位1, .... 一直到4294967295(十六進(jìn)制FFFFFFFF,這是32位二進(jìn)制數所能表達的最大值了). 這已經(jīng)有4GB的容量! 足夠容納一個(gè)程序所有的代碼和數據. 當然, 這并不表示你的機器有那么多內存. 物理內存的管理和分配是很復雜的內容, 初學(xué)者不必在意, 總之, 從程序本身的角度看, 就好象是在那么大的內存中.
在INTEL系統中, 內存地址總是由"段選擇符:有效地址"的方式給出.段選擇符(SELECTOR)存放在某一個(gè)段寄存器中, 有效地址則可由不同的方式給出. 段選擇符通過(guò)檢索段描述符確定段的起始地址, 長(cháng)度(又稱(chēng)段限制), 粒度, 存取權限, 訪(fǎng)問(wèn)性質(zhì)等. 先不用深究這些, 只要知道段選擇符可以確定段的性質(zhì)就行了. 一旦由選擇符確定了段, 有效地址相對于段的基地址開(kāi)始算. 比如由選擇符1A7選擇的數據段, 其基地址是400000, 把1A7 裝入DS中, 就確定使用該數據段. DS:0 就指向線(xiàn)性地址400000. DS:1F5278 就指向線(xiàn)性地址5E5278. 我們在一般情況下, 看不到也不需要看到段的起始地址, 只需要關(guān)心在該段中的有效地址就行了. 在32位系統中, 有效地址也是由32位數字表示, 就是說(shuō), 只要有一個(gè)段就足以涵蓋4GB線(xiàn)性地址空間, 為什么還要有不同的段選擇符呢? 正如前面所說(shuō)的, 這是為了對數據進(jìn)行不同性質(zhì)的訪(fǎng)問(wèn). 非法的訪(fǎng)問(wèn)將產(chǎn)生異常中斷, 而這正是保護模式的核心內容, 是構造優(yōu)先級和多任務(wù)系統的基礎. 這里有涉及到很多深層的東西, 初學(xué)者先可不必理會(huì ).
有效地址的計算方式是: 基址+間址*比例因子+偏移量. 這些量都是指段內的相對于段起始地址的量度, 和段的起始地址沒(méi)有關(guān)系. 比如, 基址=100000, 間址=400, 比例因子=4, 偏移量=20000, 則有效地址為:
100000+400*4+20000=100000+1000+20000=121000. 對應的線(xiàn)性地址是400000+121000=521000. (注意, 都是十六進(jìn)制數).
基址可以放在任何32位通用寄存器中, 間址也可以放在除ESP外的任何一個(gè)通用寄存器中. 比例因子可以是1, 2, 4 或8. 偏移量是立即數. 如: [EBP+EDX*8+200]就是一個(gè)有效的有效地址表達式. 當然, 多數情況下用不著(zhù)這么復雜, 間址,比例因子和偏移量不一定要出現.
內存的基本單位是字節(BYTE). 每個(gè)字節是8個(gè)二進(jìn)制位, 所以每個(gè)字節能表示的最大的數是11111111, 即十進(jìn)制的255. 一般來(lái)說(shuō), 用十六進(jìn)制比較方便, 因為每4個(gè)二進(jìn)制位剛好等于1個(gè)十六進(jìn)制位, 11111111b = 0xFF. 內存中的字節是連續存放的, 兩個(gè)字節構成一個(gè)字(WORD), 兩個(gè)字構成一個(gè)雙字(DWORD). 在INTEL架構中, 采用small endian格式, 即在內存中,高位字節在低位字節后面. 舉例說(shuō)明:十六進(jìn)制數803E7D0C, 每?jì)晌皇且粋€(gè)字節, 在內存中的形式是: 0C 7D 3E 80. 在32位寄存器中則是正常形式,如在EAX就是803E7D0C. 當我們的形式地址指向這個(gè)數的時(shí)候,實(shí)際上是指向第一個(gè)字節,即0C. 我們可以指定訪(fǎng)問(wèn)長(cháng)度是字節, 字或者雙字. 假設DS

mov AL, byte ptr DS

mov AX, word ptr DS

mov EAX, dword ptr DS

在段的屬性中,有一個(gè)就是缺省訪(fǎng)問(wèn)寬度.如果缺省訪(fǎng)問(wèn)寬度為雙字(在32位系統中經(jīng)常如此),那么要進(jìn)行字節或字的訪(fǎng)問(wèn),就必須用byte/word ptr顯式地指明.
缺省段選擇:如果指令中只有作為段內偏移的有效地址,而沒(méi)有指明在哪一個(gè)段里的時(shí)候,有如下規則:
如果用ebp和esp作為基址或間址,則認為是在SS確定的段中;
其他情況,都認為是在DS確定的段中。
如果想打破這個(gè)規則,就必須使用段超越前綴。舉例如下:
mov eax, dword ptr [edx] ;缺省使用DS,把DS

mov ebx, dword ptr ES


堆棧:
堆棧是一種數據結構,嚴格地應該叫做“棧”。“堆”是另一種類(lèi)似但不同的結構。SS 和 ESP 是INTEL對棧這種數據結構的硬件支持。push/pop指令是專(zhuān)門(mén)針對棧結構的特定操作。SS指定一個(gè)段為棧段,ESP則指出當前的棧頂。push xxx 指令作如下操作:
把ESP的值減去4;
把xxx存入SS

這樣,esp的值減小了4,并且SS


最后,有一個(gè)要注意的事實(shí)是,匯編語(yǔ)言是面向機器的,指令和機器碼基本上是一一對應的,所以它們的實(shí)現取決于硬件.有些看似合理的指令實(shí)際上是不存在的,比如:
mov DS


mov DS, 1A7 ;段寄存器不能直接由立即數賦值
mov EIP, 3D4E7 ;不能對指令指針直接操作.

