【问题标题】:What happens if i overwrite the return address on the stack?如果我覆盖堆栈上的返回地址会发生什么?
【发布时间】:2015-05-25 14:38:32
【问题描述】:

我知道这是危险行为,但我想弄清楚发生了什么。

代码如下:

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

static int count = 0;

void hello(void){
    count ++;  
    fprintf(stderr,"hello! %d\n",count);
}

void foo(void){
    void *buf[10];
    static int i;

    for(i = 0 ; i < 100 ; i++){ // put enough data to overwrite the return address on the stack
        buf[i] = hello;
    }
}

int main(void){
    int buf[1000];
    foo();
    return 0;
}

结果如下:

……
hello! 83
hello! 84
hello! 85
hello! 86
hello! 87
hello! 88
hello! 89
Segmentation fault (core dumped)

为什么 hello 函数被调用了 89 次? 当函数foo返回时,pc寄存器应该得到hello函数的地址,不是吗? 于是调用了hello,执行了里面的代码,然后呢?程序不应该返回主函数吗? “89”从何而来?我似乎遗漏了什么,请有人指出来。

【问题讨论】:

  • 您使用的是哪个编译器/操作系统?另外,你知道 main 中的 buf 和 foo 中的 buf 没有任何关系,对吧?
  • @Ashalynd centos 7, gcc
  • 哪个版本的 gcc?
  • @Ashalynd gcc (GCC) 4.8.2 20140120(红帽 4.8.2-16)
  • 你自己说你覆盖了返回地址,所以main不能被返回。你的程序“返回”到hello,直到它崩溃。

标签: c++ c stack-overflow


【解决方案1】:

您将函数hello 的地址写入foo 的堆栈中,超出了您使用buf 保留的空间量的90 倍。当foo 试图返回时,它会将hello 的第一个额外地址(其中一个)加载到程序计数器中,将其弹出并“返回”给它。我之所以说“其中之一”是因为在您保留的数组的末尾和返回地址的开头之间,堆栈上可能有一些空间。

hello 假定它是由foo 调用的。但是,它并没有被调用,换句话说,foo 的其余部分的地址没有被压入堆栈以供hello 返回。所以,当hello 返回时,它弹出的东西是堆栈中的下一个东西,这又是hello 的地址。

从hello 的角度来看,这再次看起来像是一个调用,但它实际上不是一个调用,因为它是一个返回,所以hello 不断返回到它自己并从其中弹出一个hello 地址每次堆栈,直到用完这些地址。

在这些用完之后,堆栈上的下一个字节(您没有覆盖)当被视为指针时,将指向内存中的其他地方,可能是未分配给您的进程内存映射的东西。 hello 试图回到那个地方,这就是程序出现段错误的时候。

【讨论】:

  • 这就是我想知道的,谢谢
【解决方案2】:

这是未定义行为。当您踏入未分配的内存时,任何事情都可能发生,具体取决于目前存在的“垃圾”。

分段错误基本上意味着您退出了程序的受保护内存(在您的情况下,它花了“89 次”)。您的操作系统会保护您免受这种情况的影响,并告诉您您尝试在为程序分配的空间之外进行写入。

(在旧操作系统中并非如此,如果您的程序溢出到系统空间,这样的错误可能会导致您的操作系统崩溃。)

【讨论】:

    【解决方案3】:

    正如 Nahum 所说,这是未定义的行为,但这并不意味着它无法解释:

    100 - 89 = 11
    

    确切地说,这是本地堆栈的大小 (void *[10] + int)。

    试试这个(未定义的行为,可能会擦除您的硬盘或修复全球经济):

    #include<stdio.h>
    #include<stdlib.h>
    
    static int count = 0;
    
    static void hello(void){
        count ++;  
        fprintf(stderr,"hello! %d\n",count);
    }
    
    static void foo(void){
        void *buf[1];
        static int i;
    
        for(i = 0 ; i <= 100 + 2; i++){ // put enough data to overwrite the return address on stack
            buf[i] = hello;
        }
    }
    
    static void bye(void) {
        printf("Bye, bye...\n");
        exit(EXIT_SUCCESS);
    }
    
    int main(void){
        void *buf[1000];
        int i;
    
        for (i = 0; i < 1000; ++i) {
            buf[i] = &bye;
        }
        foo();
        return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 2021-12-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-11
      • 2010-12-14
      相关资源
      最近更新 更多