X64 彙編入門

目錄

前言

64位和32位彙編的差異

示例說明

 

前言

公司項目需要實現通過彙編來獲取調用棧的功能,自己寫了一個,直接崩潰,回頭學習一下,以此爲記;

64位和32位彙編的差異

這方面我現在涉及的不多,就不展開了,大家可以自己去查一下資料,或者直接參考一下下面的鏈接:

https://blog.csdn.net/qq_29343201/article/details/51278798

示例說明

重頭戲來了,先上C源碼:

#include <stdio.h>
#include <stdlib.h>
//gcc test.c -o test -g

int SumFunc(int a, int b)
{
	int sum = 0;
	sum = a + b;
	return sum;
}

int main(int argc, char* argv[])
{
    int a = 3;
    int b = 4;
    int sum = 0;
    sum = SumFunc(a, b);
    printf("sum = %d\n",sum);

    return 0;
}

編譯後,通過objdump看一下彙編代碼:

注意AT&T和Intel的彙編的差異,後面不再說了;

開始GDB之:

前面幾步沒啥好說的,主要是關注RBP、RSP、RIP這3個,然後直接到關鍵點:

輸入si[注意一定是si,不能是ni],執行單條彙編命令跳入函數,也就是執行call 0x40052d,注意RSP、RIP以及stack的變化:

發生變化的寄存器:

1、RIP

變化是正常的,RIP每一步都會變換;但是注意SumFunc裏的push rbp還沒有執行——這是下一個要執行的指令——那麼變化了的RIP到底執行了什麼?

2、RSP

原來:

現在:

——這裏有點奇怪!

首先看一下RSP當前指向的是啥:

可以看到,RSP入棧了main函數裏call SumFunc後的下一條指令——這就很清楚了,這是保護現場,用於在SumFunc完成調用後繼續執行main流程的下一步;

所以,RSP減少了 0x7fffffffe170 - 0x7fffffffe168 = 8[這裏注意不是10-8=2,而是(F+1)-8 = 8],就是RIP寄存器的大小(8字節),等於是把一條彙編指令入棧;

同時可以得到這個結論:

在彙編代碼裏,call 一個函數的時候,其實是調用了 push RIP;

下面繼續:

從上面的截圖也可以看到,進入SumFunc後,前兩條彙編是:

一般進入每個函數的開始都是這兩條;下面來分析一下這兩條指令做了什麼;

1. push rbp

很明顯,就是把rbp入棧;rbp裏保存的是什麼呢?

rbp指向的是:

從開始到現在,rbp的值還沒有變化過,用下面的圖可以簡單表示:

——上面這個截圖有個錯誤,就是edi應該是佔4個字節,rdi纔是8字節,edi是指rdi的低32位;

在進入SumFunc時,執行第一句 push rbp前,當前的棧,以及各個寄存器的情況:

現在我們執行push rbp:

可以發現,入棧後,rbp的值沒有變化,還是190,而rsp的值又減小了8——符合預期,現在的棧的情況:

在GDB調試界面上輸入ni,執行mov rbp, rsp,就是把rsp的值賦給rbp,rsp的值不發生變化,而rbp從原來的190變爲160:

然後繼續執行,下面兩句彙編是從edi和esi中獲取入參,放入rbp相應的位置上,供後面相加使用:

——這裏有一點需要注意:在64位彙編中,當參數少於7個時, 參數從左到右放入寄存器: rdi, rsi, rdx, rcx, r8, r9。在本例中就是:

簡單來說就是把原來存儲入參的寄存器的值轉移到eax和edx中,然後進行add的操作;在執行完add操作後,會把sum寫入到rbp-0x4中;

再執行pop rbp的操作,在上述過程中,rsp和rbp一直沒有發生變化,都是160,並且rbp在rsp的棧頂:

 

rbp和rsp均發生了變化:

1.rsp:

pop是做出棧的動作,所以rsp應該是增加8,變爲168

2.rbp:

pop rbp,對於rbp而言,應該是rbp = pop(rsp),等於是把棧頂的值賦給rbp

這裏注意:0x7fffffffe160是棧頂的地址,其對應的值是0x7fffffffe190,所以pop後,rbp的值是0x7fffffffe190;

 

再執行ret,回到main函數內,注意rsp和rip的變化

可以看到rsp做了pop的動作,並且把pop的值賦給了rip,所以通過這裏可以得到結論:

ret ====> pop rip

接下來就是調用printf函數打印的過程了,中間涉及一點,就是調用SumFunc時,返回的返回值是保存在eax裏的——64位彙編裏,函數返回值結果一般保存在rax或者eax(rax的低32位)中;

另外需要注意,在3和4時,rbp已經發生了變化,但對整個分析沒有影響,因爲結果是放在eax中的,使用eax進行函數結果的返回;

而edi中存儲的則是printf函數的第一個參數:

至此,整個過程可以說告一段落;

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