2012-03-02 113 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