Chinaunix首页 | 论坛 | 博客
  • 博客访问: 486996
  • 博文数量: 53
  • 博客积分: 492
  • 博客等级: 下士
  • 技术积分: 866
  • 用 户 组: 普通用户
  • 注册时间: 2011-11-14 15:12
文章分类

全部博文(53)

文章存档

2016年(1)

2015年(3)

2014年(17)

2013年(9)

2012年(7)

2011年(16)

我的朋友

分类: C/C++

2013-04-29 18:27:47

    为了弄清局部变量在栈中的内存分布情况,特意写了下面这个简单得不能再简单的程序,然后用gdb反汇编查看汇编代码,然后内存的分布情况就一目了然了。废话不多说,先贴上代码

    

点击(此处)折叠或打开

  1. #include <iostream>
  2. using namespace std;

  3. const char* text = "eee";

  4. void f(int a, char* str)
  5. {
  6.     cout<<"f"<<endl;
  7. }

  8. int main()
  9. {
  10.     int a = 0x61616161;
  11.     char str[] = "abcdefg";
  12.     f(a, str);
  13.     return 0;
  14. }


    然后用gdb调试,断点加在13行,打印出寄存器内容和反汇编代码:

    

点击(此处)折叠或打开

  1. (gdb) disassemble
  2. Dump of assembler code for function main():
  3.    0x080486b0 <+0>:    push %ebp
  4.    0x080486b1 <+1>:    mov %esp,%ebp
  5.    0x080486b3 <+3>:    and $0xfffffff0,%esp
  6.    0x080486b6 <+6>:    sub $0x20,%esp
  7.    0x080486b9 <+9>:    mov %gs:0x14,%eax
  8.    0x080486bf <+15>:    mov %eax,0x1c(%esp)
  9.    0x080486c3 <+19>:    xor %eax,%eax
  10.    0x080486c5 <+21>:    movl $0x61616161,0x10(%esp)
  11.    0x080486cd <+29>:    mov 0x8048836,%eax
  12.    0x080486d2 <+34>:    mov 0x804883a,%edx
  13.    0x080486d8 <+40>:    mov %eax,0x14(%esp)
  14.    0x080486dc <+44>:    mov %edx,0x18(%esp)
  15.    0x080486e0 <+48>:    lea 0x14(%esp),%eax
  16.    0x080486e4 <+52>:    mov %eax,0x4(%esp)
  17.    0x080486e8 <+56>:    mov 0x10(%esp),%eax
  18.    0x080486ec <+60>:    mov %eax,(%esp)
  19. => 0x080486ef <+63>:    call 0x8048684 <f(int, char*)>
  20.    0x080486f4 <+68>:    mov $0x0,%eax
  21.    0x080486f9 <+73>:    mov 0x1c(%esp),%edx
  22.    0x080486fd <+77>:    xor %gs:0x14,%edx
  23.    0x08048704 <+84>:    je 0x804870b <main()+91>
  24.    0x08048706 <+86>:    call 0x804859c <__stack_chk_fail@plt>
  25.    0x0804870b <+91>:    leave
  26.    0x0804870c <+92>:    ret
  27. End of assembler dump.
   再打印出当前寄存器的内容:

点击(此处)折叠或打开

  1. (gdb) info r
  2. eax 0x61616161    1633771873
  3. ecx 0xd4c5f813    -725223405
  4. edx 0x676665    6776421
  5. ebx 0x3bfff4    3932148
  6. esp 0xbffff2e0    0xbffff2e0
  7. ebp 0xbffff308    0xbffff308
  8. esi 0x0    0
  9. edi 0x0    0
  10. eip 0x80486ef    0x80486ef <main()+63>
  11. eflags 0x200246    [ PF ZF IF ID ]
  12. cs 0x73    115
  13. ss 0x7b    123
  14. ds 0x7b    123
  15. es 0x7b    123
  16. fs 0x0    0
  17. gs 0x33    51


好了,各种信息都准备齐全了,现在就可以开始分析了。
在寄存器中看到esp的值是0xbffff2e0,程序中的两个局部变量的地址都是以exp作为偏移基址的。汇编第10行将a压栈,变量a的地址就是$esp+0x10 = 0xbffff2f0。汇编第11行和12行是将字符串str的内容放到寄存器中,第13行开始再将这两个寄存器的内容压栈。因为字符串str的长度是8个字节,而寄存器的长度是32位,所以用两个寄存器存储。因为局部变量str的字符串内容是存储静态存储区的,所以看到的是地址0x8048836。汇编第15行就是将局部变量str的地址存储到eax中,str的地址是$esp+0x14 = 0xbffff2f4。此时打印esp到ebp的内存可以看到:

点击(此处)折叠或打开

  1. (gdb) x /12 $esp
  2. 0xbffff2e0:    0x61616161    0xbffff2f4    0x003bfff4    0xbffff308
  3. 0xbffff2f0:    0x61616161    0x64636261    0x00676665    0xf5ea0b00
  4. 0xbffff300:    0x08048770    0x00000000    0xbffff388    0x00272e37
第3行oxbffff2f0的值就是0x61616161,也就是a,0xbffff2f4开始就是字符串str,因为str的内容"abcdefg"是小端存储,数组的地址是向上增长的 ,0x64在高字节,0x61在低字节,所以看到的就是0x64636261。
汇编第15行开始将函数f要调用的参数压栈,因为是从右向左压栈,所以先将局部变量str的值压栈(压入到$esp+0x4),因为str是一个指针变量,所以压入的是一个地址。整型局部变量直接压入变量值,存入地址$esp。
下面就是函数f()的调用过程,call指令相当于几条指令,首先将当前指令的下一条压栈,esp-0x4。接下来就像main函数开头那样ebp压栈,esp同时减4,再赋给ebp,接下来将内存打印出来,过程就不多说了。

点击(此处)折叠或打开

  1. (gdb) x /18x 0xbffff2c0
  2. 0xbffff2c0:    0x0804a040    0x08048834    0xbffff2d8    0x08048528
  3. 0xbffff2d0:    0x003bfff4    0x08049ff4    0xbffff308    0x080486f4
  4. 0xbffff2e0:    0x61616161    0xbffff2f4    0x003bfff4    0xbffff308
  5. 0xbffff2f0:    0x61616161    0x64636261    0x00676665    0xf5ea0b00
  6. 0xbffff300:    0x08048770    0x00000000

点击(此处)折叠或打开

  1. (gdb) info r
  2. eax 0x61616161    1633771873
  3. ecx 0xd4c5f813    -725223405
  4. edx 0x676665    6776421
  5. ebx 0x3bfff4    3932148
  6. esp 0xbffff2c0    0xbffff2c0
  7. ebp 0xbffff2d8    0xbffff2d8
  8. esi 0x0    0
  9. edi 0x0    0
  10. eip 0x8048699    0x8048699 <f(int, char*)+21>
  11. eflags 0x200286    [ PF SF IF ID ]
  12. cs 0x73    115
  13. ss 0x7b    123
  14. ds 0x7b    123
  15. es 0x7b    123
  16. fs 0x0    0
  17. gs 0x33    51
可以看出main函数栈的esp和f函数的ebp相差了8个字节,也就是0xbffff2d8~0xbffff2e0这8个字节,存储的就是main的ebp和下一条指令的地址。用于执行完函数f后恢复用的。
阅读(4238) | 评论(0) | 转发(3) |
给主人留下些什么吧!~~