2013-04-01 166 views
0

我想了解当堆栈损坏时会发生什么。这是我试图理解的示例程序。我已经将缓冲区的大小定义为1个字节。但是在我输入第13个字节后堆栈损坏发生。为什么在第13个字节后会损坏?堆栈损坏使用缓冲区

C代码:

#include<stdio.h> 
#include<string.h> 
int main(int argc,char *argv[]){ 
char buffer[1]; 
strcpy(buffer,argv[1]); 
printf("\n buffer : %s \n",buffer); 
return 0; 
} 

汇编代码:

 .file "buffer_overflow.c" 
     .section  .rodata 
.LC0: 
     .string "\n buffer : %s \n" 
     .text 
.globl main 
     .type main, @function 
main: 
     pushl %ebp 
     movl %esp, %ebp 
     andl $-16, %esp 
     subl $32, %esp 
     movl 12(%ebp), %eax 
     addl $4, %eax 
     movl (%eax), %eax 
     movl %eax, 4(%esp) 
     leal 31(%esp), %eax 
     movl %eax, (%esp) 
     call strcpy 
     movl $.LC0, %eax 
     leal 31(%esp), %edx 
     movl %edx, 4(%esp) 
     movl %eax, (%esp) 
     call printf 
     movl $0, %eax 
     leave 
     ret 
     .size main, .-main 
     .ident "GCC: (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5" 
     .section  .note.GNU-stack,"",@progbits 
+0

如果你去写愚蠢的代码,然后期望愚蠢的结果。 –

+1

@EdHeal - 我不认为OP会不同意。我怀疑他在试图理解为什么他在某些情况下崩溃,但在其他情况下不会。这使得这是关于缓冲区溢出性质的一个很好的问题。 – selbie

+0

可能的重复[为什么我的程序崩溃时,我写过一个数组的末尾?](http://stackoverflow.com/questions/6452959/why-doesnt-my-program-crash-when-i- (请注意,该问题中的数组也被分配到堆栈中,以防您特别对堆栈(而不是堆)缓冲区溢出感兴趣)。 – jogojapan

回答

1

最重要的是 - 当你越过第一个字节时,栈被损坏。 :)但我离题了。

在主被调用之前,堆栈可能会将以下内容推送到堆栈上。

4个字节为来自此函数的返回地址。

4字节为“argc”

“argv”为4字节。

1字节为“缓冲器”,但是编译器在4字节边界

因此,第一个12个字节堆栈的是所有变量可能对准此。一旦你破坏了返回地址,在偏移12或13左右,你会使程序处于不良状态,并可能在试图从这个调用返回时崩溃。

+0

谢谢selbie,为了清楚地显示 – Angus

+0

我删除了argc和argv参数,因此从堆栈中删除了8个字节。所以我可以预料seg错误发生在第7个字节,但seg错误仍然发生在第13个字节。 – Angus

+0

我怀疑这是“主”的特例。无论你是如何声明main的,argc和argv都被推到栈上。将你的函数的名字改为“foo”,并且只需要main调用foo - 或者直接将argc/argv传递给它,或者不带。 – selbie

1

堆栈向下生长在英特尔处理器。变量放在堆栈上。在函数调用之后,ABI(应用程序二进制接口)也可能占用几个字节来存储帧指针,指向变量块的指针以及其它任何其他所需的内容。你可以在你的程序集中看到它在开始时推动eb​​p。

那么,为什么只有在第13个字节后才看到堆栈损坏?那么,1字节是可以的,因为你有1个字节的内存。接下来的12个都没问题,因为ABI把额外的变量推入了那个房间。由于覆盖它可能会产生奇怪的结果,但不会崩溃。然后你进入下一个变量,这是返回地址。覆盖这个,你几乎肯定会崩溃(缺少非凡的运气)。

1

当CPU进入一个函数时,它需要将一些值推送到内存堆栈中。根据你的代码,在调用'strcpy'之前,在x86系统上这样显示堆栈帧。

------------ offset 13 
char buffer[1] 
------------ offset 12 
char *argv[] 
------------ offset 8 
int argc 
------------ offset 4 
ret 
------------ offset 0 

所以在写完13bytes之后,strcpy已经重写了ret区域。主要完成时,它已损坏。