【问题标题】:Weird execution path caused by stack buffer overflow堆栈缓冲区溢出导致的奇怪执行路径
【发布时间】:2012-03-20 19:38:09
【问题描述】:

我阅读了一些关于堆栈缓冲区溢出的文章,例如this,并了解了攻击者如何通过覆盖函数指针来利用堆栈缓冲区溢出漏洞。然后我写了一个小程序来演示一个攻击:

#include <stdio.h>
#include <string.h>

void fun1 ( char * input ) {
    char buffer[10];
    strcpy( buffer, input );
    printf( "In fun1, buffer= %s\n", buffer );
}

void fun2 ( void ) {
    printf ( "HELLO fun2!\n" );
}

int main ( int argc, char * argv[] )
{
    printf ( "Address of fun2: %p\n", fun2 );
    fun1( "abcdefghijklmnopqrstuv\x52\x84\x04\x08" );
    return 0;
}

该程序使用 GCC 4.5.1 在 Fedora 14 x86 下编译。下面是输出:

$ ./exp01

fun2的地址:0x8048452

在 fun1 中,buffer=abcdefghijklmnopqrstuvR...

你好 fun2!

你好 fun2!

我们可以看到 fun2() 被成功调用了,但是我不知道为什么它运行了两次。然后我对它进行了 GDBed(见下文)。 (我只知道一些关于GDB的基本说明╮( ̄▽ ̄)╭)

我google了一些关键词,比如“__libc_csu_fini()”,但没有找到一个清晰的方法可以帮助我理解程序的执行路径。我对编译器和进程的内部结构知之甚少,所以我认为我可能必须找到一些详细描述这些东西的书籍或文章。有什么建议吗?谢谢!


GDB 记录:

(gdb) 列表

7 printf("in fun1, buffer= %s\n", buffer);

8 }

9

10 void fun2 ( void ) {

11 printf ( "HELLO fun2!\n" );

12 }

13

14 int main (int argc, char * argv[])

15 {

16 printf("fun2 的地址:%p\n", fun2);

(gdb)

17 fun1("abcdefghijklmnopqrstuv\x52\x84\x04\x08");

18 返回 0;

19 }

(gdb) 中断 16

0x804846f 处的断点 1:文件 hello.c,第 16 行。

(gdb) 运行

启动程序:/home/yuliang/test/hello

断点 1,主要 (argc=1, argv=0xbffff394) 在 hello.c:16

16 printf("fun2 的地址:%p\n", fun2);

缺少单独的调试信息,使用:debuginfo-install glibc-2.13-2.i686

(gdb) 步骤

fun2的地址:0x8048452

17 fun1("abcdefghijklmnopqrstuv\x52\x84\x04\x08");

(gdb)

fun1 (input=0x804859a "abcdefghijklmnopqrstuvR\204\004\b") at hello.c:6

6 strcpy(缓冲区,输入);

(gdb)

7 printf("in fun1, buffer= %s\n", buffer);

(gdb)

在 fun1 中,buffer=abcdefghijklmnopqrstuvR...

8 }

(gdb)

fun2 () at hello.c:10

10 void fun2 ( void ) {

(gdb)

11 printf ( "HELLO fun2!\n" );

(gdb)

你好 fun2!

12 }

(gdb)

__libc_csu_fini()中的0x08048500

(gdb)

单步执行直到退出函数 __libc_csu_fini,

没有行号信息。

fun2 () at hello.c:10

10 void fun2 ( void ) {

(gdb)

11 printf ( "HELLO fun2!\n" );

(gdb)

你好 fun2!

12 }

(gdb)

无法访问地址 0x76757477 处的内存

(gdb)

单步执行直到退出函数 __libc_csu_init,

没有行号信息。

0x009aae36 in __libc_start_main () from /lib/libc.so.6

(gdb)

单步执行直到退出函数 __libc_start_main,

没有行号信息。

程序以代码 0241 退出。

(gdb)

【问题讨论】:

  • 当你在 fun1 中不使用 printf 运行程序时,程序也会执行两次吗?
  • 您需要使用机器代码调试器来单步调试这些东西 --- 查找 gdb nexti、stepi 和 disas 命令。 C 模式下的调试器会变得非常混乱,因为它依赖于有效的堆栈帧才能知道在哪里执行了什么,当然它们不再是了,因为您刚刚更改了它们。
  • 感谢您的回复@Azrael3000。我注释掉 fun2 中的 printf。它回到 main() 中的 printf,并进入死循环。但是 fun2() 的地址现在是 0x804843e。如果我调用 fun1("abcdefghijklmnopqrstuv\x3e\x84\x04\x08") 而不是 fun1("abcdefghijklmnopqrstuv\x52\x84\x04\x08"),fun2 仍然运行两次。
  • 非常有用的信息。我真的需要更多地了解 gdb。谢谢@DavidGiven
  • 我同意大卫的观点。调试器将依赖于拥有一个有效的堆栈指针并从那里展开堆栈。

标签: c buffer-overflow


【解决方案1】:

在 gdb 中运行程序时,在strcpy() 之前不久,请查看堆栈帧(即保存 eip 的位置)。然后运行到printf() 之后不久,再次查看存储的eip(命令为info frame)。

由于您将函数fun2() 的地址传递给fun1(),它将覆盖保存的eip,并且一旦调用return(在您的情况下是隐式的)就会执行下一条指令(由eip 和in你的情况是fun2()的地址)

别忘了阅读 aleph1 的 Smashing the stack for fun and profit

【讨论】:

  • 谢谢@dwalter!我知道 fun2() 将在 fun1() 返回后运行,但我不知道为什么它在我的机器上运行了两次。似乎这在每台机器和每个操作系统上都不是一个可重现的问题,至少在我朋友的 Gentoo 上不是。稍后我将尝试您的方法以获取有关堆栈的更多信息。还有,感谢您的精彩推荐!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-11
相关资源
最近更新 更多