做實驗的時候總是忘記各種各樣的小tips,記錄下來時常複習。

X86-64從入門到放棄(一)暫存器與指令

X86-64從入門到放棄(二)迴圈與條件

工具

OBJDUMP

Objdump是用於檢視編譯後目標檔案組成的gcc工具。使用方法如下:

objdump [-引數] 可執行檔案的檔名

反編譯過於複雜的程式過程很複雜,不利於我們的學習,因此建議使用gcc將c程式編譯成可執行檔案後對其進行反編譯。

使用Objdump對可執行檔案反彙編,並輸出到一個檔案中:

objdump -d bomb_64 > bomb。txt

// > 是Linux的輸出重定向,將輸出內容放在檔案中

// -d 僅僅反編譯需要執行的指令

GDB

以前都是用gdb除錯C程式碼,我們也可以使用gdb除錯彙編程式碼:

gdb 可執行檔案的檔名

設定、執行方式和以前一樣:

(gdb) b 方法名或行數 // 打斷點

(gdb) r // 執行

執行到斷點後檢視程式碼和記憶體的命令稍有不同:

(gdb) disassemb // 檢視執行處的彙編程式碼

(gdb) x/[記憶體單元個數][顯示變數的方式][一個地址單元的長度] 地址

暫存器

一個x86-64的CPU包含16個64位的暫存器,允許操作暫存器的64位-低32位-低16位-低8位:

rsp

-esp-sp-spl : 棧指標

rax

-eax-ax-al : 返回值

rdi

-edi-di-dil : 第1個引數

rsi

-esi-si-sil : 第2個引數

rdx

-edx-dx-dxl : 第3個引數

rcx

-ecx-cx-cxl : 第4個引數

rbx

-ebx-bx-bl : 被呼叫者儲存

rbp

-ebp-bp-bpl : 被呼叫者儲存

r10

-r10d-r10w-r10b : 被呼叫者儲存

r11

-r11d-r11w-r11b : 被呼叫者儲存

r12

-r12d-r12w-r12b : 被呼叫者儲存

r13

-r13d-r13w-r13b : 被呼叫者儲存

r14

-r14d-r14w-r14b : 被呼叫者儲存

r15

-r15d-r15w-r15b : 被呼叫者儲存

例子:

mov %rdi,%rax

操作指示符

指令中的運算元,用於指示地址或根據地址取數:

$Imm : 運算元值為Imm

Imm : 運算元值為M[Imm]

r : 運算元值為R[r]

Imm(a,b,s) : 運算元值為M[Imm+R[a]+R[b]*s],其中Imm必須為常數,a/b必須為暫存器,Imm/a/b/s不必全部出現,s必須為1/2/4/8

例子:

mov 0x8(%rdi),%r8

指令

在指令後新增不同的字尾,可以操作暫存器中的不同位數:

b(位元組) : 後1位

w(字) : 後2位

l(雙字) : 後4位

q(四字) : 後8位

例子:

movb $0x0,0x16(%rsp)

簡單資料傳送

MOV S,D : 將S傳送到D

可以從暫存器到暫存器、記憶體到暫存器、從暫存器到記憶體,但是不能記憶體到記憶體。

可用指令有:

movb

movw

movl

movq

例子:

movq $0x0,0x8(%rdi)

拓展資料傳送

MOVZ S,R : 將S進行0擴充套件後傳送到R

可以從暫存器到暫存器、記憶體到暫存器、從暫存器到記憶體,但是不能記憶體到記憶體。

可用指令有:

movzbw

movzbl

movzwl

movzbq

movzwq

MOVS : 將S進行符號擴充套件後傳送到R

可用指令有:

movsbw

movsbl

movswl

movsbq

movswq

例子:

movzbl (%rbx),%eax

入棧和出棧

pushq S : 將四字從暫存器壓入記憶體的棧中,相當於

subq $8,%rsp

movq %rbp,(%rsp)

popq D : 將四字記憶體的棧中彈出到暫存器,相當於

movq (%rsp),%rbp

addq $8,%rsp

例子:

push %rbp

算數和邏輯操作

第一個運算元可以使立即數、暫存器或記憶體,第二個運算元可以使暫存器或記憶體。

INC D : D=D+1

DEC D : D=D-1

NEG D : D=-D

NOT D : D=~D

ADD S,D : D=D+S

SUB S,D : D=D-S

IMUL S,D : D=D*S

XOR S,D : D=D^S

OR S,D : D=D|S

AND S,D : D=D&S

SAL/SHL k,D : D=D<

SAR/SHR k,D : D=D>>k

可用指令有: incb/incw/incl/incq。。。等。

例子:

add $0x1,%rbp

載入有效地址

leaq S,D 計算S的地址,存入D中。

實際上是movq的變形,只計算地址而不取數。

例子:

lea (%rax,%rax,4),%rdi

設定條件碼

一個x86-64的CPU維護著4種條件碼:

CF 是否無符號溢位

ZF 是否為0

SF 是否為負

OF 是否有符號溢位

除了leaq指令外,所有指令執行後都會設定上述條件碼。

下面的指令只設置條件碼而不進行其他操作:

CMP S1,S2 基於S2-S1設定上述條件碼

TEST S1,S2 基於S2&S1設定上述條件碼

例子:

test %rax,%rax

訪問條件碼

可以使用下面的指令讀取條件碼,並將一個位元組設定為0/1:

sete D : D=ZF

setne D : D=~ZF

sets D : D=SF

setns D : D=~SF

。。。

跳轉指令

在透過label指定跳轉的目的地後,可以使用下面的指令直接或根據條件碼跳轉:

jmp Label 無條件跳轉

je/jz Label 當ZF等於1時跳轉

jne Label 當~ZF等於1時跳轉

js Label 當SF等於1時跳轉

jns Label 當~SF等於1時跳轉

jg Label 有符號大於0時跳轉

jge Label 有符號大於等於0時跳轉

jl Label 有符號小於0時跳轉

jle Label 有符號小於等於0時跳轉

。。。

例子:

jne 400d3d

。。。

400d3d: cmp $0x2,%edi