码迷,mamicode.com
首页 > 其他好文 > 详细

缓存溢出问题简述

时间:2014-07-18 19:38:37      阅读:316      评论:0      收藏:0      [点我收藏+]

标签:style   color   os   文件   数据   art   

缓存溢出

 

缓存溢出(Buffer overflow),是指在存在缓存溢出安全漏洞的计算机中,攻击者能够用超出常规长度的字符来填满一个域,一般是内存区地址。这篇文章就是解说简单的缓存溢出问题。文章以x86_32 linux 系统平台为蓝本。为了介绍缓存溢出,数据的存储地址、基本的汇编指令、重要的寄存器等内容都要解说。

 

1.      变量存储

C语言中,变量属性有非常多中,可是对于缓存溢出问题,我们主要关心的数据的存储位置,或存储空间。因此,这里我们主要关心全局变量,局部变量和静态变量。在C语言中,通常全局变量和静态变量被分配于数据段(data section)中,可是局部变量分配在栈(stack)中。另外,大家清楚,栈空间中还存储CSIP等一些列与指令地址相关的关键数据,因此,在对局部变量进行数据拷贝的时候,假设拷贝数据块过大,就可能将IPCS等寄存器存放的数据空间覆盖掉,写入一些非法的数据,当从设IP值时,计算机就跳转到新的非法数据代码空间了。这里有一个简单的C文件,overflow1.c

int data = 0x66666666;

 

int func1(void)

{

        static int sdata = 0x55555555;

        int ret = 0;

        return ret;

}

 

int main(int argc, char *argvs[])

{

        func1();

        return 0;

}

 

我们首先对它进行编译,然后查看编译后的信息。

 

# gcc –o overflow1.o –c overflow1.c

# objdump –t overflow1.o

 

over3.o:     file format elf32-i386

 

SYMBOL TABLE:

00000000 l    df *ABS*  00000000 over3.c

00000000 l    d  .text  00000000 .text

00000000 l    d  .data  00000000 .data

00000000 l    d  .bss   00000000 .bss

00000004 l     O .data  00000004 sdata.1280

00000000 l    d  .note.GNU-stack        00000000 .note.GNU-stack

00000000 l    d  .comment       00000000 .comment

00000000 g     O .data  00000004 data

00000000 g     F .text  00000012 func1

00000012 g     F .text  0000001e main

 

       从结果中我们非常easy找到data sdata变量的size, section等信息。假设想获取变量存储的地址,能够连接后在运行该命令。可是对于局部ret,恐怕你不easy找到它的存放地址,那么ret的空间在哪里呢? 不要着急,我们继续。

       # objdump –d overflow1.o

 

over3.o:     file format elf32-i386

 

Disassembly of section .text:

 

00000000 <func1>:

   0:   55                      push   %ebp

   1:   89 e5                    mov    %esp,%ebp

   3:   83 ec 10                  sub    $0x10,%esp

   6:   c7 45 fc 00 00 00 00        movl   $0x0,-0x4(%ebp)

   d:   8b 45 fc                  mov    -0x4(%ebp),%eax

  10:   c9                       leave 

  11:   c3                       ret   

 

00000012 <main>:

  12:   8d 4c 24 04             lea    0x4(%esp),%ecx

  16:   83 e4 f0                and    $0xfffffff0,%esp

  19:   ff 71 fc                 pushl  -0x4(%ecx)

  1c:   55                      push   %ebp

  1d:   89 e5                   mov    %esp,%ebp

  1f:   51                      push   %ecx

  20:   e8 fc ff ff ff              call   21 <main+0xf>

  25:   b8 00 00 00 00           mov    $0x0,%eax

  2a:   59                      pop    %ecx

  2b:   5d                      pop    %ebp

  2c:   8d 61 fc                lea    -0x4(%ecx),%esp

  2f:   c3                      ret   

 

从汇编代码里面恐怕还是不明确ret在哪里存储吧?不用操心,我们回头看看C源文件,在func1函数里有一个 int ret = 0 的声明,聪明的你如今是不是找到相应的汇编语句了,你猜对了,就是以下这句话:

   6:   c7 45 fc 00 00 00 00        movl   $0x0,-0x4(%ebp)

 

也就是说,ret的空间分配在-0x4%(ebp)指向的空间中。从

1:   89 e5                    mov    %esp,%ebp

你会发现ebp存放的是esp的内容,也就说-0x4%(ebp)指向的是栈空间地址,而ret就存放在哪里。

 

如今明确了变量的存放空间了,以下我们要继续解说关于栈中其他的信息。

 

2.      重要指令和寄存器

为了更好的了解缓存溢出,就要了解与其相关的指令和寄存器。我在这部分内容中会解说这些信息。

IPCSebpesp是我们要讲的寄存器。CS:IP指向将要运行的指令的存储地址。一般的函数跳转指令和函数调用指令就是通过改动CS:IP的值来达到跳转目的。当指令段发生改变时,CS寄存器的数值才改变,在同一个指令段中,通常仅仅改变IP的数值就能够了。我们今天介绍的默认仅仅是通过改动IP的值而达到跳转目的。Esp寄存器存放的当前栈顶地址,而ebp作为一个备份寄存器,保存着进入新函数后esp的值。

 

在重要的指令中,主要有push, pop, call, ret, leave. Push pop是一对栈操作指令,push完毕入栈操作,将数据写入栈中,并更新esp的内容,而pop指令与其相反,它将数据从栈中取出,并更新esp的内容。 当中 call指令是函数调用指令,它主要完毕指令计数器寄存器(IP) 的入栈操作(为了简单期间,这里不考虑CS入栈问题,有兴趣的同学能够去查看引用文献),相似于指令 “push ip”。而ret指令与call指令相反,是一个函数返回指令,将IP的值从栈中弹出,相似”pop ip”Leave也能够看成一个符合指令,它相似于 “mov ebp, esp; pop ebp” 两个指令的效果。

 

了解了上面的基本知识,我们来分析一下汇编代码。为了清楚期间,我们将overflow1.o 进行连接,然后查看连接后的重要汇编代码片段。

# gcc –o overflow1 overflow.o

# objdump –d overflow1

…..

08048324 <func1>:

 8048324:       55                      push   %ebp

 8048325:       89 e5                    mov    %esp,%ebp

 8048327:       83 ec 10                 sub    $0x10,%esp

 804832a:       c7 45 fc 00 00 00 00        movl   $0x0,-0x4(%ebp)

 8048331:       8b 45 fc                  mov    -0x4(%ebp),%eax

 8048334:       c9                       leave 

 8048335:       c3                       ret   

 

08048336 <main>:

 8048336:       8d 4c 24 04             lea    0x4(%esp),%ecx

 804833a:       83 e4 f0                and    $0xfffffff0,%esp

 804833d:       ff 71 fc                pushl  -0x4(%ecx)

 8048340:       55                      push   %ebp

 8048341:       89 e5                   mov    %esp,%ebp

 8048343:       51                      push   %ecx

 8048344:       e8 db ff ff ff          call   8048324 <func1>

 8048349:       b8 00 00 00 00          mov    $0x0,%eax

 804834e:       59                      pop    %ecx

 804834f:       5d                      pop    %ebp

 8048350:       8d 61 fc                lea    -0x4(%ecx),%esp

….

 

从对func1函数開始调用開始,我们跟踪栈里面的内容。当call指令运行后,计算机会跳到func1函数处继续运行,如今就将这些指令合并:

8048344:       e8 db ff ff ff          call   8048324 <func1>

8048324:       55                      push   %ebp

8048325:       89 e5                    mov    %esp,%ebp

8048327:       83 ec 10                 sub    $0x10,%esp

804832a:       c7 45 fc 00 00 00 00        movl   $0x0,-0x4(%ebp)

运行后的结果是什么呢?

       第一条指令是将IP压栈;

       第二条指令将ebp压栈

       第三条是将esp的值保存到ebp

       第四条指令更新esp的值,向前16bytes

       第五条指令给ret赋初值0,而且能够确定ret的地址是%ebp - 4

 

因此,我们得到当前栈的值

                     IP                  0x88-0x8B      ;;High address 

Ebp->   oldEBP           0x84-0x87      ;; ebp = 0x84

        ret                 0x80-0x83

Nil                 0x7c-0x7f

Nil                 0x78-0x7b

Esp->   Nil                 0x74-0x77      ;; esp = 0x74

   ….                 ….                 ;;low address

 

而且ebp会作为备份寄存器保留老的esp寄存器的值,当函数返回时,还原espebp,以及IP。缓存溢出就是在还原之前首先将栈中IP的值改动成其余的数值,从而是CPU跳转到一个错误地址或无效地址。

假设按照上面栈的地址,那么ret变量的地址应该是0x80, 而老的IP数据的存储地址应该是0x88,假设在向ret进行数据拷贝时,数据过长,将会覆盖oldebpIP的地址,从而导致程序在返回时,将错误的IP值弹出到指令计数器中,CPU将会跳转到该错误地址进行代码运行。以下提供了两个案例程序,给大家參考。

Examples

 

Overflow2中是一个典型的内存溢出,作者通过向一个局部变量数组中写入过长数据,使程序无条件跳转的my_func() 一个非法函数中。

 

#include <stdio.h>

#include <string.h>

 

char strs[32] = {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, /

                 0xc4, 0x83, 0x04, 0x08, 0xc4, 0x83, 0x04, 0x08, /

                  0xc4, 0x83, 0x04, 0x08, 0xc4, 0x83, 0x04, 0x08} ;

/*my_func的地址是0x080483c4*/

int my_func(void)

{

        printf("in My Func!/n");

        return 87;

}

         

int print(void)

{        

        int tmp = 0x33;

        int ret = 0x22;

        char str[4];

        char *data;

        strncpy(str, strs, 24);

 

        return ret;

}

 

int main(int argc, char *argvs[])

{

        int ret = print();

 

        printf("ret = %x/n", ret);

 

        return 0;

}

 

 

Overflow3.c 是一个不通过函数调用,强制跳转到my_func()函数,并成功返回到主函数。

 

#include <stdio.h>

#include <string.h>

 

int my_func(void)

{

        printf("in My Func!/n");

        return 87;

}

 

int print(void)

{

        int ret = 0x22;

        int str[4];

 

        asm (                           /

                "mov 0(%%ebp), %%ebx;   /

                 mov %%eax, 0(%%ebp);   /

                 push %%eax;            /

                 sub $4, %%ebp;         /

                 mov %%ebx, 0(%%ebp)"   /

                :               /

                : "a"(my_func));

 

        return ret;

}

 

int main(int argc, char *argvs[])

{

        int ret = print();

        printf("ret = %x/n", ret);

 

        return 0;

}

 

 

Reference

缓存溢出问题简述,布布扣,bubuko.com

缓存溢出问题简述

标签:style   color   os   文件   数据   art   

原文地址:http://www.cnblogs.com/hrhguanli/p/3850559.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!