为了弄清局部变量在栈中的内存分布情况,特意写了下面这个简单得不能再简单的程序,然后用gdb反汇编查看汇编代码,然后内存的分布情况就一目了然了。废话不多说,先贴上代码
-
#include <iostream>
-
using namespace std;
-
-
const char* text = "eee";
-
-
void f(int a, char* str)
-
{
-
cout<<"f"<<endl;
-
}
-
-
int main()
-
{
-
int a = 0x61616161;
-
char str[] = "abcdefg";
-
f(a, str);
-
return 0;
-
}
然后用gdb调试,断点加在13行,打印出寄存器内容和反汇编代码:
-
(gdb) disassemble
-
Dump of assembler code for function main():
-
0x080486b0 <+0>: push %ebp
-
0x080486b1 <+1>: mov %esp,%ebp
-
0x080486b3 <+3>: and $0xfffffff0,%esp
-
0x080486b6 <+6>: sub $0x20,%esp
-
0x080486b9 <+9>: mov %gs:0x14,%eax
-
0x080486bf <+15>: mov %eax,0x1c(%esp)
-
0x080486c3 <+19>: xor %eax,%eax
-
0x080486c5 <+21>: movl $0x61616161,0x10(%esp)
-
0x080486cd <+29>: mov 0x8048836,%eax
-
0x080486d2 <+34>: mov 0x804883a,%edx
-
0x080486d8 <+40>: mov %eax,0x14(%esp)
-
0x080486dc <+44>: mov %edx,0x18(%esp)
-
0x080486e0 <+48>: lea 0x14(%esp),%eax
-
0x080486e4 <+52>: mov %eax,0x4(%esp)
-
0x080486e8 <+56>: mov 0x10(%esp),%eax
-
0x080486ec <+60>: mov %eax,(%esp)
-
=> 0x080486ef <+63>: call 0x8048684 <f(int, char*)>
-
0x080486f4 <+68>: mov $0x0,%eax
-
0x080486f9 <+73>: mov 0x1c(%esp),%edx
-
0x080486fd <+77>: xor %gs:0x14,%edx
-
0x08048704 <+84>: je 0x804870b <main()+91>
-
0x08048706 <+86>: call 0x804859c <__stack_chk_fail@plt>
-
0x0804870b <+91>: leave
-
0x0804870c <+92>: ret
-
End of assembler dump.
再打印出当前寄存器的内容:
-
(gdb) info r
-
eax 0x61616161 1633771873
-
ecx 0xd4c5f813 -725223405
-
edx 0x676665 6776421
-
ebx 0x3bfff4 3932148
-
esp 0xbffff2e0 0xbffff2e0
-
ebp 0xbffff308 0xbffff308
-
esi 0x0 0
-
edi 0x0 0
-
eip 0x80486ef 0x80486ef <main()+63>
-
eflags 0x200246 [ PF ZF IF ID ]
-
cs 0x73 115
-
ss 0x7b 123
-
ds 0x7b 123
-
es 0x7b 123
-
fs 0x0 0
-
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的内存可以看到:
-
(gdb) x /12 $esp
-
0xbffff2e0: 0x61616161 0xbffff2f4 0x003bfff4 0xbffff308
-
0xbffff2f0: 0x61616161 0x64636261 0x00676665 0xf5ea0b00
-
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,接下来将内存打印出来,过程就不多说了。
-
(gdb) x /18x 0xbffff2c0
-
0xbffff2c0: 0x0804a040 0x08048834 0xbffff2d8 0x08048528
-
0xbffff2d0: 0x003bfff4 0x08049ff4 0xbffff308 0x080486f4
-
0xbffff2e0: 0x61616161 0xbffff2f4 0x003bfff4 0xbffff308
-
0xbffff2f0: 0x61616161 0x64636261 0x00676665 0xf5ea0b00
-
0xbffff300: 0x08048770 0x00000000
-
(gdb) info r
-
eax 0x61616161 1633771873
-
ecx 0xd4c5f813 -725223405
-
edx 0x676665 6776421
-
ebx 0x3bfff4 3932148
-
esp 0xbffff2c0 0xbffff2c0
-
ebp 0xbffff2d8 0xbffff2d8
-
esi 0x0 0
-
edi 0x0 0
-
eip 0x8048699 0x8048699 <f(int, char*)+21>
-
eflags 0x200286 [ PF SF IF ID ]
-
cs 0x73 115
-
ss 0x7b 123
-
ds 0x7b 123
-
es 0x7b 123
-
fs 0x0 0
-
gs 0x33 51
可以看出main函数栈的esp和f函数的ebp相差了8个字节,也就是0xbffff2d8~0xbffff2e0这8个字节,存储的就是main的ebp和下一条指令的地址。用于执行完函数f后恢复用的。
阅读(333) | 评论(0) | 转发(0) |