Linux下棧溢出的初步

由於是剛剛學習一下Linux下的棧溢出,所以對於GDB調試也很不熟悉。以前在windows下做過棧溢出,感覺和linux的原理是相同的,就是在linux用GDB調試沒有在windows下用OllyDbg方便。

首先存在溢出的代碼:

#include"stdio.h"

void return_input(void)

{

char array[30];

gets(array);

printf("%s\n",array);

}

main()

{

return_input();

return 0;

}

gcc3.4.6編譯:

gcc -mpreferred-stack-boundary=2 -ggdb overflow.c -o overflow

其中“-mpreferred-stack-boundary=2 ”是使編譯的時候棧以雙字節爲單位遞增(或遞減),否則gcc將對棧進行優化。 “-ggdb”是讓編譯後的程序支持gdb調試。

#gdb ./overflow

 

GNU gdb (GDB) 7.1-ubuntu

Copyright (C) 2010 Free Software Foundation, Inc.

License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software: you are free to change and redistribute it.

There is NO WARRANTY, to the extent permitted by law.  Type "show copying"

and "show warranty" for details.

This GDB was configured as "i486-linux-gnu".

For bug reporting instructions, please see:

<http://www.gnu.org/software/gdb/bugs/>...

Reading symbols from /root/Desktop/code/overflow...done.

查看input_return()的反彙編代碼:

 

(gdb) disas return_input

Dump of assembler code for function return_input:

   0x0804837c <+0>:push   %ebp

   0x0804837d <+1>:mov    %esp,%ebp

   0x0804837f <+3>:sub    $0x28,%esp

   0x08048382 <+6>:lea    -0x20(%ebp),%eax

   0x08048385 <+9>:mov    %eax,(%esp)

   0x08048388 <+12>:call   0x80482cc <gets@plt>

   0x0804838d <+17>:lea    -0x20(%ebp),%eax

   0x08048390 <+20>:mov    %eax,0x4(%esp)

   0x08048394 <+24>:movl   $0x804847c,(%esp)

   0x0804839b <+31>:call   0x80482ec <printf@plt>

   0x080483a0 <+36>:leave  

   0x080483a1 <+37>:ret    

End of assembler dump.

需要注意的是linux的AT&T格式的彙編與Linux下的不同

AT&T的彙編語法中目標操作數在原操作數的右邊

兩個call分別是gets函數和Printf函數,測試發現當輸入的字符大於35時將報錯

 

root@bt:~/Desktop/code# ./overflow

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

 

 

Segmentation fault

給兩個函數分別下斷點:

(gdb) break *0x08048388

Breakpoint 1 at 0x8048388: file overflow.c, line 6.

(gdb) break *0x0804839b

Breakpoint 2 at 0x804839b: file overflow.c, line 7.

然後運行:

(gdb) run

Starting program: /root/Desktop/code/overflow 

 

Breakpoint 1, 0x08048388 in return_input () at overflow.c:6

6gets(array);

程序中斷在第一個斷點(return_input函數)

先看看main函數的彙編代碼:

(gdb) disas main

Dump of assembler code for function main:

   0x080483a2 <+0>:push   %ebp

   0x080483a3 <+1>:mov    %esp,%ebp

   0x080483a5 <+3>:call   0x804837c <return_input>

   0x080483aa <+8>:mov    $0x0,%eax

   0x080483af <+13>:pop    %ebp

   0x080483b0 <+14>:ret    

End of assembler dump.

有windows下溢出基礎的都知道,現在函數執行到return_input(),那麼要返回到main函數,那麼返回地址保存在棧當中,返回地址就是call   0x804837c <return_input> 這條指令的下一條指令的地址,即0x080483aa,好,現在我們看看堆棧的情況:

 

(gdb) x/20x $esp

0xbffff4f8:  0xbffff500   0x080483e9   0xb7fc9324   0xb7fc8ff4

0xbffff508: 0x080483d0  0xbffff528   0xb7ea24a5   0xb7ff1030

0xbffff518: 0x080483db  0xb7fc8ff4   0xbffff528    0x080483aa

0xbffff528:  0xbffff5a8   0xb7e89bd6  0x00000001  0xbffff5d4

0xbffff538:  0xbffff5dc   0xb7fe1858   0xbffff590   0x0177ff8e

可以看到main函數的返回的地址保存在0xbffff518 +0x0c處

現在我們嘗試輸入35個A看看

 

(gdb) continue

Continuing.

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

 

Breakpoint 2, 0x0804839b in return_input () at overflow.c:7

7printf("%s\n",array);

gets函數得到執行,現在看看堆棧的情況:

 

(gdb) x/20x 0xbffff4f8

0xbffff4f8:0x0804847c  0xbffff500  0x61616161  0x61616161

0xbffff508:0x61616161  0x61616161  0x61616161  0x61616161

0xbffff518:0x61616161  0x61616161  0x00616161  0x080483aa

0xbffff528:0xbffff5a8  0xb7e89bd6  0x00000001  0xbffff5d4

0xbffff538:0xbffff5dc  0xb7fe1858  0xbffff590  0x0177ff8e

很好,我們看到現在返回地址前面的棧空間已經被大量的61(‘a’的ASCII碼)覆蓋,這也解釋

了爲什麼輸入36個字符會崩潰。注意字符這個字符串是以'\0'結尾的,還有就是注意內存的大小

端問題。

現在就是要覆蓋這個返回地址,當執行ret指令的時候將會把這個返回地址彈出給EIP,這樣,我們

就可以用錯誤的返回地址控制EIP,改變程序的執行流程。

第二次調試我們嘗試覆蓋返回地址,用40個‘a’。

分別在gets和ret上設置斷點:

 

(gdb) break * 0x08048388

Breakpoint 1 at 0x8048388: file overflow.c, line 6.

(gdb) break * 0x080483a1

Breakpoint 2 at 0x80483a1: file overflow.c, line 8.

然後繼續執行程序:

 

(gdb) continue

Continuing.

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

 

Breakpoint 2, 0x080483a1 in return_input () at overflow.c:8

8}

查看一下堆棧:

 

(gdb) x/20x 0xbffff4f8

0xbffff4f8:0x0804847c  0xbffff500  0x61616161   0x61616161

0xbffff508:0x61616161  0x6161616  10x61616161  0x61616161

0xbffff518:0x61616161  0x61616161  0x61616161  0x61616161

0xbffff528:0xbffff500   0xb7e89bd6  0x00000001  0xbffff5d4

0xbffff538:0xbffff5dc  0xb7fe1858   0xbffff590   0x0177ff8e

可以看到返回地址已經被完全覆蓋

看看EIP寄存器:

 

(gdb) x/1i $eip

=> 0x80483a1 <return_input+37>:ret   

然後單步執行ret指令

 

(gdb) stepi

0x61616161 in ?? ()

(gdb) x/1i $eip

=> 0x61616161:Cannot access memory at address 0x61616161

看到EIP已經被我們輸入的字符所控制,當然如果這裏放是一段惡意代碼(比如shellcode)

那麼程序的執行流程就可以完全被我們的輸入所控制,,,

待續....

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章