2012-03-02 110 views
2

我读了一些关于堆栈缓冲区溢出的文章,如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; 
} 

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

$ ./exp01

FUN2地址:0x8048452

在FUN1,缓冲= abcdefghijklmnopqrstuvR

HELLO FUN2!

HELLO fun2!

我们可以看到fun2()被成功调用,但我不知道它为什么会跑两次。然后我GDBed它(见下文)。 (我只知道关于GDB的一些基本指令(¯▽¯))

我搜索了一些关键词,比如“__libc_csu_fini()”,但没有找到明确的方法可以帮助我理解程序的执行路径。我对编译器和进程的内部结构知之甚少,所以我认为我可能不得不找到一些书或文章来详细描述这些东西。任何建议?谢谢!


GDB记录:

(GDB)列表

7的printf( “以FUN1,缓冲液=%S \ n” 个,缓冲液);

8}

10空隙FUN2(无效){

11的printf(“HELLO FUN2!\ n “);

12}

14 INT主(INT的argc,字符* argv的[])

15 {

16的printf(” FUN2的地址:%p \ n “个,FUN2);

(GDB)

17 FUN1(” abcdefghijklmnopqrstuv \ x52 \ x84 \ x04 \ x08“);

18 return 0;

19}

(GDB)16断裂

在0x804846f

断点1:文件hello.c,线16

(GDB)运行

启动程序:/家庭/渔梁/测试/你好

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

16 printf(“Fun2的地址:%p \ n”,fun2);

缺少单独debuginfos,使用:debuginfo软安装的glibc-2.13-2.i686

(GDB)步骤

FUN2的地址:0x8048452

17 FUN1(“abcdefghijklmnopqrstuv \ X52 \ X84 \ x04 \ x08“);

(GDB)

FUN1(输入= 0x804859a “abcdefghijklmnopqrstuvR \ 204 \ 004 \ B”)在的hello.c:6

6的strcpy(缓冲器,输入);

(GDB)

7的printf( “以FUN1,缓冲液=%S \ n” 个,缓冲液);

(GDB)

在FUN1,缓冲液= abcdefghijklmnopqrstuvR

8}

(GDB)

在hello.c的FUN2():10

10空隙fun2(void){

(gdb)

11 printf(“HELLO fun2!\ n”);

(GDB)

HELLO FUN2

12}

(GDB)

在__libc_csu_fini 0x08048500()

(GDB)

单步直到退出函数__libc_csu_fini,

没有行号信息。

在hello.c的FUN2():10

10空隙FUN2(无效){

(GDB)

11的printf( “!HELLO FUN2 \ n”);

(gdb)

HELLO fun2!

12}

(GDB)

在地址0x76757477

(GDB)

单步执行,直到从功能__libc_csu_init退出,

它没有行不能访问内存号码信息。

在__libc_start_main从/lib/libc.so.6

(GDB)

单步,直到从功能__libc_start_main退出,不具有行号信息

0x009aae36()。

程序与代码0241.

(GDB)退出

+0

Smashing the stack for fun and profit当您在FUN1没有的printf运行的程序将在程序执行两次呢? – Azrael3000 2012-03-02 10:00:14

+1

您需要使用机器代码调试器来浏览这些东西---查找gdb nexti,stepi和disas命令。在C模式下的调试器会变得非常困惑,因为它依赖于有效的栈帧来知道哪里正在执行什么,当然它们不再是了,因为你刚才改变了它们。 – 2012-03-02 11:14:03

+0

谢谢你的回复@ Azrael3000。我在fun2中注释掉printf。它回到main()中的printf中,并进入死循环。但fun2()的地址现在是0x804843e。如果我调用fun1(“abcdefghijklmnopqrstuv x3e x84 x04 x08”)而不是fun1(“abcdefghijklmnopqrstuv x52 x84 x04 x08”),fun2仍然运行两次。 – Gnailuy 2012-03-02 11:18:34

回答

0

当在gdb休息运行程序不久之前strcpy()并看看堆栈帧(即在保存EIP被储存了)。然后运行,直到printf()后不久,然后再次查看存储的eip(命令是info frame)。

因为你传递的功能fun2()地址fun1()它将覆盖保存的EIP,并尽快回叫(隐含在你的情况下),下一条指令将被执行(这是由EIP和你的情况给它是)的fun2()地址

而且不要忘了阅读由aleph1

+0

谢谢@dwalter!我知道fun2()会在fun1()返回后运行,但我不知道为什么它会在我的机器上运行两次。似乎这不是每台机器和每个操作系统上的可重现问题,至少不是我朋友之一的Gentoo。稍后我会尝试您的方法以获取堆栈的更多信息。而且,谢谢你的推荐! – Gnailuy 2012-04-08 08:31:35