2011-12-06 94 views
2

下面的代码来自着名的文章Smashing The Stack For Fun And Profit我的溢出代码不起作用

void function(int a, int b, int c) { 
    char buffer1[5]; 
    char buffer2[10]; 
    int *ret; 
    ret = buffer1 + 12; 
    (*ret)+=8; 
} 

void main() { 
    int x; 
    x=0; 
    function(1,2,3); 
    x=1; 
    printf("%d\n",x); 
} 

我想我必须解释我的这段代码的目标。 堆栈模型如下。该字下面的数字是堆栈中变量的字节数。所以,如果我想重写RET来跳过我想要的语句,我计算从buffer1到RET的偏移量是8 + 4 = 12。由于该架构是x86 Linux。

buffer2 buffer1 BSP RET a b c 
(12) (8)  (4) (4) (4) (4) (4) 

我想跳过声明x=1;,让屏幕上printf()输出0

我编译代码:

gcc stack2.c -g 

,并在gdb运行它:

gdb ./a.out 

GDB给我的结果是这样的:

Program received signal SIGSEGV, Segmentation fault. 
main() at stack2.c:17 
17 x = 1; 

我认为Linux使用一些防止堆栈溢出的机制。也许Linux将RET地址存储在另一个地方,并在函数返回之前比较堆栈中的RET地址。

这个机制的细节是什么?我应该如何重写代码才能使程序输出0

好的,反汇编代码在下面。它来自gdb的输出,因为我觉得更容易阅读。有人可以告诉我如何粘贴一个长代码序列吗?一个接一个地复制粘贴我太累了......

Dump of assembler code for function main: 
0x08048402 <+0>: push %ebp 
0x08048403 <+1>: mov %esp,%ebp 
0x08048405 <+3>: sub $0x10,%esp 
0x08048408 <+6>: movl $0x0,-0x4(%ebp) 
0x0804840f <+13>: movl $0x3,0x8(%esp) 
0x08048417 <+21>: movl $0x2,0x4(%esp) 
0x0804841f <+29>: movl $0x1,(%esp) 
0x08048426 <+36>: call 0x80483e4 <function> 
0x0804842b <+41>: movl $0x1,-0x4(%ebp) 
0x08048432 <+48>: mov $0x8048520,%eax 
0x08048437 <+53>: mov -0x4(%ebp),%edx 
0x0804843a <+56>: mov %edx,0x4(%esp) 
0x0804843e <+60>: mov %eax,(%esp) 
0x08048441 <+63>: call 0x804831c <[email protected]> 
0x08048446 <+68>: mov $0x0,%eax 
0x0804844b <+73>: leave 
0x0804844c <+74>: ret 


Dump of assembler code for function function: 
0x080483e4 <+0>: push %ebp 
0x080483e5 <+1>: mov %esp,%ebp 
0x080483e7 <+3>: sub $0x14,%esp 
0x080483ea <+6>: lea -0x9(%ebp),%eax 
0x080483ed <+9>: add $0x3,%eax 
0x080483f0 <+12>: mov %eax,-0x4(%ebp) 
0x080483f3 <+15>: mov -0x4(%ebp),%eax 
0x080483f6 <+18>: mov (%eax),%eax 
0x080483f8 <+20>: lea 0x8(%eax),%edx 
0x080483fb <+23>: mov -0x4(%ebp),%eax 
0x080483fe <+26>: mov %edx,(%eax) 
0x08048400 <+28>: leave 
0x08048401 <+29>: ret 

我检查汇编代码,发现我的一些程序错误,我必须重写(*ret)+=8(*ret)+=7,因为0x08048432 <+48>减去0x0804842b <+41>是7

+1

检查程序集生成,可能你的函数已经内联。 – kan

+0

我保证函数没有被内联,因为我检查了汇编代码。 –

+1

假设'x = 1'生成8个字节的汇编代码,并且堆栈按照您显示的方式进行布局(可能还有其他内容)。添加汇编列表,答案将变得非常明显。 – Skizz

回答

2

因为那篇文章是从1996年开始的,而且这些假设是错误的。

请参阅 “砸现代堆栈的乐趣和利润”

http://www.ethicalhacker.net/content/view/122/24/

从上面的链接:

然而,GNU C编译器(GCC)自1998年以来的演变,结果,许多人仍然想知道为什么他们不能为他们工作的例子,或者如果他们确实得到代码工作,为什么他们必须做出他们所做的改变。

+0

非常感谢! –

0

功能function覆盖一些其堆栈外的地方wn,这种情况是main的堆栈。重写的内容我不知道,但会导致你看到的分段错误。它可能是操作系统采用的一些保护措施,但当错误的值位于堆栈上的位置时,生成的代码可能会出错。

这是一个很好的例子,说明当您在分配的内存之外写入时可能发生的情况。它可能会直接崩溃,它可能会崩溃在完全不同的地方,或者如果可能不会崩溃,但只是做一些错误的计算。

+0

-1没有什么能够阻止你覆盖任何其他函数的堆栈帧到堆栈的根目录。在这种情况下,他可能会写入堆栈的起始位置(具体取决于调用'main()'的代码) –

+2

分段错误是由进程在其内存之外写入引起的。由于这两个函数属于同一个进程,因此它们可以在不提高SIGSEGV的情况下对对方的内存进行涂写(除非编译器或操作系统已经做了某些*非常奇怪的事情来防止堆栈崩溃)。 – Jonathan

0

ret = buffer1 + 3;尝试

说明:ret是整数指针;递增1增加4个字节到32位机器上的地址。

+0

它不起作用。 –

+3

打印'x','a'和'buffer1'的地址。这应该给你一个错误是哪一方的想法。 PS:“不起作用”作为错误信息是没有用的。请不要再使用这个句子。 :-) –

+0

OK。我已经厌倦了重写“ret = buffer1 + 3”并运行该程序。并且在我厌倦了代码之后发布了第一条评论。但它不起作用。 –