玩转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++对象模型的一些有意思的东西,并没有具体分析,因为初窥嘛,以后会讲清楚的,请关注我呦。

请关注下一篇,谢谢。

玩转oo对象模型(1) 之 初窥c++对象模型,布布扣,bubuko.com

时间: 2024-08-03 14:23:32

玩转oo对象模型(1) 之 初窥c++对象模型的相关文章

Ioc容器Autofac系列(1)-- 初窥(转)

前言 第一次接触Autofac是因为CMS系统--Orchard,后来在一个开源爬虫系统--NCrawler中也碰到过,随着深入了解,我越发觉得Ioc容器是Web开发中必不可少的利器.那么,Ioc容器是用来做什么的?用了有什么好处?我相信如果不明白这两点就很难敞开心扉接受Ioc容器. 传统解耦设计的弊端 为方便描述,举个日志的栗子.我简化实现,一个Log类,一个SaveLog方法.如果其他类想拥有记日志功能,那么只需在内部包含一个Log类型的变量: 双击代码全选 1 2 3 4 5 6 7 8

Scrapy 1.4 文档 01 初窥 Scrapy

初窥 Scrapy Scrapy 是用于抓取网站并提取结构化数据的应用程序框架,其应用非常广泛,如数据挖掘,信息处理或历史存档. 尽管 Scrapy 最初设计用于网络数据采集(web scraping),但它也可用于使用 API(如 Amazon Associates Web Services)提取数据或用作通用的网络爬虫. 爬虫(spider)示例 为了向您展示 Scrapy 带给您的是什么,我们将使用最简单的方式运行一个爬虫,向您展示一个 Scrape Spider 的例子. 这是一个爬虫的

jQuery源码学习(2):选择器初窥

选择器初窥 代码架构: jQuery选择器可以依照传入数据的类型分为五大类: 传入字符串:$("div"), $("#id"), $(".div1"),$(".div p.title") 传入html代码:$("<div></div>"), $("<div>1</div><div>2</div>") 传入对象:$(d

Boost.ASIO简要分析-1 初窥

Boost.Asio是一个主要用于网络及底层I/O编程的跨平台C++库. 1. 初窥 Boost.Asio支持对I/O对象进行同步及异步操作. 1.1 同步操作 同步操作的事件顺序如下图所示: 1) 调用者调用I/O对象的connect函数开始连接操作,socket.connect(server_endpoint): 2) I/O对象将连接请求传递给io_service: 3) io_service调用操作系统函数: 4) 操作系统返回结果给io_service: 5) io_service将结

Swift初窥----深入Swift

存储函数 内存中的Fibonacci函数,避免重复递归,来提高代码执行效率 模板 编译器 Swift编译器,使其可以编译出更快的机器代码 Swift初窥----深入Swift,布布扣,bubuko.com

初窥netfilter/iptables

做这个东西太麻烦了,一不小心,就被自己关门外了. ---------------------------------------------- 一.前言 二.环境 三.语法解析 四.配置及测试 1.SNAT案例 2.DNAT案例 3.SSH案例 4.SSH深入案例(自定义规则) 5.web和ftp(自定义规则) 6.web和ftp(系统默认规则) 五.保存 ---------------------------------------------- 一.前言 iptables即Linux 内核集

初窥ElasticSearch

初窥ElasticSearch 官网上面的,不知道讲的是什么.. youtube上面有一个start with,内容是在windows下面跑这个elastic search,然后用一个fidler工具可视化测试 https://www.youtube.com/watch?v=60UsHHsKyN4 粗略看起来,其实es和其他db没什么大区别,只是在搜索上有很多强大功能,所以很适合用在需要搜索的项目.貌似用curl发送一个JSON格式的数据(实际上是命令)到es就可以做CRUD elasticse

C++拾遗(二)——初窥标准库类型

本篇博文的开始,先介绍一道书上看到的智力题:有20瓶药丸,其中19瓶装有1克/粒的药丸,余下一瓶装有1.1克/粒的药丸.有一台称重精准的天平,只是用一次天平的情况下如何找出比较重的那瓶药丸? 好了,直接公布答案.从药瓶#1取出一粒药丸,从药瓶#2取出两粒,从药瓶#3取出三粒,依此类推.如果每粒药丸均重1克,则称得总重量为210克(1 + 2 + … + 20 = 20 * 21 / 2 = 210),“多出来的”重量必定来自每粒多0.1克的药丸.药瓶的编号可由算式(weight - 210 gr

初窥CSS布局(一篇文章学会布局)

写一篇文章,难免要为之命名,所谓名不正,则言不顺:言不顺,则事不成.这篇文章是要说明一下CSS中的布局,实为入门之法矣. 本想命名为"布局说"的,但是总感觉题目太大,被大神们看到难免沦为笑柄,思来想去,便命名为"初窥CSS布局". 不管是写一个html页面,还是打算建一个网站,首先应该想的是怎么为之布局,这是常常让我头疼的事情,不知,这是否也曾困扰着 足下?您是怎么为页面布局的呢?是否有为页面布局的通用之法呢?我不知道.但是下文,就是从头到尾介绍了一种页面布局的方法