【反汇编玩耍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