零度资讯 - 文章、资讯、攻略、教程随你看!
您的位置:零度软件下载程序设计C&C++ → 突破C++的虚拟指针--C++程序的缓冲区溢出攻击

突破C++的虚拟指针--C++程序的缓冲区溢出攻击

发表时间:09-04-23     作者:佚名     阅读:次     评论:0字体大小:A- A+

本文导读:backend注:本文来自Phrack56期的《SMASHING C++ VPTRS》。正如大多数国外黑客的文章,技术原理及应用都讲得比较详细,但所提供的源代码似乎总是会存在不大不小的问题。这也许是因为他们觉得应该让读者自己去研究和调试,以更好地掌握这些技术。或许以后..
backend注:本文来自Phrack56期的《SMASHING C++ VPTRS》。正如大多数国外黑客的文章,技术原理及应用都讲得比较详细,但所提供的源代码似乎总是会存在不大不小的问题。这也许是因为他们觉得应该让读者自己去研究和调试,以更好地掌握这些技术。或许以后我也会这样做。;)

测试环境:

  操作系统:Red Hat 6.1 (i386)
  内核版本:Kernel 2.2.14
  内核补丁:None        Non-executable stack patch (by Solar Design)
  C++编译器:gcc
  

---[[ 前言 ]]--------------------------------------

  到目前为止,我所掌握的缓冲区溢出程序都是针对C编程语言的。虽然C语言编程在UNIX系统中几乎无处不在,但越来越多的C++程序也开始出现了。对于大多数情况,C语言的溢出技术对于C++语言也是适用的,但C++的面向对象的特性也导致了新的缓冲区溢出技术。下面以x86 Linux系统和C++ GNU编译器为平台进行分析。


---[[ 基础--简单的C++程序 ]]--------------------------------------

  我不愿在这里浪费时间讲解太多的C++语言基础。如果你对C++或面向对象编程技术一无所知,请先找本这方面的书籍看看。在继续往下看之前,请确认你已经掌握或了解以下C++术语:
  
  1、Class(类)
  2、Object(对象)
  3、Method(方法)
  4、Virtual(虚拟)
  5、Inherit(继承)
  6、Derivative(派生)

  接着,把下面的两个程序看完,确认你了解每条语句的含义和作用:
  
// bo1.cpp
// C++基础程序

#include <stdio.h>
#include <string.h>

class MyClass
{
  private:
    char Buffer[32];
  public:
    void SetBuffer(char *String)
    {
      strcpy(Buffer, String);
    }
    void PrintBuffer()
    {
      printf("%s\n", Buffer);
    }
};

void main()
{
   MyClass Object;

   Object.SetBuffer("string");
   Object.PrintBuffer();
}

===========================================================

// bo2.cpp
// 有缓冲区溢出漏洞的常见C++程序

#include <stdio.h>
#include <string.h>

class BaseClass
{
  private:
    char Buffer[32];
  public:
    void SetBuffer(char *String)
    {
      strcpy(Buffer,String); // 存在缓冲区溢出漏洞
    }
    virtual void PrintBuffer()
    {
      printf("%s\n",Buffer);
    }
};

class MyClass1:public BaseClass
{
  public:
    void PrintBuffer()
    {
      printf("MyClass1: ");
      BaseClass::PrintBuffer();
    }
};

class MyClass2:public BaseClass
{
  public:
    void PrintBuffer()
    {
      printf("MyClass2: ");
      BaseClass::PrintBuffer();
    }
};

void main()
{
  BaseClass *Object[2];

  Object[0] = new MyClass1;
  Object[1] = new MyClass2;

  Object[0]->SetBuffer("string1");
  Object[1]->SetBuffer("string2");
  Object[0]->PrintBuffer();
  Object[1]->PrintBuffer();
}

  以下是bo2.cpp编译后的运行结果:

[backend@isbase test]> ./bo2
MyClass1: string1
MyClass2: string2
[backend@isbase test]>

  再一次提醒,在继续往下看时,确信你读懂了上面的程序,特别是对象虚拟(virtual)方法PrintBuffer()。与SetBuffer()方法不同,PrintBuffer方法必须在基类BaseClass的派生类MyClass1和MyClass2中声明并实现。这使得SetBuffer与PrintBuffer方法在运行时的处理会有所不同。


---[[ C++的虚拟指针(Virtual PoinTeR,VPTR)]]--------------------------------------

  我们知道,虚拟方法与非虚拟方法的一个不同之处是,非虚拟方法的调用是在编译时确定(通常称为“静态绑定”),而虚拟方法的调用却是在程序时确定的(通常称为“动态绑定”)。下面以上例中的BaseClass基类及其派生类为例,对动态绑定的机制做一些解释。

  编译器在编译时首先检查BaseClass基类的声明。在本例,编译器首先为私有变量Buffer(字符串型)保留32个字节,接着为非虚拟方法SetBuffer()计算并指定相应的调用地址(静态绑定处理),最后在检查到虚拟方法PrintBuffer()时,将做动态绑定处理,即在类中分配4个字节用以存放该虚拟方法的指针。结构如下:

    BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBVVVV

说明: B 变量Buffer占用。
    V 虚拟方法指针占用。

  这个指针通常被称为“VPTR”(Virtual Pointer),它指向一个“VTABLE”结构中的函数入口之一。每一个类都有一个VTABLE。如下图所示:

Object[0]: BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBVVVV
                      =+==
            |
     +------------------------------+
     |
     +--> VTABLE_MyClass1: IIIIIIIIIIIIPPPP

Object[1]: BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBWWWW
                      =+==
            | [][]     +------------------------------+
     |
     +--> VTABLE_MyClass2: IIIIIIIIIIIIQQQQ

说明: B 变量Buffer占用。
    V 指向VTABLE_MyClass1的VPTR指针占用。
    W 指向VTABLE_MyClass2的VPTR指针占用。
    I 其它用途的数据
    P MyClass1对象实例的PrintBuffer()方法的地址指针。
    Q MyClass2对象实例的PrintBuffer()方法的地址指针。

  我们可以发现,VPTR位于进程内存中Buffer变量之后。即当调用危险的strcpy()函数时有可能覆盖VPTR的内容!
  
  根据rix的研究测试,对于Windows平台上的Visual C++ 6.0,VPTR位于对象的起始位置,因此这里提到的技术无法产生作用。这点与GNU C++有很大的不同。


---[[ 剖析VPTR ]]--------------------------------------

  在Linux下当然是使用GDB来分析了:

[backend@isbase test]> gcc -o bo2 bo2.cpp
[backend@isbase test]> gdb bo2
GNU gdb 4.18
Copyright 1998 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux"...
(gdb) disassemble main
Dump of assembler code for function main:
0x8049400 <main>:    push  %ebp
0x8049401 <main+1>:   mov  %esp,%ebp
0x8049403 <main+3>:   sub  $0x8,%esp
0x8049406 <main+6>:   push  %edi
0x8049407 <main+7>:   push  %esi
0x8049408 <main+8>:   push  %ebx
0x8049409 <main+9>:   push  $0x24
0x804940b <main+11>:  call  0x804b580 <__builtin_new>
0x8049410 <main+16>:  add  $0x4,%esp
0x8049413 <main+19>:  mov  %eax,%eax
0x8049415 <main+21>:  mov  %eax,%ebx
0x8049417 <main+23>:  push  %ebx
0x8049418 <main+24>:  call  0x804c90c <__8MyClass1>
0x804941d <main+29>:  add  $0x4,%esp
0x8049420 <main+32>:  mov  %eax,%esi
0x8049422 <main+34>:  jmp

阅读本文后您有什么感想? 已有 1 人给出评价!

  • 0

    欢迎
  • 0

    白痴
  • 0

    拜托
  • 0

    惊讶
  • 0

    加油
  • 0

    鄙视
相关资讯
相关软件
最新评论
我要抢沙发
(您的评论需要经过审核才能显示)0人参与,0条评论
140

零度软件 - 所有软件均来自网络如有版权问题请联系我们 - 蜀ICP备 05031544号
Copyright © 2004-2016 www.05sun.com online services. All rights reserved.