粉碎堆栈无法正常工作

时间:2014-03-05 00:31:30

标签: c function unix stack

我已经完成了关于粉碎堆栈的演练。这里http://insecure.org/stf/smashstack.html和我在Trying to smash the stack找到的一个{{3}}。我理解会发生什么,但我不能让它正常工作。

这与其他场景一样。我需要跳过x = 1并打印0作为x的值。

我编译:

gcc file.c

原始代码:

void function(){
    char buffer[8];
}

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

当我跑步时

objdump -dS a.out

我得到了

0000000000400530 <function>:
  400530:       55                      push   %rbp
  400531:       48 89 e5                mov    %rsp,%rbp
  400534:       5d                      pop    %rbp
  400535:       c3                      retq

0000000000400536 <main>:
  400536:       55                      push   %rbp
  400537:       48 89 e5                mov    %rsp,%rbp
  40053a:       48 83 ec 20             sub    $0x20,%rsp
  40053e:       89 7d ec                mov    %edi,-0x14(%rbp)
  400541:       48 89 75 e0             mov    %rsi,-0x20(%rbp)
  400545:       c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%rbp)
  40054c:       b8 00 00 00 00          mov    $0x0,%eax
  400551:       e8 da ff ff ff          callq  400530 <function>
  400556:       c7 45 fc 01 00 00 00    movl   $0x1,-0x4(%rbp)
  40055d:       8b 45 fc                mov    -0x4(%rbp),%eax
  400560:       89 c6                   mov    %eax,%esi
  400562:       bf 10 06 40 00          mov    $0x400610,%edi
  400567:       b8 00 00 00 00          mov    $0x0,%eax
  40056c:       e8 9f fe ff ff          callq  400410 <printf@plt>
  400571:       c9                      leaveq
  400572:       c3                      retq
  400573:       66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
  40057a:       00 00 00
  40057d:       0f 1f 00                nopl   (%rax)

在函数中,我需要弄清楚返回地址超出缓冲区开头的字节数。我不确定这个价值。但是因为从函数的beginnig到返回有6个字节;我会在缓冲区中添加7个字节吗?

然后我需要跳过指令     X = 1; 因为该指令长7个字节。我会将7添加到返回指针吗?

这样的东西?

void function(){
    char buffer[8];
    int *ret = buffer + 7;
    (*ret) += 7;
}

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

这会抛出警告:

warning: initialization from incompatible pointer type [enabled by default]
  int *ret = buffer1 + 5;
         ^

输出是1.我做错了什么?你能解释一下如何做到正确以及为什么这是正确的方法?

谢谢。

2 个答案:

答案 0 :(得分:1)

我们知道在堆栈上创建自动变量 - 因此获取自动变量的地址会产生指向堆栈的指针。当您调用void函数时,它的返回地址被压入堆栈,该地址的大小取决于您的平台(通常为4或8个字节)。因此,如果将自动变量的地址传递给函数,然后在该地址之前写入内存,则会损坏返回地址并粉碎堆栈。这是一个例子:

#include <stdlib.h>
#include <stdio.h>

static void f(int *p)
{
    p[0] = 0x30303030;
    p[1] = 0x31313131;
    *(p - 1) = 0x35353535;
    *(p - 2) = 0x36363636;
}

int main()
{
    int a = 0x41424344;
    int b = 0x45464748;
    int c = 0x494a4b5c;
    f(&b);
    printf("%08x %08x %08x\n", a, b, c);
    return 0;
}

我使用'gcc -g'在linux上编译了这个并在gdb下运行并得到了这个:

Program received signal SIGSEGV, Segmentation fault.
0x000000000040056a in f (p=0x7fffffffde74) at smash.c:10
10  }
(gdb) bt
#0  0x000000000040056a in f (p=0x7fffffffde74) at smash.c:10
#1  0x3636363600400594 in ?? ()
#2  0x3030303035353535 in ?? ()
#3  0x494a4b5c31313131 in ?? ()
#4  0x0000000000000000 in ?? ()
(gdb)

如您所见,父函数地址现在包含一些我的幻数。我在64位linux上运行它,所以我应该使用64位整数来完全覆盖返回地址 - 因为我保持低位字未触及。

答案 1 :(得分:1)

尝试下面的函数,我为32位编译器编写了尝试使用(-m32 gcc标志)或者稍加努力就可以使它与64位编译器一起工作(注意在你的{{ 1}}列出调用objdump与下一条指令之间的7字节偏移量,因此请使用function代替7

8