为什么函数的参数被写入主堆栈帧而不是函数堆栈帧?

时间:2016-03-13 04:41:57

标签: c gcc stack gdb

所以我在gdb中反汇编了这段C代码:

0x08048474 <main+0>: push ebp
0x08048475 <main+1>: mov ebp,esp
0x08048477 <main+3>: sub esp,0x8
0x0804847a <main+6>: and esp,0xfffffff0
0x0804847d <main+9>: mov eax,0x0
0x08048482 <main+14>: sub esp,eax
0x08048484 <main+16>: cmp DWORD PTR [ebp+8],0x1
0x08048488 <main+20>: jg 0x80484ab <main+55>
0x0804848a <main+22>: mov eax,DWORD PTR [ebp+12]
0x0804848d <main+25>: mov eax,DWORD PTR [eax]
0x0804848f <main+27>: mov DWORD PTR [esp+4],eax
0x08048493 <main+31>: mov DWORD PTR [esp],0x80485e5
0x0804849a <main+38>: call 0x804831c <printf@plt>
0x0804849f <main+43>: mov DWORD PTR [esp],0x0
0x080484a6 <main+50>: call 0x804833c <exit@plt>
0x080484ab <main+55>: mov eax,DWORD PTR [ebp+12]
0x080484ae <main+58>: add eax,0x4
0x080484b1 <main+61>: mov eax,DWORD PTR [eax]
0x080484b3 <main+63>: mov DWORD PTR [esp],eax
0x080484b6 <main+66>: call 0x8048414 <check_authentication>
0x080484bb <main+71>: test eax,eax
0x080484bd <main+73>: je 0x80484e5 <main+113>
0x080484bf <main+75>: mov DWORD PTR [esp],0x80485fb
0x080484c6 <main+82>: call 0x804831c <printf@plt>
0x080484cb <main+87>: mov DWORD PTR [esp],0x8048619
0x080484d2 <main+94>: call 0x804831c <printf@plt>
0x080484d7 <main+99>: mov DWORD PTR [esp],0x8048630
0x080484de <main+106>: call 0x804831c <printf@plt>
0x080484e3 <main+111>: jmp 0x80484f1 <main+125>
0x080484e5 <main+113>: mov DWORD PTR [esp],0x804864d
0x080484ec <main+120>: call 0x804831c <printf@plt>
0x080484f1 <main+125>: leave
0x080484f2 <main+126>: ret
End of assembler dump.

我的主要问题围绕以下几点:

0x080484b3 <main+63>: mov DWORD PTR [esp],eax
0x080484b6 <main+66>: call 0x8048414 <check_authentication>

此时我单步执行esp = 0xbffff7e0。当我进入check_authentication函数esp = 0xbffff7a0时。这些行正在写入(char *)的地址,它是check_authentication的参数,但是它们在0xbffff7e0处写入,而不是在0xbffff7a0 - 0xbffff7e0的堆栈帧内。我能想到的唯一理由是堆栈在创建时会分配填充,因为这可能是填充编译器这样做是为了节省空间吗?有谁知道这是不是这样的?为什么不在esp-4处写入地址,这实际上是在函数的堆栈帧内?

编辑:添加内存输出以帮助我解释不好

在main()中:

(gdb) x/4xw $esp
0xbffff7e0: 0xb8000ce0 0x08048510 0xbffff848 0xb7eafebc

check_authentication()内部:

(gdb) x/32xw $esp
0xbffff7a0: 0x00000000 0x08049744 0xbffff7b8 0x080482d9
0xbffff7b0: 0xb7f9f729 0xb7fd6ff4 0xbffff7e8 0x00000000
0xbffff7c0: 0xb7fd6ff4 0xbffff880 0xbffff7e8 0xb7fd6ff4
0xbffff7d0: 0xb7ff47b0 0x08048510 0xbffff7e8 0x080484bb
0xbffff7e0: 0xbffff9b7 0x08048510 0xbffff848 0xb7eafebc

主要是0xbffff7e0 = 0xe00c00b8

在check_authentication中,0xbffff7e0 = 0xb7f9ffbf

在跳转到check_authentication之前发生了更改,但更改是针对check_authentication的,所以为什么它位于0xbffff7e0,这是mains()esp指向的位置,而不是check_function的堆栈帧。我试图解决这个问题,我正在绞尽脑汁。

1 个答案:

答案 0 :(得分:0)

x86中没有“分配的堆栈帧”。堆栈是一个连续的内存区域,SP指向“堆栈顶部”(其最低地址)。 程序集不存储在某个随机地址,而是存储在[ESP]中,因此它位于函数的堆栈框架内 - 在EBP和当前ESP 包含之间。

PUSH操作码的定义与以下C语句不同:

*--SP = value;   // decrease pointer first, then store

POP

value = *SP++;   // take the value pointed to, then increase pointer

现在在编译器的CALL check_authentication之前的这一行,而不是生成PUSHPOP对,只是相当于

*SP = value;