C和C++安全编码(原书第2版)
上QQ阅读APP看书,第一时间看更新

3.7 虚指针

在C++中可以定义虚函数(virtual function)。虚函数就是用virtual关键字声明的类成员函数。该函数可以由派生类中的同名函数重写。一个指向派生类对象的指针可以被赋给基类指针,并且通过该指针来调用函数。如果没有虚函数,则调用的是基类的函数,因为它和指针的静态类型相关联。当使用虚函数时,调用的则是派生类的函数,因为该函数和对象的动态类型相关联。

例3.10描述了虚函数的语义。类a是一个基类,它包含一个常规函数f()和一个虚函数g()。

类b派生自a,分别重写了这两个函数。在main()中,声明了一个指向基类的指针my_b,但它被赋值为指向一个派生类b对象。当在第25行中调用非虚函数my_b-﹥f()时,与a(基类)相关联的函数f()被调用。而当在第26行中调用虚函数my_b-﹥g()时,与b(派生类)相关联的函数g()被调用。

例3.10 虚函数的语义


01  class a {
02    public:
03      void f(void) {
04        cout << "base f" << '\n';
05      };
06
07      virtual void g(void) {
08        cout << "base g" << '\n';
09      };
10  };
11
12  class b: public a {
13    public:
14      void f(void) {
15        cout << "derived f" << '\n';
16      };
17
18      void g(void) {
19        cout << "derived g" << '\n';
20      };
21  };
22
23  int main(void) {
24    a *my_b = new b();
25    my_b->f();
26    my_b->g();
27    return 0;
28  }

大多数C++编译器使用虚函数表(Virual Function Table,VTBL)实现虚函数。VTBL是一个函数指针数组,用于在运行时派发虚函数调用。在每一个对象的头部,都包含一个指向VTBL的虚指针(Virtual Pointer,VPTR)。VTBL含有指向虚函数的每一个实现的指针。图3.2展示了例3.10中的数据结构。

图3.2 VTBL的运行时表示

覆写VTBL中的函数指针或者改变VPTR使其指向其他任意的VTBL都是可能的,可以通过任意内存写或者利用缓冲区溢出直接写入对象实现这一操作。通过对对象的VTBL和VPTR的覆写,攻击者可以使函数指针执行任意的代码。VPTR粉碎(VPTR smashing)攻击尚未泛滥,但在其他攻击手段都失效的情况下它就可能会被采用[Pincus 2004]。