【反匯編玩耍1】通過反匯編真正理解函數參數傳遞過程


過去我一直以為,函數參數就只是通過棧傳遞的,最近看一些反匯編代碼,發現好多時候都是通過寄存器。做了個實驗,最終明白了,函數的參數怎樣傳遞,其實是和參數個數有關。

在linux下的objdump反匯編的結果:如果參數很多,有一些參數會通過棧傳遞,否則通過寄存器。

在windows下的VS反匯編的結果:都通過棧。

 

C代碼:

#include <stdio.h>

void func1(int a, int b)
{
    printf("%d", a+b);
}

void func2(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k)
{
    printf("%d",a+b+c+d+e+f+g+h+i+j+k);
}

void main ()
{
    int a = 1;
    int b = 1;
    int c = 1;
    int d = 1;
    int e = 1;
    int f = 1;
    int g = 1;
    int h = 1;
    int i = 1;
    int j = 1;
    int k = 1;
    func1(a, b);
    func2(a, b, c, d, e, f, g, h, i, j, k);
}

 

linux下通過objdump反匯編:

[root@VM_120_194_centos C]# gcc -E 20170704.c -o 20170704.i
[root@VM_120_194_centos C]# gcc -S 20170704.i -o 20170704.s
[root@VM_120_194_centos C]# gcc -c 20170704.s -o 20170704.o
[root@VM_120_194_centos C]# objdump -S 20170704.o

20170704.o:     文件格式 elf64-x86-64


Disassembly of section .text:

0000000000000000 <func1>:
   0:    55                       push   %rbp
   1:    48 89 e5                 mov    %rsp,%rbp
   4:    48 83 ec 10              sub    $0x10,%rsp
   8:    89 7d fc                 mov    %edi,-0x4(%rbp)
   b:    89 75 f8                 mov    %esi,-0x8(%rbp)
   e:    8b 45 f8                 mov    -0x8(%rbp),%eax
  11:    8b 55 fc                 mov    -0x4(%rbp),%edx
  14:    01 d0                    add    %edx,%eax
  16:    89 c6                    mov    %eax,%esi
  18:    bf 00 00 00 00           mov    $0x0,%edi
  1d:    b8 00 00 00 00           mov    $0x0,%eax
  22:    e8 00 00 00 00           callq  27 <func1+0x27>
  27:    c9                       leaveq 
  28:    c3                       retq   

0000000000000029 <func2>:
  29:    55                       push   %rbp
  2a:    48 89 e5                 mov    %rsp,%rbp
  2d:    48 83 ec 20              sub    $0x20,%rsp
  31:    89 7d fc                 mov    %edi,-0x4(%rbp)
  34:    89 75 f8                 mov    %esi,-0x8(%rbp)
  37:    89 55 f4                 mov    %edx,-0xc(%rbp)
  3a:    89 4d f0                 mov    %ecx,-0x10(%rbp)
  3d:    44 89 45 ec              mov    %r8d,-0x14(%rbp)
  41:    44 89 4d e8              mov    %r9d,-0x18(%rbp)
  45:    8b 45 f8                 mov    -0x8(%rbp),%eax
  48:    8b 55 fc                 mov    -0x4(%rbp),%edx
  4b:    01 c2                    add    %eax,%edx
  4d:    8b 45 f4                 mov    -0xc(%rbp),%eax
  50:    01 c2                    add    %eax,%edx
  52:    8b 45 f0                 mov    -0x10(%rbp),%eax
  55:    01 c2                    add    %eax,%edx
  57:    8b 45 ec                 mov    -0x14(%rbp),%eax
  5a:    01 c2                    add    %eax,%edx
  5c:    8b 45 e8                 mov    -0x18(%rbp),%eax
  5f:    01 c2                    add    %eax,%edx
  61:    8b 45 10                 mov    0x10(%rbp),%eax
  64:    01 c2                    add    %eax,%edx
  66:    8b 45 18                 mov    0x18(%rbp),%eax
  69:    01 d0                    add    %edx,%eax
  6b:    89 c6                    mov    %eax,%esi
  6d:    bf 00 00 00 00           mov    $0x0,%edi
  72:    b8 00 00 00 00           mov    $0x0,%eax
  77:    e8 00 00 00 00           callq  7c <func2+0x53>
  7c:    c9                       leaveq 
  7d:    c3                       retq   

000000000000007e <main>:
  7e:    55                       push   %rbp
  7f:    48 89 e5                 mov    %rsp,%rbp
  82:    48 83 ec 60              sub    $0x60,%rsp
  86:    c7 45 fc 01 00 00 00     movl   $0x1,-0x4(%rbp)
  8d:    c7 45 f8 01 00 00 00     movl   $0x1,-0x8(%rbp)
  94:    c7 45 f4 01 00 00 00     movl   $0x1,-0xc(%rbp)
  9b:    c7 45 f0 01 00 00 00     movl   $0x1,-0x10(%rbp)
  a2:    c7 45 ec 01 00 00 00     movl   $0x1,-0x14(%rbp)
  a9:    c7 45 e8 01 00 00 00     movl   $0x1,-0x18(%rbp)
  b0:    c7 45 e4 01 00 00 00     movl   $0x1,-0x1c(%rbp)
  b7:    c7 45 e0 01 00 00 00     movl   $0x1,-0x20(%rbp)
  be:    c7 45 dc 01 00 00 00     movl   $0x1,-0x24(%rbp)
  c5:    c7 45 d8 01 00 00 00     movl   $0x1,-0x28(%rbp)
  cc:    c7 45 d4 01 00 00 00     movl   $0x1,-0x2c(%rbp)
  d3:    8b 55 f8                 mov    -0x8(%rbp),%edx
  d6:    8b 45 fc                 mov    -0x4(%rbp),%eax
  d9:    89 d6                    mov    %edx,%esi
  db:    89 c7                    mov    %eax,%edi
  dd:    e8 00 00 00 00           callq  e2 <main+0x64>
  e2:    44 8b 4d e8              mov    -0x18(%rbp),%r9d
  e6:    44 8b 45 ec              mov    -0x14(%rbp),%r8d
  ea:    8b 4d f0                 mov    -0x10(%rbp),%ecx
  ed:    8b 55 f4                 mov    -0xc(%rbp),%edx
  f0:    8b 75 f8                 mov    -0x8(%rbp),%esi
  f3:    8b 45 fc                 mov    -0x4(%rbp),%eax
  f6:    8b 7d d4                 mov    -0x2c(%rbp),%edi
  f9:    89 7c 24 20              mov    %edi,0x20(%rsp)
  fd:    8b 7d d8                 mov    -0x28(%rbp),%edi
 100:    89 7c 24 18              mov    %edi,0x18(%rsp)
 104:    8b 7d dc                 mov    -0x24(%rbp),%edi
 107:    89 7c 24 10              mov    %edi,0x10(%rsp)
 10b:    8b 7d e0                 mov    -0x20(%rbp),%edi
 10e:    89 7c 24 08              mov    %edi,0x8(%rsp)
 112:    8b 7d e4                 mov    -0x1c(%rbp),%edi
 115:    89 3c 24                 mov    %edi,(%rsp)
 118:    89 c7                    mov    %eax,%edi
 11a:    e8 00 00 00 00           callq  11f <main+0xa1>
 11f:    c9                       leaveq 
 120:    c3                       retq   

func1和func2兩個函數的反匯編代碼不用管,我們看main函數里的。可以清晰看到,86 - cc是給變量(棧空間內存)賦值的過程,也就是

int a = 1;int b = 1;int c = 1;int d = 1;int e = 1;int f = 1;int g = 1;int h = 1;int i = 1;int j = 1;int k = 1;

d3 - dd是func1調用的過程。需要兩個參數,賦值給edi和esi寄存器。也就是說,這里函數參數是存在寄存器里的。

 

func1里面,則是先將edi和esi的內容copy到自己的棧空間。這就是C語言書里說的所謂“值傳遞”吧?? 也不知道誰發明的這種詞。以匯編視角看,就是內存的copy而已。

 

func1的調用是,main函數的e2 - 11a,這個很有趣,前一部分變量是存在寄存器里(和上面講的一個道理),而寄存器不夠的時候(參數太多了),就通過rsp棧指針寄存器,直接存到下一個要調用的函數的棧空間里面。好有趣。

 

windows下VS反匯編代碼:

void main ()
{
00BF1540  push        ebp  
00BF1541  mov         ebp,esp  
00BF1543  sub         esp,144h  
00BF1549  push        ebx  
00BF154A  push        esi  
00BF154B  push        edi  
00BF154C  lea         edi,[ebp-144h]  
00BF1552  mov         ecx,51h  
00BF1557  mov         eax,0CCCCCCCCh  
00BF155C  rep stos    dword ptr es:[edi]  
    int a = 1;
00BF155E  mov         dword ptr [a],1  
    int b = 1;
00BF1565  mov         dword ptr [b],1  
    int c = 1;
00BF156C  mov         dword ptr [c],1  
    int d = 1;
00BF1573  mov         dword ptr [d],1  
    int e = 1;
00BF157A  mov         dword ptr [e],1  
    int f = 1;
00BF1581  mov         dword ptr [f],1  
    int g = 1;
00BF1588  mov         dword ptr [g],1  
    int h = 1;
00BF158F  mov         dword ptr [h],1  
    int i = 1;
00BF1596  mov         dword ptr [i],1  
    int j = 1;
00BF159D  mov         dword ptr [j],1  
    int k = 1;
00BF15A4  mov         dword ptr [k],1  
    func1(a, b);
00BF15AB  mov         eax,dword ptr [b]  
00BF15AE  push        eax  
00BF15AF  mov         ecx,dword ptr [a]  
00BF15B2  push        ecx  
00BF15B3  call        func1 (0BF11F4h)  
00BF15B8  add         esp,8  
    func2(a, b, c, d, e, f, g, h, i, j, k);
00BF15BB  mov         eax,dword ptr [k]  
00BF15BE  push        eax  
00BF15BF  mov         ecx,dword ptr [j]  
00BF15C2  push        ecx  
00BF15C3  mov         edx,dword ptr [i]  
00BF15C6  push        edx  
00BF15C7  mov         eax,dword ptr [h]  
00BF15CA  push        eax  
00BF15CB  mov         ecx,dword ptr [g]  
00BF15CE  push        ecx  
00BF15CF  mov         edx,dword ptr [f]  
00BF15D2  push        edx  
00BF15D3  mov         eax,dword ptr [e]  
00BF15D6  push        eax  
00BF15D7  mov         ecx,dword ptr [d]  
00BF15DA  push        ecx  
00BF15DB  mov         edx,dword ptr [c]  
00BF15DE  push        edx  
00BF15DF  mov         eax,dword ptr [b]  
00BF15E2  push        eax  
00BF15E3  mov         ecx,dword ptr [a]  
00BF15E6  push        ecx  
00BF15E7  call        func2 (0BF11FEh)  
00BF15EC  add         esp,2Ch  
}
00BF15EF  xor         eax,eax  
00BF15F1  pop         edi  
00BF15F2  pop         esi  
00BF15F3  pop         ebx  
00BF15F4  add         esp,144h  
00BF15FA  cmp         ebp,esp  
00BF15FC  call        __RTC_CheckEsp (0BF1140h)  
00BF1601  mov         esp,ebp  
00BF1603  pop         ebp  
00BF1604  ret  

dword ptr [x]是指取標號為x,地址內的值,大小是雙字double word。

這里代碼清晰的多,看到push指令就明白了。

可以看出,這里反匯編代碼,不管參數多少,都是通過棧來傳參的。

 

至於【在linux下的objdump反匯編的結果】與【在windows下的VS反匯編的結果】為何有這種差別?我就不清楚了。

 

具體區別也有:

                         (1)為何VS下反匯編函數調用時,固定要將ebx、esi、edi寄存器入棧(函數運行結束再pop),不管有沒有必要。而linux objdump反匯編就沒有這個過程,可能這個程序這樣做沒有意義就把這過程優化了吧。

                         (2)為何VS下反匯編是通過push指令入棧,而linux objdump是通過操作rsp棧指針入棧。雖然作用都是一樣的。

 

其實二者都是大同小異了。不管是通過寄存器傳遞,還是通過棧傳遞,最后被調用函數內部都是只用棧內存來存參數的。

 

附一張之前畫的函數調用棧內存開辟、回收過程示意圖:

這張圖有一個沒描繪到的部分,就是ip的入棧,是通過call指令達到的。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM