zoukankan      html  css  js  c++  java
  • 【反汇编玩耍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指令达到的。

  • 相关阅读:
    day 66 ORM django 简介
    day 65 HTTP协议 Web框架的原理 服务器程序和应用程序
    jQuery的事件绑定和解绑 事件委托 轮播实现 jQuery的ajax jQuery补充
    background 超链接导航栏案例 定位
    继承性和层叠性 权重 盒模型 padding(内边距) border(边框) margin 标准文档流 块级元素和行内元素
    属性选择器 伪类选择器 伪元素选择器 浮动
    css的导入方式 基础选择器 高级选择器
    03-body标签中相关标签
    Java使用内存映射实现大文件的上传
    正则表达式
  • 原文地址:https://www.cnblogs.com/rixiang/p/7116589.html
Copyright © 2011-2022 走看看