日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

X86汇编概要

發布時間:2025/6/17 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 X86汇编概要 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

來自:https://www.cnblogs.com/jiftle/p/8453106.html

本文翻譯自:http://www.cs.virginia.edu/~evans/cs216/guides/x86.html

參考資料:標識寄存器?

?

本文描述基本的32位X86匯編語言的一個子集,其中涉及匯編語言的最核心部分,包括寄存器結構,數據表示,基本的操作指令(包括數據傳送指令、邏輯計算指令、算數運算指令),以及函數的調用規則。個人認為:在理解了本文后,基本可以無障礙地閱讀絕大部分標準X86匯編程序。當然,更復雜的指令請參閱Intel相關文檔。

1 寄存器.

主要寄存器如下圖所示:

X86處理器中有8個32位的通用寄存器。由于歷史的原因,EAX通常用于計算,ECX通常用于循環變量計數。ESP和EBP有專門用途,ESP指示棧指針(用于指示棧頂位置),而EBP則是基址指針(用于指示子程序或函數調用的基址指針)。如圖中所示,EAX、EBX、ECX和EDX的前兩個高位字節和后兩個低位字節可以獨立使用,其中兩位低字節又被獨立分為H和L部分,這樣做的原因主要是考慮兼容16位的程序,具體兼容匹配細節請查閱相關文獻。

應用寄存器時,其名稱大小寫是不敏感的,如EAX和eax沒有區別。

2 內存和尋址模式

2.1聲明靜態數據區

可以在X86匯編語言中用匯編指令.DATA聲明靜態數據區(類似于全局變量),數據以單字節、雙字節、或雙字(4字節)的方式存放,分別用DB,DW, DD指令表示聲明內存的長度。在匯編語言中,相鄰定義的標簽在內存中是連續存放的。

.DATA???
varDB?64??;聲明一個字節,并將數值64放入此字節中
var2DB??; 聲明一個為初始化的字節.
?DB?10; 聲明一個沒有label的字節,其值為10.
XDW??;?聲明一個雙字節,未初始化.
YDD?30000????; 聲明一個4字節,其值為30000.

還可以聲明連續的數據和數組,聲明數組時使用DUP關鍵字

ZDD 1, 2, 3; Declare three 4-byte values, initialized to 1, 2, and 3. The value of location?Z + 8?will be 3.
bytes??DB 10 DUP(?); Declare 10 uninitialized bytes starting at location?bytes.
arrDD 100 DUP(0)????; Declare 100 4-byte words starting at location?arr, all initialized to 0
strDB 'hello',0; Declare 6 bytes starting at the address str, initialized to the ASCII character values for?hello?and the null (0) byte.

2.2 尋址模式

現代X86處理器具有232字節的尋址空間。在上面的例子中,我們用標簽(label)表示內存區域,這些標簽在實際匯編時,均被32位的實際地址代替。除了支持這種直接的內存區域描述,X86還提供了一種靈活的內存尋址方式,即利用最多兩個32位的寄存器和一個32位的有符號常數相加計算一個內存地址,其中一個寄存器可以左移1、2或3位以表述更大的空間。下面例子是匯編程序中常見的方式

mov eax, [ebx]; 將ebx值指示的內存地址中的4個字節傳送到eax中
mov [var], ebx;?將ebx的內容傳送到var的值指示的內存地址中.
mov eax, [esi-4]; 將esi-4值指示的內存地址中的4個字節傳送到eax中
mov [esi+eax], cl; 將cl的值傳送到esi+eax的值指示的內存地址中
mov edx, [esi+4*ebx]????; 將esi+4*ebx值指示的內存中的4個字節傳送到edx

下面是違反規則的例子:

mov eax, [ebx-ecx]; 只能用加法
mov [eax+esi+edi], ebx????; 最多只能有兩個寄存器參與運算

2.3 長度規定

在聲明內存大小時,在匯編語言中,一般用DB,DW,DD均可聲明的內存空間大小,這種現實聲明能夠很好地指導匯編器分配內存空間,但是,對于

mov [ebx], 2

如果沒有特殊的標識,則不確定常數2是單字節、雙字節,還是雙字。對于這種情況,X86提供了三個指示規則標記,分別為BYTE PTR,?WORD PTR, and?DWORD PTR,如上面例子寫成:mov?BYTE PTR?[ebx], 2,?mov WORD PTR [ebx], 2,?mov?DWORD PTR?[ebx], 2,則意思非常清晰。

3 匯編指令

匯編指令通常可以分為數據傳送指令、邏輯計算指令和控制流指令。本節將講述其中最重要的指令,以下標記分別表示寄存器、內存和常數。

<reg32>????32位寄存器 (EAX,?EBX,?ECX,?EDX,?ESI,?EDI,?ESP, or?EBP)
<reg16>16位寄存器 (AX,?BX,?CX, or?DX)
<reg8>8位寄存器(AH,?BH,?CH,?DH,?AL,?BL,?CL, or?DL)
<reg>任何寄存器
??
<mem>內存地址 (e.g.,?[eax],?[var + 4], or?dword ptr [eax+ebx])
<con32>32為常數
<con16>16位常數
<con8>8位常數
<con>任何8位、16位或32位常數

3.1 數據傳送指令

mov?— Move (Opcodes: 88, 89, 8A, 8B, 8C, 8E, ...)

mov指令將第二個操作數(可以是寄存器的內容、內存中的內容或值)復制到第一個操作數(寄存器或內存)。mov不能用于直接從內存復制到內存,其語法如下所示:

mov <reg>,<reg>
mov <reg>,<mem>
mov <mem>,<reg>
mov <reg>,<const>
mov <mem>,<const>

Examples
mov eax, ebx?— 將ebx的值拷貝到eax
mov byte ptr [var], 5?— 將5保存找var指示內存中的一個字節中

push— Push stack (Opcodes: FF, 89, 8A, 8B, 8C, 8E, ...)

push指令將操作數壓入內存的棧中,棧是程序設計中一種非常重要的數據結構,其主要用于函數調用過程中,其中ESP只是棧頂。在壓棧前,首先將ESP值減4(X86棧增長方向與內存地址編號增長方向相反),然后將操作數內容壓入ESP指示的位置。其語法如下所示:

push <reg32>
push <mem>
push <con32>

Examples
push eax?— 將eax內容壓棧
push [var]?— 將var指示的4直接內容壓棧

pop— Pop stack

pop指令與push指令相反,它執行的是出棧的工作。它首先將ESP指示的地址中的內容出棧,然后將ESP值加4. 其語法如下所示:
pop <reg32>
pop <mem>

Examples
pop edi?— pop the top element of the stack into EDI.
pop [ebx]?— pop the top element of the stack into memory at the four bytes starting at location EBX.

lea— Load effective address

?lea實際上是一個載入有效地址指令,將第二個操作數表示的地址載入到第一個操作數(寄存器)中。其語法如下所示:

Syntax
lea <reg32>,<mem>

Examples
lea eax, [var]?— var指示的地址載入eax中.
lea edi, [ebx+4*esi]?— ebx+4*esi表示的地址載入到edi中,這實際是上面所說的尋址模式的一種表示方式.

3.2 算術和邏輯指令

add— Integer Addition

add指令將兩個操作數相加,且將相加后的結果保存到第一個操作數中。其語法如下所示:

add <reg>,<reg>
add <reg>,<mem>
add <mem>,<reg>
add <reg>,<con>
add <mem>,<con>
Examples
add eax, 10?— EAX ← EAX + 10
add BYTE PTR [var], 10?— 10與var指示的內存中的一個byte的值相加,并將結果保存在var指示的內存中

sub— Integer Subtraction

sub指令指示第一個操作數減去第二個操作數,并將相減后的值保存在第一個操作數,其語法如下所示:

sub <reg>,<reg>
sub <reg>,<mem>
sub <mem>,<reg>
sub <reg>,<con>
sub <mem>,<con>
Examples
sub al, ah?— AL ← AL - AH
sub eax, 216?— eax中的值減26,并將計算值保存在eax中

inc, dec— Increment, Decrement

inc,dec分別表示將操作數自加1,自減1,其語法如下所示:

inc <reg>
inc <mem>
dec <reg>
dec <mem>

Examples
dec eax?— eax中的值自減1.
inc DWORD PTR [var]?—?var指示內存中的一個4-byte值自加1

imul— Integer Multiplication

整數相乘指令,它有兩種指令格式,一種為兩個操作數,將兩個操作數的值相乘,并將結果保存在第一個操作數中,第一個操作數必須為寄存器;第二種格式為三個操作數,其語義為:將第二個和第三個操作數相乘,并將結果保存在第一個操作數中,第一個操作數必須為寄存器。其語法如下所示:

imul <reg32>,<reg32>
imul <reg32>,<mem>
imul <reg32>,<reg32>,<con>
imul <reg32>,<mem>,<con>

Examples

imul eax, [var]?— eax→ eax * [var] imul esi, edi, 25?— ESI → EDI * 25

idiv— Integer Division

idiv指令完成整數除法操作,idiv只有一個操作數,此操作數為除數,而被除數則為EDX:EAX中的內容(一個64位的整數),操作的結果有兩部分:商和余數,其中商放在eax寄存器中,而余數則放在edx寄存器中。其語法如下所示:

Syntax
idiv <reg32>
idiv <mem>

Examples

idiv ebx idiv DWORD PTR [var] ? and, or, xor— Bitwise logical and, or and exclusive or 邏輯與、邏輯或、邏輯異或操作指令,用于操作數的位操作,操作結果放在第一個操作數中。其語法如下所示: and <reg>,<reg>
and <reg>,<mem>
and <mem>,<reg>
and <reg>,<con>
and <mem>,<con>

or <reg>,<reg>
or <reg>,<mem>
or <mem>,<reg>
or <reg>,<con>
or <mem>,<con>

xor <reg>,<reg>
xor <reg>,<mem>
xor <mem>,<reg>
xor <reg>,<con>
xor <mem>,<con>

Examples
and eax, 0fH?— 將eax中的錢28位全部置為0,最后4位保持不變.
xor edx, edx?— 設置edx中的內容為0.

not— Bitwise Logical Not

位翻轉指令,將操作數中的每一位翻轉,即0->1, 1->0。其語法如下所示:

not <reg>
not <mem>

Example
not BYTE PTR [var]?—?將var指示的一個字節中的所有位翻轉.

neg— Negate

取負指令。語法為:

neg <reg>
neg <mem>

Example
neg eax?— EAX → - EAX

shl, shr— Shift Left, Shift Right

位移指令,有兩個操作數,第一個操作數表示被操作數,第二個操作數指示位移的數量。其語法如下所示:

shl <reg>,<con8>
shl <mem>,<con8>
shl <reg>,<cl>
shl <mem>,<cl>

shr <reg>,<con8>
shr <mem>,<con8>
shr <reg>,<cl>
shr <mem>,<cl>

Examples

shl eax, 1?— Multiply the value of EAX by 2 (if the most significant bit is 0),左移1位,相當于乘以2 shr ebx, cl?— Store in EBX the floor of result of dividing the value of EBX by 2n?where?n?is the value in?CL. 3.3 控制轉移指令 X86處理器維持著一個指示當前執行指令的指令指針(IP),當一條指令執行后,此指針自動指向下一條指令。IP寄存器不能直接操作,但是可以用控制流指令更新。 一般用標簽(label)指示程序中的指令地址,在X86匯編代碼中,可以在任何指令前加入標簽。如: mov esi, [ebp+8] begin: xor ecx, ecxmov eax, [esi]

如第二條指令用begin指示,這種標簽的方法在某種程度上簡化了匯編程序設計,控制流指令通過標簽實現程序指令跳轉。

jmp?— Jump

控制轉移到label所指示的地址,(從label中取出執行執行),如下所示:

jmp <label>

Example
jmp begin?— Jump to the instruction labeled?begin.

jcondition— Conditional Jump

條件轉移指令,條件轉移指令依據機器狀態字中的一些列條件狀態轉移。機器狀態字中包括指示最后一個算數運算結果是否為0,運算結果是否為負數等。機器狀態字具體解釋請見微機原理、計算機組成等課程。語法如下所示:

je <label>?(jump when equal)
jne <label>?(jump when not equal)
jz <label>?(jump when last result was zero)
jg <label>?(jump when greater than)
jge <label>?(jump when greater than or equal to)
jl <label>?(jump when less than)
jle <label>(jump when less than or equal to)

Example
cmp eax, ebx
jle done? , 如果eax中的值小于ebx中的值,跳轉到done指示的區域執行,否則,執行下一條指令。

cmp— Compare cmp指令比較兩個操作數的值,并根據比較結果設置機器狀態字中的條件碼。此指令與sub指令類似,但是cmp不用將計算結果保存在操作數中。其語法如下所示: cmp <reg>,<reg>
cmp <reg>,<mem>
cmp <mem>,<reg>
cmp <reg>,<con>

Example
cmp DWORD PTR [var], 10
jeq loop,?

比較var指示的4字節內容是否為10,如果不是,則繼續執行下一條指令,否則,跳轉到loop指示的指令開始執行 call,?ret— Subroutine call and return 這兩條指令實現子程序(過程、函數等意思)的調用及返回。call指令首先將當前執行指令地址入棧,然后無條件轉移到由標簽指示的指令。與其它簡單的跳轉指令不同,call指令保存調用之前的地址信息(當call指令結束后,返回到調用之前的地址)。 ret指令實現子程序的返回機制,ret指令彈出棧中保存的指令地址,然后無條件轉移到保存的指令地址執行。 call,ret是函數調用中最關鍵的兩條指令。具體細節見下面一部分的講解。語法為: call <label> ret 4 調用規則 為了加強程序員之間的協作及簡化程序開發進程,設定一個函數調用規則非常必要,函數調用規則規定函數調用及返回的規則,只要遵照這種規則寫的程序均可以正確執行,從而程序員不必關心諸如參數如何傳遞等問題;另一方面,在匯編語言中可以調用符合這種規則的高級語言所寫的函數,從而將匯編語言程序與高級語言程序有機結合在一起。 調用規則分為兩個方面,及調用者規則和被調用者規則,如一個函數A調用一個函數B,則A被稱為調用者(Caller),B被稱為被調用者(Callee)。 下圖顯示一個調用過程中的內存中的棧布局:

在X86中,棧增長方向與內存編號增長方向相反。

Caller Rules

調用者規則包括一系列操作,描述如下:

1)在調用子程序之前,調用者應該保存一系列被設計為調用者保存的寄存器的值。調用者保存寄存器有eax,ecx,edx。由于被調用的子程序會修改這些寄存器,所以為了在調用子程序完成之后能正確執行,調用者必須在調用子程序之前將這些寄存器的值入棧。

2)在調用子程序之前,將參數入棧。參數入棧的順序應該是從最后一個參數開始,如上圖中parameter3先入棧。

3)利用call指令調用子程序。這條指令將返回地址放置在參數的上面,并進入子程序的指令執行。(子程序的執行將按照被調用者的規則執行)

當子程序返回時,調用者期望找到子程序保存在eax中的返回地址。為了恢復調用子程序執行之前的狀態,調用者應該執行以下操作:

1)清除棧中的參數;

2)將棧中保存的eax值、ecx值以及edx值出棧,恢復eax、ecx、edx的值(當然,如果其它寄存器在調用之前需要保存,也需要完成類似入棧和出棧操作)

Example?

如下代碼展示了一個調用子程序的調用者應該執行的操作。此匯編程序調用一個具有三個參數的函數_myFunc,其中第一個參數為eax,第二個參數為常數216,第三個參數為var指示的內存中的值。

push [var] ; Push last parameter first push 216 ; Push the second parameter push eax ; Push first parameter lastcall _myFunc ; Call the function (assume C naming)add esp, 12

在調用返回時,調用者必須清除棧中的相應內容,在上例中,參數占有12個字節,為了消除這些參數,只需將ESP加12即可。

?_myFunc的值保存在eax中,ecx和edx中的值也許已經被改變,調用者還必須在調用之前保存在棧中,并在調用結束之后,出棧恢復ecx和edx的值。

被調用者規則

被調用者應該遵循如下規則:

1)將ebp入棧,并將esp中的值拷貝到ebp中,其匯編代碼如下:

push ebpmov ebp, esp

上述代碼的目的是保存調用子程序之前的基址指針,基址指針用于尋找棧上的參數和局部變量。當一個子程序開始執行時,基址指針保存棧指針指示子程序的執行。為了在子程序完成之后調用者能正確定位調用者的參數和局部變量,ebp的值需要返回。

2)在棧上為局部變量分配空間。

3)保存callee-saved寄存器的值,callee-saved寄存器包括ebx,edi和esi,將ebx,edi和esi壓棧。

4)在上述三個步驟完成之后,子程序開始執行,當子程序返回時,必須完成如下工作:

  4.1)將返回的執行結果保存在eax中

  4.2)彈出棧中保存的callee-saved寄存器值,恢復callee-saved寄存器的值(ESI和EDI)

  4.3)收回局部變量的內存空間。實際處理時,通過改變EBP的值即可:mov?esp,?ebp。?

  4.4)通過彈出棧中保存的ebp值恢復調用者的基址寄存器值。

  4.5)執行ret指令返回到調用者程序。

After these three actions are performed, the body of the subroutine may proceed. When the subroutine is returns, it must follow these steps:

  • Leave the return value in EAX.
  • Example

    .486 .MODEL FLAT .CODE PUBLIC _myFunc _myFunc PROC; Subroutine Prologuepush ebp ; Save the old base pointer value.mov ebp, esp ; Set the new base pointer value.sub esp, 4 ; Make room for one 4-byte local variable.push edi ; Save the values of registers that the functionpush esi ; will modify. This function uses EDI and ESI.; (no need to save EBX, EBP, or ESP); Subroutine Bodymov eax, [ebp+8] ; Move value of parameter 1 into EAXmov esi, [ebp+12] ; Move value of parameter 2 into ESImov edi, [ebp+16] ; Move value of parameter 3 into EDImov [ebp-4], edi ; Move EDI into the local variableadd [ebp-4], esi ; Add ESI into the local variableadd eax, [ebp-4] ; Add the contents of the local variable; into EAX (final result); Subroutine Epilogue pop esi ; Recover register valuespop edimov esp, ebp ; Deallocate local variablespop ebp ; Restore the caller's base pointer valueret _myFunc ENDP END

    子程序首先通過入棧的手段保存ebp,分配局部變量,保存寄存器的值。

    在子程序體中,參數和局部變量均是通過ebp進行計算。由于參數傳遞在子程序被調用之前,所以參數總是在ebp指示的地址的下方(在棧中),因此,上例中的第一個參數的地址是ebp+8,第二個參數的地址是ebp+12,第三個參數的地址是ebp+16;而局部變量在ebp指示的地址的上方,所有第一個局部變量的地址是ebp-4,而第二個這是ebp-8.

    轉載于:https://www.cnblogs.com/timeObjserver/p/9455186.html

    總結

    以上是生活随笔為你收集整理的X86汇编概要的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。