zoukankan      html  css  js  c++  java
  • 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)

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

    待续....

    标题: 作者: 时间: 链接:
  • 相关阅读:
    [转]十步完全理解SQL
    [转]Java日期时间使用总结
    [转]Mybatis出现:无效的列类型: 1111 错误
    [转]java.lang.OutOfMemoryError: PermGen space及其解决方法
    [转]Spring3核心技术之事务管理机制
    [转]Spring的事务管理难点剖析(1):DAO和事务管理的牵绊
    设计模式之装饰模式
    进程通信
    设计模式之备忘录模式
    设计模式之单例模式
  • 原文地址:https://www.cnblogs.com/Lamboy/p/2580990.html
Copyright © 2011-2022 走看看