天天看點

X86函數記憶體棧

       本文描述的是在intel X86_64體系架構中,C語言函數在執行過程中的記憶體棧結構情況。其它體系會有所不同,僅供參考,實驗在Ubuntu14.04 X64中使用,考慮到寄存器的熟悉程度,編譯的可執行程式是32位的。

       源碼如下:

#include<stdio.h>
void func(int a, int b)
{
    int sec[5];
    sec[2] = a;
    return;
}
int main()
{
    func(0xFF, 0x02);
    return 0;
}
           

            編譯源碼 gcc -win32 func.c -o func

      執行gdb調用可執行程式 gdb func

      使用gdb反彙編func和main函數

      func:

(gdb) disas func
Dump of assembler code for function func:
   0x080483ed <+0>:	push   %ebp
   0x080483ee <+1>:	mov    %esp,%ebp
   0x080483f0 <+3>:	sub    $0x20,%esp
   0x080483f3 <+6>:	mov    0x8(%ebp),%eax
   0x080483f6 <+9>:	mov    %eax,-0xc(%ebp)
   0x080483f9 <+12>:	nop
   0x080483fa <+13>:	leave  
   0x080483fb <+14>:	ret    
End of assembler dump.
           

      main:

(gdb) disas main
Dump of assembler code for function main:
   0x080483fc <+0>:	push   %ebp
   0x080483fd <+1>:	mov    %esp,%ebp
   0x080483ff <+3>:	sub    $0x8,%esp
   0x08048402 <+6>:	movl   $0x2,0x4(%esp)
   0x0804840a <+14>:	movl   $0xff,(%esp)
   0x08048411 <+21>:	call   0x80483ed <func>
   0x08048416 <+26>:	mov    $0x0,%eax
   0x0804841b <+31>:	leave  
   0x0804841c <+32>:	ret    
End of assembler dump.
           

      在函數func添加斷點 (gdb)b func,使用gdb執行程式(gdb) r,檢視此刻寄存器的值。

Breakpoint 1, 0x080483f3 in func ()
(gdb) i r
eax            0x1	1
ecx            0xd14b7383	-783584381
edx            0xffffd104	-12028
ebx            0xf7fbf000	-134483968
esp            0xffffd0a8	0xffffd0a8
ebp            0xffffd0c8	0xffffd0c8
esi            0x0	0
edi            0x0	0
eip            0x80483f3	0x80483f3 <func+6>
eflags         0x282	[ SF IF ]
cs             0x23	35
ss             0x2b	43
ds             0x2b	43
es             0x2b	43
fs             0x0	0
gs             0x63	99
           

        因為函數棧位址是逐漸減小的,此時檢視ebp位址8個32位整數值。

(gdb) x /10x 0xffffd0c8
0xffffd0c8:	0xffffd0d8	0x08048416	0x000000ff	0x00000002
0xffffd0d8:	0x00000000	0xf7e31a63	0x00000001	0xffffd174
           

        從ebp所存位址儲存的資料可以看到:

        1、參數1 0x000000ff在offset 2上,參數2 0x00000002在offset 3上,由此可見函數的參數是從右向左入棧,是以如果函數參數中有表達式時,也會先執行右邊的參數;

        2、offset 1 存放的值是0x08048416,可以從main的反彙編代碼中看見,該值為func函數傳回要執行指令的位址,即offset 1存放的是函數傳回位址。

        3、offset 0 存放的值是0xffffd0d8,該值在文中沒有顯示可查處,請讀者可以自行測試,可以在執行main函數時檢視寄存器ebp的值,即ebp存放值指向位址對應的值是主調函數的ebp。

        4、從操作 

0x080483f3 <+6>:	mov    0x8(%ebp),%eax
0x080483f6 <+9>:	mov    %eax,-0xc(%ebp)
           

        可以看出,局部資料sec是從位址ebp開始申請,是以局部變量是儲存在函數棧中。

        OK,綜上所述,ebp向下位置存放函數傳回位址,再向下是函數使用的參數值,而ebp存放值是主調函數的ebp,可以得出下圖的結果,請将func抽象為被調函數,main抽象為主調函數。

X86函數記憶體棧

繼續閱讀