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)

那么程序的执行流程就可以完全被我们的输入所控制,,,

待续....

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