反汇编之函数调用

10 篇文章 0 订阅
首先提出2个问题:
1.函数调用时,参数是怎样传递的?
2.函数调用时,函数内部的局部变量和函数的参数在栈中是如何布局的?

参考资料:
1. AT&T与Intel汇编语言的比较
2. C++反汇编代码分析–函数调用
3. 栈溢出
4. 常见寄存器名称及其作用

目前得出的结论:
1.从函数A中调用函数B之前,如果参数少的话(少于cpu一些可用的通用寄存器个数)则会把参数放到寄存器里,如果多的话,会把参数放到函数A所在的栈的内存里。
2.进入函数B时,会把函数A的栈的偏移地址sp作为函数B的栈的栈基址。
3.进入函数后,会分配一些内存空间来存放函数用到的局部变量,这个预分配的空间我暂时不知道是什么规则,反正目前看不会把整个预分配的空间占满。进入函数后会把EDI和ESI寄存器保存在这段空间中。
4.函数调用结束后,会把结果放在EAX寄存器中!
5.函数在处理参数和局部变量时,并不一定是使用push,像我下面的实验那样,是直接mov修改内存的,所以存放顺序与编译器有关系!
6.大概的内存布局如下:
高地址   
-----------函数A--
        ......
        |      参数n         |
        |      参数n-1      |
        ......
        |      参数1         |
        ......
------------------------------------进入函数B----
        |     rbp              |
                                   <--函数B的栈基址      rbp
        |     局部变量n   |
        ......
        |     局部变量1    |
        ......
        |     ESI               |
        ......
        |    EDI               |
        ......
---------------------<--函数的栈的偏移地址 rsp
低地址



下面是centos 64位下的实验:

#t.c
int t(int i,int j,int k){
  int sum;
  sum = i+j+k;
  return sum;
}
int main(int argc,char *argv[]){
  int i,j,sum;
  i=1;
  j=2;
  sum = t(i,j,3);
  return sum;
}
gcc -g t.c
gdb ./a.out
(gdb) disass main
Dump of assembler code for function main:
0x0000000000400466 <main+0>:    push   %rbp              //函数调用时,首先是保存前一个栈的基址,
0x0000000000400467 <main+1>:    mov    %rsp,%rbp         //接着把之前的栈偏移地址作为新的栈基址,
                                                         //这个过程与最后的 leaveq和retq对应。
0x000000000040046a <main+4>:    sub    $0x20,%rsp        //为什么是申请32个字节呢?函数里声明的变量共加起来也就只有4字节*3=12字节,
                                                         //加上一些存寄存器的,也到不了32字节啊,这里猜想,编译器为函数调用
                                                         //预分配的内存空间应该是某个长度的倍数,我用的centos64位的gcc,
0x000000000040046e <main+8>:    mov    %edi,-0x14(%rbp)  //调用函数的时候,如果参数少的话,会使用寄存器edi,rsi之类的
                                                         //寄存器来传参数,所以这里会保存原来寄存器的值。
0x0000000000400471 <main+11>:   mov    %rsi,-0x20(%rbp)
0x0000000000400475 <main+15>:   movl   $0x1,-0xc(%rbp)
0x000000000040047c <main+22>:   movl   $0x2,-0x8(%rbp)
0x0000000000400483 <main+29>:   mov    -0x8(%rbp),%esi
0x0000000000400486 <main+32>:   mov    -0xc(%rbp),%edi
0x0000000000400489 <main+35>:   mov    $0x3,%edx
0x000000000040048e <main+40>:   callq  0x400448 <t>
0x0000000000400493 <main+45>:   mov    %eax,-0x4(%rbp)
0x0000000000400496 <main+48>:   mov    -0x4(%rbp),%eax   //函数调用后的结果是放在EAX中的,这点非常重要!
0x0000000000400499 <main+51>:   leaveq                   //leaveq首先会把现在的栈基址赋值给rsp,也就是恢复原来的栈的
                                                         //偏移地址,然后pop rbp,恢复原来的栈基址。
0x000000000040049a <main+52>:   retq                     //返回调用函数前的下一条指令的地址。
End of assembler dump.

(gdb) disassemble t
Dump of assembler code for function t:                   //下面这段代码与main类似
0x0000000000400448 <t+0>:       push   %rbp
0x0000000000400449 <t+1>:       mov    %rsp,%rbp
0x000000000040044c <t+4>:       mov    %edi,-0x14(%rbp)
0x000000000040044f <t+7>:       mov    %esi,-0x18(%rbp)
0x0000000000400452 <t+10>:      mov    %edx,-0x1c(%rbp)
0x0000000000400455 <t+13>:      mov    -0x18(%rbp),%eax
0x0000000000400458 <t+16>:      add    -0x14(%rbp),%eax
0x000000000040045b <t+19>:      add    -0x1c(%rbp),%eax
0x000000000040045e <t+22>:      mov    %eax,-0x4(%rbp)
0x0000000000400461 <t+25>:      mov    -0x4(%rbp),%eax
0x0000000000400464 <t+28>:      leaveq 
0x0000000000400465 <t+29>:      retq   
End of assembler dump.

下面把上面的代码改改,把函数的参数改为9个:
int t(int i,int j,int k,int l,int n,int m,int o,int p,int q){
  int sum;
  sum = i+j+k;
  return sum;
}
int main(int argc,char *argv[]){
  int i,sum,j;
  long k;
  i=1;
  j=2;
  sum = t(i,j,3,4,5,6,7,8,9);
  return sum;
}

(gdb) disass main
Dump of assembler code for function main:
0x0000000000400471 <main+0>:    push   %rbp
0x0000000000400472 <main+1>:    mov    %rsp,%rbp
0x0000000000400475 <main+4>:    sub    $0x48,%rsp
0x0000000000400479 <main+8>:    mov    %edi,-0x24(%rbp)
0x000000000040047c <main+11>:   mov    %rsi,-0x30(%rbp)
0x0000000000400480 <main+15>:   movl   $0x1,-0x14(%rbp)
0x0000000000400487 <main+22>:   movl   $0x2,-0xc(%rbp)
0x000000000040048e <main+29>:   mov    -0xc(%rbp),%esi
0x0000000000400491 <main+32>:   mov    -0x14(%rbp),%edi
0x0000000000400494 <main+35>:   movl   $0x9,0x10(%rsp)
0x000000000040049c <main+43>:   movl   $0x8,0x8(%rsp)
0x00000000004004a4 <main+51>:   movl   $0x7,(%rsp)
0x00000000004004ab <main+58>:   mov    $0x6,%r9d           //看到这里,我服了,去查了下,
                                                           //竟然64位下还多了N个通用寄存器,直接看下一段实验吧...
0x00000000004004b1 <main+64>:   mov    $0x5,%r8d
0x00000000004004b7 <main+70>:   mov    $0x4,%ecx
0x00000000004004bc <main+75>:   mov    $0x3,%edx
0x00000000004004c1 <main+80>:   callq  0x400448 <t>
0x00000000004004c6 <main+85>:   mov    %eax,-0x10(%rbp)
0x00000000004004c9 <main+88>:   mov    -0x10(%rbp),%eax
0x00000000004004cc <main+91>:   leaveq 
0x00000000004004cd <main+92>:   retq   
End of assembler dump.
(gdb) disass t
Dump of assembler code for function t:
0x0000000000400448 <t+0>:       push   %rbp
0x0000000000400449 <t+1>:       mov    %rsp,%rbp
0x000000000040044c <t+4>:       mov    %edi,-0x14(%rbp)
0x000000000040044f <t+7>:       mov    %esi,-0x18(%rbp)
0x0000000000400452 <t+10>:      mov    %edx,-0x1c(%rbp)
0x0000000000400455 <t+13>:      mov    %ecx,-0x20(%rbp)
0x0000000000400458 <t+16>:      mov    %r8d,-0x24(%rbp)
0x000000000040045c <t+20>:      mov    %r9d,-0x28(%rbp)
0x0000000000400460 <t+24>:      mov    -0x18(%rbp),%eax
0x0000000000400463 <t+27>:      add    -0x14(%rbp),%eax
0x0000000000400466 <t+30>:      add    -0x1c(%rbp),%eax
0x0000000000400469 <t+33>:      mov    %eax,-0x4(%rbp)
0x000000000040046c <t+36>:      mov    -0x4(%rbp),%eax
0x000000000040046f <t+39>:      leaveq 
0x0000000000400470 <t+40>:      retq   
End of assembler dump.

实在不服!我把参数搞为20个试试!
int t(int i,int j,int k,int l,int n,int m,int o,int p,int q,int qa,int qb,int qc,
      int qd,int qe,int qf,int qg,int qh,int qi,int qj,int qk){
  int sum;
  sum = i+j+k+l+n+m+o+p+q+qa+qb+qc+qd+qe+qf+qg+qh+qi+qj+qk;
  return sum;
}
int main(int argc,char *argv[]){
  int i,sum,j;
  long k;
  i=1;
  j=2;
  sum = t(i,j,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20);
  return sum;
}

(gdb) disass main
Dump of assembler code for function main:
0x00000000004004a4 <main+0>:    push   %rbp
0x00000000004004a5 <main+1>:    mov    %rsp,%rbp
0x00000000004004a8 <main+4>:    sub    $0xa0,%rsp
0x00000000004004af <main+11>:   mov    %edi,-0x24(%rbp)
0x00000000004004b2 <main+14>:   mov    %rsi,-0x30(%rbp)
0x00000000004004b6 <main+18>:   movl   $0x1,-0x14(%rbp)
0x00000000004004bd <main+25>:   movl   $0x2,-0xc(%rbp)
0x00000000004004c4 <main+32>:   mov    -0xc(%rbp),%esi
0x00000000004004c7 <main+35>:   mov    -0x14(%rbp),%edi
0x00000000004004ca <main+38>:   movl   $0x14,0x68(%rsp)  //哈哈,系统终于寄存器不够用了,果然是放到内存里去了!
                                                         //不过貌似%esi和%edi寄存器就是死活要拿来用啊,想想也对,寄存器快嘛!
0x00000000004004d2 <main+46>:   movl   $0x13,0x60(%rsp)
0x00000000004004da <main+54>:   movl   $0x12,0x58(%rsp)
0x00000000004004e2 <main+62>:   movl   $0x11,0x50(%rsp)
0x00000000004004ea <main+70>:   movl   $0x10,0x48(%rsp)
0x00000000004004f2 <main+78>:   movl   $0xf,0x40(%rsp)
0x00000000004004fa <main+86>:   movl   $0xe,0x38(%rsp)
0x0000000000400502 <main+94>:   movl   $0xd,0x30(%rsp)
0x000000000040050a <main+102>:  movl   $0xc,0x28(%rsp)
0x0000000000400512 <main+110>:  movl   $0xb,0x20(%rsp)
0x000000000040051a <main+118>:  movl   $0xa,0x18(%rsp)
0x0000000000400522 <main+126>:  movl   $0x9,0x10(%rsp)
0x000000000040052a <main+134>:  movl   $0x8,0x8(%rsp)
0x0000000000400532 <main+142>:  movl   $0x7,(%rsp)
0x0000000000400539 <main+149>:  mov    $0x6,%r9d
0x000000000040053f <main+155>:  mov    $0x5,%r8d
0x0000000000400545 <main+161>:  mov    $0x4,%ecx
0x000000000040054a <main+166>:  mov    $0x3,%edx
0x000000000040054f <main+171>:  callq  0x400448 <t>
0x0000000000400554 <main+176>:  mov    %eax,-0x10(%rbp)
0x0000000000400557 <main+179>:  mov    -0x10(%rbp),%eax
0x000000000040055a <main+182>:  leaveq 
0x000000000040055b <main+183>:  retq   
End of assembler dump.

(gdb) disass t
Dump of assembler code for function t:
0x0000000000400448 <t+0>:       push   %rbp
0x0000000000400449 <t+1>:       mov    %rsp,%rbp
0x000000000040044c <t+4>:       mov    %edi,-0x14(%rbp)
0x000000000040044f <t+7>:       mov    %esi,-0x18(%rbp)
0x0000000000400452 <t+10>:      mov    %edx,-0x1c(%rbp)
0x0000000000400455 <t+13>:      mov    %ecx,-0x20(%rbp)
0x0000000000400458 <t+16>:      mov    %r8d,-0x24(%rbp)
0x000000000040045c <t+20>:      mov    %r9d,-0x28(%rbp)
0x0000000000400460 <t+24>:      mov    -0x18(%rbp),%eax
0x0000000000400463 <t+27>:      add    -0x14(%rbp),%eax
0x0000000000400466 <t+30>:      add    -0x1c(%rbp),%eax
0x0000000000400469 <t+33>:      add    -0x20(%rbp),%eax
0x000000000040046c <t+36>:      add    -0x24(%rbp),%eax
0x000000000040046f <t+39>:      add    -0x28(%rbp),%eax
0x0000000000400472 <t+42>:      add    0x10(%rbp),%eax   //回头去取数据
0x0000000000400475 <t+45>:      add    0x18(%rbp),%eax
0x0000000000400478 <t+48>:      add    0x20(%rbp),%eax
0x000000000040047b <t+51>:      add    0x28(%rbp),%eax
0x000000000040047e <t+54>:      add    0x30(%rbp),%eax
0x0000000000400481 <t+57>:      add    0x38(%rbp),%eax
0x0000000000400484 <t+60>:      add    0x40(%rbp),%eax
0x0000000000400487 <t+63>:      add    0x48(%rbp),%eax
0x000000000040048a <t+66>:      add    0x50(%rbp),%eax
0x000000000040048d <t+69>:      add    0x58(%rbp),%eax
0x0000000000400490 <t+72>:      add    0x60(%rbp),%eax
0x0000000000400493 <t+75>:      add    0x68(%rbp),%eax
0x0000000000400496 <t+78>:      add    0x70(%rbp),%eax
0x0000000000400499 <t+81>:      add    0x78(%rbp),%eax
0x000000000040049c <t+84>:      mov    %eax,-0x4(%rbp)
0x000000000040049f <t+87>:      mov    -0x4(%rbp),%eax
0x00000000004004a2 <t+90>:      leaveq 
0x00000000004004a3 <t+91>:      retq   
End of assembler dump.










  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值