首页 > 代码库 > 玩转oo对象模型(1) 之 初窥c++对象模型

玩转oo对象模型(1) 之 初窥c++对象模型

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

copyright: KIRA-lzn

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

转载请注明出处,这篇是我原创,翻版必究!

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

第二篇,如果写的好,请点个赞呦。当然有不对之处,非常欢迎拍砖!!!!

 

自我介绍: USTC研一学生,目前在intel实习,研发岗

 

第二篇来了,这次篇幅不能那么长了,得简短一点了,xixi。说好的oo对象模型,我一定能讲清楚,很久以前自己就已经写过至少5篇关于object model的文章,只是自己写着玩的,自己保存用的,怕以后忘记了,这次既然要发文章,就好好把以前的东西有条理的整理下。

 

1. 本人读过inside the c++ object model ,  effective c++ , more effective c++ , c++沉思录,c++ primer 等书

2. 编译器写过 minijava -> c 的代码生成

3. 研究过信息安全相关

所以深知c++对象模型的机理,才敢操刀 oo对象模型的相关文章。

 

 

首先还是用我自己的话给 object model 下一个定义:

当你用  Base* b = new Base(); 的时候,你应该考虑,此时发生了什么?考虑内存怎么样分配的? 此时是单一继承呢,还是多重继承呢? 虚函数表应该长成什么样? 多重继承又因为比如(微软专利:虚基类虚函数表技术)菱形多重继承导致的数据二义性问题呢?最最关键的,b指向内存中的哪里??

放心吧,这些我都会讲清楚的。但我还是喜欢一步一步深入,带我一步一步引导,不是隐刀,和dota没关系,要不写篇dota攻略吧,哈哈

 

先看一段我认为入门最好的,最有趣的小程序,可能有点毁三观(第一反应:这tm也行?怎么做到的?,高手飘过,谢谢),不过没关系,这可以充分展示对象模型。

请欣赏:毁三观4部曲

note:

毁三观: 这tm也行?

 

1.第一毁

#include <iostream>

using namespace std;

class Base{

public:
    virtual void f(){cout<<"Base"<<endl;}                        
};

class Derived:public Base
{
private:
    void f(){cout<<"Derived"<<endl;}
};

int main()
{
    Base* bp = new Derived();
    bp->f();
    return 0;
}

答案是什么?

毫不犹豫的回答,输出 Derived 

ok,答案是没问题了,这个不就是多态嘛? 没错,就是多态,但是注意,

注意,我飘红的关键字,这里好像违反了c++语法?

因为对象是不能直接访问类的private成员函数的,对不对?

 

这tm也行?

如果不深入探讨,仅仅认为就是多态而已,当然也行。。。不过打破砂锅问到底的精神还是值得赞扬的!!!!!

note:

注意,我举这个例子的更深层次的意图:这就是为什么说 多态 ,是运行期绑定?

提前给出答案:后面有详细解释,不懂没关系。

是吧,运行期绑定,我可以负责任(写过编译器)的告诉你,编译期的时候编译器根本不知道,只有当你程序跑起来的时候,去相应位置调用了相应位置的函数指针,才知道你到底运行的是什么函数,so,运行期(动态)绑定就是这个原因!

 

好吧,第一个程序如果还是不过瘾,请欣赏第二个程序。

2.第二毁

#include <iostream>

using namespace std;

class Base{

public:

    virtual void f(){cout<<"Base"<<endl;}

};

class Derived:public Base {

private:

     void f(){cout<<"Derived"<<endl;}

};

 typedef void (*Func)();

int main() {

    Base* bp = new Derived();

    Func fp = (Func)*(int*)*(int*)bp;

    fp(); 

    return 0;

}

 

这tm是什么?为什么fp尽然能调用bp对象模型里面的函数?这里面有什么猫腻吗?

此时应该引发思考,bp指向的对象模型到底长成神马模样?能让 人 如此用指针摧残?

 

 

ok,如果还不过瘾,我再敲一个,更毁三观的程序,可以叫它   ->   hook虚函数表 , 不行,这样叫不准确,就叫他 object hook 吧,其实就hook了一个函数指针而已。

 

3.第三毁

 

#include <iostream>

using namespace std;

class Base{

public: virtual void f(){cout<<"Base"<<endl;}

};

class Derived:public Base {

private: void f(){cout<<"Derived"<<endl;}

};

void hookf() { cout<<"you are being hooked"<<endl; }

typedef void (*Func)();

int main()

{

Base* bp = new Derived();

Func fp = (Func)*(int*)*(int*)bp;

*fp = hookf;

bp->f();

return 0;

}

 

注意,这里*fp = hookf; 是编译不通过的,因为 vtable是被编译到 .data段 ,是只读的,g++下:

a.cpp:29:11: error: assignment of read-only location ‘* fp’

但是,在windows下,没有神马是不能修改的,比如(VirtualProtect,VirtualProtectex,修改CR0的写保护位,MDL等等),这里不详细讨论。

所以说,如果能改,则hook一个函数指针有多么容易???

 

如果改了之后,bp->f();输出什么,应该很明了了。

 

4.第四毁,来了,不像3一样,4是可以直接跑的,所以说,c++对象模型中。。vptr的设计。。。 -> hook 整张虚函数表

windows下cl.exe编译 , linux 下 g++ 编译均可以。

这个是我写的 监控虚函数表差异定位 时候的程序,我简化到最简。

#include <iostream>

#include <string.h>

#include <stdlib.h>

using namespace std;

void Hook_all_Vtable(int* pObjectBase);

void hookfunc();

class Game {

int a;

int b;

string s;

public:

virtual void add(){cout<<"virtual function add() being called:0x"<<endl;}

virtual void sub(){cout<<"virtual function sub() being called:0x"<<endl;}

virtual void mul(){cout<<"virtual function mul() being called:0x"<<endl;}

virtual void divv(){cout<<"virtual function div() being called:0x"<<endl;}

};

void hookfunc() { cout<<"you are being hooked."<<endl; }

 

typedef void (*Func)();

int main() {

Game* gp = new Game();

Hook_all_Vtable((int*)gp);

gp->add();

return 0;

}

void Hook_all_Vtable(int* pObjectBase){

int* allocp = (int*)calloc(1,100*sizeof(long));

memcpy(allocp,(void*)(*pObjectBase),100*sizeof(long));

*pObjectBase = (int)allocp;

*allocp = (int)hookfunc;

}

程序的细节,不做任何过多的解释,初窥嘛~,细节请听下回分解。

此时,一段可以直接跑的程序出炉了。

输出什么,相信大家很明白。

这tm就随意的就hook整张虚函数表了??????

c++对象模型到底长成神马模样??

 

是不是有点意思了?????????

通过这段最最简单的hook虚函数表程序,大家可以发现,其实玩转c++对象模型其实不是难事,希望大家跟着我一点一点学习,我保证能解释清楚。

 

 

最后还是要总结一下下:

note:

注意,所以这里说,c++有指针,所以可以肆意访问c++的对象模型,去hook里面的对象模型里的函数指针,比如堆溢出,对喷射等技术,所以c++对象模型在所有人都是好人的情况下,模型是完美的,是B.lippman在cfront的时候设计出来的完美模型,但是。

所以说,java做的比较彻底,没有指针,就不让你用指针,这样你怎么去遍历java对象模型的函数指针呢?是不是变得更安全了???而且java还有反射机制,也是存在于java对象模型中,所以,就是java,就是不让你用指针。就用reference吧。

but,我要说明一下:

不要认为c++对象模型不安全就不用c++,hook函数指针,不是因为c++对象模型的设计而造成的,说白了,

就是结构体里面有函数指针,然后函数指针被hook了,就这么简单!我们亲切的称之为 object hook

只要你用oo,注意,不是c++能oo,但是c不能oo,其实c也能oo,很easy(自己搞vptr和vtable)

所以,基本上,只要你用oo,就会存在函数指针,就有可能被hook,所以错不在c++,请大家明示!!! 但是c++允许指针去遍历虚函数表,这就不对了,呼呼。

java么有指针,看,多彻底??

 

注意:c++的虚函数表,是被编译器编译到 .rodata 段,即只读段,你是不能直接修改虚函数表里面的函数指针,当然在windows下,没有神马是不能修改的,比如(VirtualProtect,VirtualProtectex,修改CR0的写保护位,MDL等等),但是可以修改vptr的指向,即可以hook整张虚函数表,因为vptr是在结构体里,当然看看你是分配在 栈上,还是堆里,还是.data段咯,这些都是可以直接修改(参考第4个程序)的, .text 和 .rodata是只读的。

 

note:

这里插播一条我写的小程序,和对象模型没关系,仅供娱乐:一定要运行一下,看下输出,嘻嘻。

#include <stdio.h>

int main()

{

    int i = 0x00752065;

    int j = 0x00766f6c;

    short k = 0x0049;

    printf("%s %s%s",&k,&j,&i);

    return 0;

}

 

note:

由以上语言,当年一起开发c编译器和unix的男银最后得了图灵奖,但是c++这么庞大编译器的男银却没有得图灵奖,这是又说明了神马??

(又想到了特洛伊木马,有兴趣的童鞋可以自行去了解,蛋生鸡鸡生蛋的问题,得想清楚呦)

各位看官有自己的观点,是吧。

 

当然第一篇,只是随便谈谈c++对象模型的一些有意思的东西,并没有具体分析,因为初窥嘛,以后会讲清楚的,请关注我呦。

请关注下一篇,谢谢。