【逆向學習記錄】x86-64棧幀及跳轉

1.概述

32位寄存器的堆棧和64位的寄存器的函數堆棧,還是有較大的差別的,這裏準備花兩篇文章好好學習溫習一下,防止在不做技術的路上越忘越遠,因此儘量寫的詳細簡單,本文主要是64位寄存器的堆棧圖,調試系統爲ubuntu 16.04(64位)
具體堆棧圖的畫法,我是偶爾在網易雲課堂學習的一節課(堆棧圖),使用excel表格畫圖,也挺形象的,
64位的作圖,原則上要採用8個字節的,因爲要與上一篇文章進行對比,所以依然採用的是4個字節(int型的數據)

2.源代碼及其彙編

源代碼

#include<stdio.h>
int sum(int a,int b) {
    return a+b;
}
int main(){
    int i = 1;
    int j = 2;
    int k = 0;
    k = sum(i,j);
    printf("%d",k);
    return 0;
}

彙編代碼

    0x000000000040053a <+0>:     push   rbp
    0x000000000040053b <+1>:     mov    rbp,rsp
    0x000000000040053e <+4>:     sub    rsp,0x10
    0x0000000000400542 <+8>:     mov    DWORD PTR [rbp-0xc],0x1
    0x0000000000400549 <+15>:    mov    DWORD PTR [rbp-0x8],0x2
    0x0000000000400550 <+22>:    mov    DWORD PTR [rbp-0x4],0x0
    0x0000000000400557 <+29>:    mov    edx,DWORD PTR [rbp-0x8]
    0x000000000040055a <+32>:    mov    eax,DWORD PTR [rbp-0xc]
    0x000000000040055d <+35>:    mov    esi,edx
    0x000000000040055f <+37>:    mov    edi,eax
    0x0000000000400561 <+39>:    call   0x400526 <sum>// 將sum函數拆解
        0x0000000000400526 <+0>:     push   rbp
        0x0000000000400527 <+1>:     mov    rbp,rsp
        0x000000000040052a <+4>:     mov    DWORD PTR [rbp-0x4],edi
        0x000000000040052d <+7>:     mov    DWORD PTR [rbp-0x8],esi
        0x0000000000400530 <+10>:    mov    edx,DWORD PTR [rbp-0x4]
        0x0000000000400533 <+13>:    mov    eax,DWORD PTR [rbp-0x8]
        0x0000000000400536 <+16>:    add    eax,edx
        0x0000000000400538 <+18>:    pop    rbp
        0x0000000000400539 <+19>:    ret
    0x0000000000400566 <+44>:    mov    DWORD PTR [rbp-0x4],eax
    0x0000000000400569 <+47>:    mov    eax,DWORD PTR [rbp-0x4]
    0x000000000040056c <+50>:    mov    esi,eax
    0x000000000040056e <+52>:    mov    edi,0x400614
    0x0000000000400573 <+57>:    mov    eax,0x0
    0x0000000000400578 <+62>:    call   0x400400 <printf@plt>
    0x000000000040057d <+67>:    mov    eax,0x0
    0x0000000000400582 <+72>:    leave
    0x0000000000400583 <+73>:    ret

3.堆棧圖(64位)

3.1開始執行main函數

gdb調試結果在這裏插入圖片描述

堆棧圖

其中:0x400590 (<__libc_csu_init>: push r15)這個值執行main函數之前的rp

在這裏插入圖片描述

3.2函數跳轉之前的棧分佈

參數傳遞之前,將參數保存在寄存器:RDI/RCI

gdb調試結果

在這裏插入圖片描述

堆棧圖

在這裏插入圖片描述

3.3執行call指令之後

gdb調試結果

在這裏插入圖片描述

堆棧圖

通過這個堆棧圖可以看到,執行call指令的時候,將RIP壓棧(main的下一條指令),同時保存RBP
在這裏插入圖片描述

3.4執行SUM函數的過程

gdb調試結果

在這裏插入圖片描述

堆棧圖

在這裏插入圖片描述
通過查看堆棧情況,這裏有個比較奇怪的地方,上面兩個內存沒有申請,竟然在使用:
在這裏插入圖片描述

3.5 執行完成子函數,跳轉回main函數

gdb調試結果

在這裏插入圖片描述

堆棧圖

在這裏插入圖片描述

4總結

1 注意點:

push操作會導致ESP-1(8個字節)
pop操作會導致ESP+1(8個字節)
64位下的指針是8個字節,但是int是4個字節,因此賦值採用的是edx,eax
有很多文章說說ebp不再作爲棧幀指針,是存在一定的偏差的,實際linux-64中依然是使用rbp作爲棧幀指針,待進一步研究調查

2 call指令:

push rip
函數跳轉之後會導致
push rbp
mov rbp,rsp

3 Ret指令:

pop rip

4 函數傳參和32位不同:

32位是通過堆棧傳參
64位是通過寄存器+堆棧傳參,順序依次是:rdi、rsi、rdx、rcx、r8、r9(edi、esi、edx、ecx、r8、r9)

5奇怪的點:

跳轉到新的函數之後,並沒有分配新的內存,但是程序卻臨時借用了就近的幾個字節

5參考

64位和32位的寄存器和彙編的比較
上面這篇文章實際操作過程中,會有不相符的情況,比如rbp棧幀指針
x86-64傳參規則
x64函數調用過程分析

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章