【反汇编分析】函数栈帧

来源:互联网 发布:vue.js chrome插件 编辑:程序博客网 时间:2024/05/22 06:20

      本节通过反汇编可执行文件得到的文件,研究函数栈帧相关内容;


函数栈帧

栈帧整体示意图如下



示例代码

#include <stdio.h>#include <iostream>using namespace std;int z = 10;int add(int x, int y){  return x+y+z;}int inc20(int x){  int y = 10;  return add(x, y);}int main(void){  int a = 30;  a = inc20(a);  cout << a << endl;  return 0;}
说明几点

(1)本节只研究main和inc20、add之间的函数栈帧,对于cout,以及main函数之前和之后的启动和结束代码不作研究;

例子整体栈帧示意图如下


反汇编代码

080486ac <_Z3addii>: 80486ac:55                   push   %ebp 80486ad:89 e5                mov    %esp,%ebp 80486af:8b 55 08             mov    0x8(%ebp),%edx 80486b2:8b 45 0c             mov    0xc(%ebp),%eax 80486b5:01 c2                add    %eax,%edx 80486b7:a1 84 9a 04 08       mov    0x8049a84,%eax 80486bc:01 d0                add    %edx,%eax 80486be:5d                   pop    %ebp 80486bf:c3                   ret    080486c0 <_Z5inc20i>: 80486c0:55                   push   %ebp 80486c1:89 e5                mov    %esp,%ebp 80486c3:83 ec 10             sub    $0x10,%esp 80486c6:c7 45 fc 0a 00 00 00 movl   $0xa,-0x4(%ebp) 80486cd:ff 75 fc             pushl  -0x4(%ebp) 80486d0:ff 75 08             pushl  0x8(%ebp) 80486d3:e8 d4 ff ff ff       call   80486ac <_Z3addii> 80486d8:83 c4 08             add    $0x8,%esp 80486db:c9                   leave   80486dc:c3                   ret    080486dd <main>: 80486dd:8d 4c 24 04          lea    0x4(%esp),%ecx 80486e1:83 e4 f0             and    $0xfffffff0,%esp 80486e4:ff 71 fc             pushl  -0x4(%ecx) 80486e7:55                   push   %ebp 80486e8:89 e5                mov    %esp,%ebp 80486ea:51                   push   %ecx 80486eb:83 ec 14             sub    $0x14,%esp 80486ee:c7 45 f4 1e 00 00 00 movl   $0x1e,-0xc(%ebp) 80486f5:ff 75 f4             pushl  -0xc(%ebp) 80486f8:e8 c3 ff ff ff       call   80486c0 <_Z5inc20i> 80486fd:83 c4 04             add    $0x4,%esp 8048700:89 45 f4             mov    %eax,-0xc(%ebp) 8048703:83 ec 08             sub    $0x8,%esp 8048706:ff 75 f4             pushl  -0xc(%ebp) 8048709:68 c0 9a 04 08       push   $0x8049ac0 804870e:e8 e9 fd ff ff       call   80484fc <_ZNSolsEi@plt> 8048713:83 c4 10             add    $0x10,%esp 8048716:83 ec 08             sub    $0x8,%esp 8048719:68 6c 85 04 08       push   $0x804856c 804871e:50                   push   %eax 804871f:e8 38 fe ff ff       call   804855c <_ZNSolsEPFRSoS_E@plt> 8048724:83 c4 10             add    $0x10,%esp 8048727:b8 00 00 00 00       mov    $0x0,%eax 804872c:8b 4d fc             mov    -0x4(%ebp),%ecx 804872f:c9                   leave   8048730:8d 61 fc             lea    -0x4(%ecx),%esp 8048733:c3                   ret   
说明几点

(1)首先在_Z3addii找出z变量的地址和相关信息;

sykpour@sykpour:~/Desktop$ readelf -s test | grep 8049a84    75: 08049a84     4 OBJECT  GLOBAL DEFAULT   23 z
(2)立即数前面的标识是$,popl为双字(单字为16字节)出栈,对于寄存器而言pop %esp也是栈中双字赋值给esp;如movl   $0xa,-0x4(%ebp)表示将立即数0xa存放到ebp+4地址处;而mov  0x8(%ebp),%edx表示将ebp+8地址处的内容存放到edx寄存器中

汇编注释

080486ac <_Z3addii>: 80486ac:55                   push   %ebp 80486ad:89 e5                mov    %esp,%ebp 80486af:8b 55 08             mov    0x8(%ebp),%edx   #取参数x到edx 80486b2:8b 45 0c             mov    0xc(%ebp),%eax   #取参数y到eax 80486b5:01 c2                add    %eax,%edx        #x=x+y; 80486b7:a1 84 9a 04 08       mov    0x8049a84,%eax   #取z的内容到eax 80486bc:01 d0                add    %edx,%eax        #z=z+x; 80486be:5d                   pop    %ebp 80486bf:c3                   ret    080486c0 <_Z5inc20i>: 80486c0:55                   push   %ebp 80486c1:89 e5                mov    %esp,%ebp 80486c3:83 ec 10             sub    $0x10,%esp 80486c6:c7 45 fc 0a 00 00 00 movl   $0xa,-0x4(%ebp)   #局部变量y的值 80486cd:ff 75 fc             pushl  -0x4(%ebp)        #add的参数y 80486d0:ff 75 08             pushl  0x8(%ebp)         #add的参数x,ebp是在main函数的栈中找的值 80486d3:e8 d4 ff ff ff       call   80486ac <_Z3addii> 80486d8:83 c4 08             add    $0x8,%esp         #调整栈指针 80486db:c9                   leave   80486dc:c3                   ret    080486dd <main>: 80486dd:8d 4c 24 04          lea    0x4(%esp),%ecx 80486e1:83 e4 f0             and    $0xfffffff0,%esp 80486e4:ff 71 fc             pushl  -0x4(%ecx) 80486e7:55                   push   %ebp                #保存栈帧寄存器 80486e8:89 e5                mov    %esp,%ebp           #帧指针指向栈指针 80486ea:51                   push   %ecx                #保护ecx寄存器的内容 80486eb:83 ec 14             sub    $0x14,%esp          #调整栈指针,存放局部变量 80486ee:c7 45 f4 1e 00 00 00 movl   $0x1e,-0xc(%ebp)    #局部变量a的值 80486f5:ff 75 f4             pushl  -0xc(%ebp)          #inc20的参数x 80486f8:e8 c3 ff ff ff       call   80486c0 <_Z5inc20i> 80486fd:83 c4 04             add    $0x4,%esp           #调整栈指针 8048700:89 45 f4             mov    %eax,-0xc(%ebp)     #cout打印的参数,直接从add的eax中取回的 8048703:83 ec 08             sub    $0x8,%esp 8048706:ff 75 f4             pushl  -0xc(%ebp) 8048709:68 c0 9a 04 08       push   $0x8049ac0 804870e:e8 e9 fd ff ff       call   80484fc <_ZNSolsEi@plt>

越界访问

在使用gets这样的函数时,很有可能使用栈中分配一个数组来保存字符串,但是字符串的长度却超过了分配的空间,这有可能会带来很严重的问题;

代码片段

#include <stdio.h>using namespace std;int main(void){  char buf[16];  gets(buf);  puts(buf);  return 0;}

反汇编

080484fc <main>: 80484fc:8d 4c 24 04          lea    0x4(%esp),%ecx 8048500:83 e4 f0             and    $0xfffffff0,%esp 8048503:ff 71 fc             pushl  -0x4(%ecx) 8048506:55                   push   %ebp 8048507:89 e5                mov    %esp,%ebp 8048509:51                   push   %ecx 804850a:83 ec 14             sub    $0x14,%esp 804850d:83 ec 0c             sub    $0xc,%esp 8048510:8d 45 e8             lea    -0x18(%ebp),%eax 8048513:50                   push   %eax 8048514:e8 83 fe ff ff       call   804839c <gets@plt> 8048519:83 c4 10             add    $0x10,%esp 804851c:83 ec 0c             sub    $0xc,%esp 804851f:8d 45 e8             lea    -0x18(%ebp),%eax 8048522:50                   push   %eax 8048523:e8 94 fe ff ff       call   80483bc <puts@plt> 8048528:83 c4 10             add    $0x10,%esp 804852b:b8 00 00 00 00       mov    $0x0,%eax 8048530:8b 4d fc             mov    -0x4(%ebp),%ecx 8048533:c9                   leave   8048534:8d 61 fc             lea    -0x4(%ecx),%esp 8048537:c3                   ret     8048538:90                   nop 8048539:90                   nop 804853a:90                   nop 804853b:90                   nop 804853c:90                   nop 804853d:90                   nop 804853e:90                   nop 804853f:90                   nop

main函数栈帧示意图


说明几点:

(1)通过示意图发现,如果gets输入超过16个字节,那么保存的ebp和ecx的值都有可能受到破坏,也就极有可能发生段错误;

(2)如果修改main函数上的返回地址,也就有可能使得程序跳转到不安全的代码中去;

汇编注释

 8048506:55                   push   %ebp             #保存帧指针 8048507:89 e5                mov    %esp,%ebp        #帧指针指向栈指针 8048509:51                   push   %ecx 804850a:83 ec 14             sub    $0x14,%esp 804850d:83 ec 0c             sub    $0xc,%esp 8048510:8d 45 e8             lea    -0x18(%ebp),%eax  #取buf的地址 8048513:50                   push   %eax 8048514:e8 83 fe ff ff       call   804839c <gets@plt>

0 0