X86-64從入門到放棄(一)
做實驗的時候總是忘記各種各樣的小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