35

我有一个家庭作业要求我调用一个函数而不显式调用它,使用缓冲区溢出。代码基本上是这样的:

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

void g()
{
    printf("now inside g()!\n");
}


void f()
{   
    printf("now inside f()!\n");
    // can only modify this section
    // cant call g(), maybe use g (pointer to function)
}

int main (int argc, char *argv[])
{
    f();
    return 0;
}

虽然我不确定如何进行。我考虑过更改程序计数器的返回地址,以便它直接转到 g() 的地址,但我不确定如何访问它。无论如何,提示会很棒。

4

5 回答 5

14

基本思想是改变函数的返回地址,以便在函数返回时继续在新的被黑地址处执行。正如 Nils 在其中一个答案中所做的那样,您可以声明一块内存(通常是数组)并以这样的方式溢出它,即返回地址也被覆盖。

我建议您不要在没有真正了解它们的工作原理的情况下盲目地接受这里给出的任何程序。这篇文章写得很好,你会发现它非常有用:

缓冲区溢出漏洞的分步说明

于 2010-02-25T12:53:11.960 回答
12

这取决于编译器,因此无法给出单一答案。

以下代码将为 gcc 4.4.1 执行您想要的操作。在禁用优化的情况下编译(重要!)

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

void g()
{
    printf("now inside g()!\n");
}


void f()
{   
  int i;
  void * buffer[1];
  printf("now inside f()!\n");

  // can only modify this section
  // cant call g(), maybe use g (pointer to function)

  // place the address of g all over the stack:
  for (i=0; i<10; i++)
     buffer[i] = (void*) g;

  // and goodbye..
}

int main (int argc, char *argv[])
{
    f();
    return 0;
}

输出:

nils@doofnase:~$ gcc overflow.c
nils@doofnase:~$ ./a.out
now inside f()!
now inside g()!
now inside g()!
now inside g()!
now inside g()!
now inside g()!
now inside g()!
Segmentation fault
于 2010-02-25T12:39:06.640 回答
8

由于这是家庭作业,我想回应codeaddict 的建议,即了解缓冲区溢出的实际工作原理。

我通过阅读关于利用缓冲区溢出漏洞Smashing The Stack For Fun And Profit的优秀(如果有点过时)文章/教程来学习该技术。

于 2010-02-25T15:07:31.770 回答
4

试试这个:

void f()
{   
    void *x[1];
    printf("now inside f()!\n");
    // can only modify this section
    // cant call g(), maybe use g (pointer to function)
    x[-1]=&g;
}

或者这个:

void f()
{   
    void *x[1];
    printf("now inside f()!\n");
    // can only modify this section
    // cant call g(), maybe use g (pointer to function)
    x[1]=&g;
}
于 2010-02-25T12:30:12.543 回答
4

虽然这个解决方案没有使用溢出技术来覆盖函数在堆栈上的返回地址,但它仍然会导致在返回的路上被g()调用,只需要修改而不是直接调用。f()main()f()g()

添加了类似函数尾声的内联汇编f()来修改堆栈上的返回地址的值,以便f()通过g().

#include <stdio.h>

void g()
{
    printf("now inside g()!\n");
}

void f()
{   
    printf("now inside f()!\n");
    // can only modify this section
    // cant call g(), maybe use g (pointer to function)

    /* x86 function epilogue-like inline assembly */
    /* Causes f() to return to g() on its way back to main() */
    asm(
        "mov %%ebp,%%esp;"
        "pop %%ebp;"
        "push %0;"
        "ret"
        : /* no output registers */
        : "r" (&g)
        : "%ebp", "%esp"
       );
}

int main (int argc, char *argv[])
{
    f();
    return 0;
}

了解这段代码的工作原理可以更好地理解函数的堆栈帧是如何为构成缓冲区溢出技术基础的特定架构设置的。

于 2010-06-18T20:42:35.850 回答