https://www.toutiao.com/article/7287409808045818428/?log_from=cca2e23dd92df_1697203661621
(用objdump反匯編時(shí)可以把C代碼和匯編代碼穿插起來(lái)顯示這樣C代碼和匯編代碼的對(duì)應(yīng)關(guān)系看得更清楚)
(匯編語(yǔ)言編碼是ATT模式 可以手動(dòng)修改為intel風(fēng)格依個(gè)人喜好我個(gè)人更偏向intel風(fēng)格)
調(diào)用者把函數(shù)的參數(shù)按照調(diào)用約定壓?;虼鎯?chǔ)到寄存器中。
調(diào)用要使用的函數(shù),先把調(diào)用者的地址入棧,方便回來(lái)
跳轉(zhuǎn)到函數(shù)
把函數(shù)使用到的一些寄存器壓棧,避免修改寄存器的值
執(zhí)行函數(shù)
處理函數(shù)返回值
對(duì)于第4步中的壓棧的那些寄存器,恢復(fù)他們?cè)瓉?lái)的值
清空第一步中的壓棧參數(shù)和處理返回值
返回到調(diào)用者調(diào)用時(shí)的地址(步驟1已經(jīng)記錄)繼續(xù)往下執(zhí)行
系統(tǒng):centos 7,64位。64位基本使用寄存器存儲(chǔ)函數(shù)參數(shù),寄存器不夠才入棧。
32位使用棧幀來(lái)作為傳遞的參數(shù)的保存位置,而64位使用寄存器,分別用rdi, rsi, rdx, rcx, r8, r9作為第1-6個(gè)參數(shù),rax作為返回值。
用gdb調(diào)試,測(cè)試代碼如下:test.c
#include <stdio.h> int add(int a, int b, int c) { return (a + b + c); } int main() { int sum = 0; int a = 1; int b = 2; int c = 3; sum = add(a, b ,c); sum = sum + 1; return 0; }
編譯
在編譯時(shí)加上-g選項(xiàng),那么用objdump反匯編時(shí)可以把C代碼和匯編代碼穿插起來(lái)顯示,這樣C代碼和匯編代碼的對(duì)應(yīng)關(guān)系看得更清楚。
gcc test.c -g objdump -dS a.out
匯編后的代碼如下
00000000004004e9 <main>: int main() { 4004e9: push %rbp ; rbp入棧,即將調(diào)用main()的函數(shù)的棧底地址放入棧中 4004ea: mov %rsp, %rbp ; 將rsp的值給rbp,此時(shí)rbp指向新的棧底 4004ed: sub $0x10, %rsp ; rsp - 0x10,為main函數(shù)開辟空間 int sum = 0; 4004f1: c7 45 fc 00 }
gdb跟蹤,在main函數(shù)設(shè)置斷點(diǎn)
看當(dāng)前的匯編
默認(rèn)的匯編語(yǔ)言編碼是ATT模式。如果看不順的話,可以手動(dòng)修改為intel風(fēng)格,依個(gè)人喜好,我個(gè)人更偏向intel風(fēng)格
set disassembly-flavor intel
剛開始用到了rbp,rsp。那先重點(diǎn)關(guān)注rbp,rsp,cs,rip這四個(gè)寄存器
通過(guò)如下命令查看
info registers rbp rsp cs rip 或者 print $rbp
通過(guò)畫圖來(lái)分析棧里面當(dāng)前的數(shù)據(jù)
1,設(shè)置棧底和棧頂
00000000004004e9 <main>: int main() { 4004e9: push %rbp ; rbp入棧,即將調(diào)用main()的函數(shù)的棧底地址放入棧中 4004ea: mov %rsp, %rbp ; 將rsp的值給rbp,此時(shí)rbp指向新的棧底 4004ed: sub $0x10, %rsp ; rsp - 0x10,為main函數(shù)開辟空間
過(guò)程如圖
來(lái)看下舊rbp里面的值,通過(guò)x /nfu addr來(lái)查看內(nèi)存里面的值,如下
發(fā)現(xiàn)舊rbp為0x0,這里不做擴(kuò)展,繼續(xù)
2,局部變量入棧
匯編語(yǔ)句的單步調(diào)試(si 4),向前4步
代碼解釋
00000000004004e9 <main>: int main() { ... 4004f1: mov DWORD PTR [rbp-0x4], 0x0 ; sum入棧 4004f8: mov DWORD PTR [rbp-0x8], 0x1 ; a入棧 4004ff: mov DWORD PTR [rbp-0xc], 0x2 ; b入棧 400506: mov DWORD PTR [rbp-0x10], 0x3 ; c入棧 ...
此時(shí)如下圖
3,被調(diào)函數(shù)的參數(shù)儲(chǔ)存在寄存器中
00000000004004e9 <main>: int main() { ... mov edx, DWORD PTR [rbp-0x10] mov ecx, DWORD PTR [rbp-0xc] mov eax, DWORD PTR [rbp-0x8] ...
向前3步(si 3)
寄存器 | 值 |
edx | c(3) |
ecx | b(2) |
eax | a(1) |
00000000004004e9 <main>: int main() { ... mov esi, ecx mov edi, eax ...
寄存器 | 值 |
esi | ecx(b:2) |
edi | eax(a:1) |
4,跳轉(zhuǎn)函數(shù)
此時(shí)rip寄存器的值為:
執(zhí)行call語(yǔ)句后
call語(yǔ)句分解為:
push rip;
jump ip;
看各個(gè)寄存器的值
看rsp的值為:0x7fff,ffff,e4b8,看入棧的返回地址的值; rip 為0x4004cd
即0x40051f正好對(duì)應(yīng)于call下面的語(yǔ)句
5,執(zhí)行函數(shù),將main函數(shù)的棧底壓棧,并將rsp的值給rbp,此時(shí)rbp指向新的棧底
00000000004004cd <add>: int add(int a, int b, int c) { 4004cd: push rbp 4004ce: mov rbp, rsp ... }
rbp和rsp相等
6,將局部變量壓棧
00000000004004cd <add>: int add(int a, int b, int c) { ... 4004d1: mov DWORD PTR [rbp-0x4], edi 4004d4: mov DWORD PTR [rbp-0x8], esi 4004d7: mov DWORD PTR [rbp-0xc], edx ... }
7, 開始計(jì)算
00000000004004cd <add>: int add(int a, int b, int c) { ... 4004da: mov eax, DWORD PTR [rbp-0x8]; eax = b = 1 4004dd: mov edx, DWORD PTR [rbp-0x4]; edx = a = 2 4004e0: add edx, eax; edx = eax + edx = 3; 4004e2: mov eax, DWORD PTR [rbp-0xc]; eax = c = 3 4004e5: add eax, edx; eax = eax + edx = 6, 即返回值保存在寄存器eax中 ... }
8,恢復(fù)rbp
各個(gè)寄存器的值
00000000004004cd <add>: int add(int a, int b, int c) { ... 4004e7: pop rbp
9,回到main函數(shù)
00000000004004cd <add>: int add(int a, int b, int c) { ... 4004e8: ret
ret的作用是: pop rip
10,返回值賦值
00000000004004e9 <main>: int main() { ... sum = add(a, b, c) ... mov DWORD PTR [rbp-0x4], eax ; 將eax的值賦值給sum sum = sum + 1; add DWORD PTR [rbp-0x4], 0x1 ; sum += 1 return 0; mov eax, 0x0 ; 將返回值0通過(guò)eax傳遞 ...
11,執(zhí)行l(wèi)eave
執(zhí)行前
執(zhí)行后
rbp = 0
rsp = 0x7fff,ffff,e4d8
12,執(zhí)行ret,回到_libc_start_main函數(shù)里
(匯編語(yǔ)言編碼是ATT模式 可以手動(dòng)修改為intel風(fēng)格依個(gè)人喜好我個(gè)人更偏向intel風(fēng)格)
聯(lián)系客服