【C++逆向】虚表(Virtualtable)逆向|安卓so虚函数逆向-创新互联

什么是多态

定义一个虚基类ISpeaker

创新互联建站为企业提供:品牌网站建设、网络营销策划、小程序定制开发、营销型网站建设和网站运营托管,一站式网络营销整体服务。实现不断获取潜在客户之核心目标,建立了企业专属的“营销型网站建设”,就用不着再为了获取潜在客户而苦恼,相反,客户会主动找您,生意就找上门来了!
class ISpeaker{
protected:
	size_t b;
public:
	ISpeaker( size_t _v )
		: b(_v) 
	{}
	virtual void speak() = 0;
};

有两个子类,都实现了虚函数speak():

class Dog : public ISpeaker {
public:
	Dog()
		: ISpeaker(0)
	{}
	//
	virtual void speak() override {
		printf("woof! %llu\n", b);
	}
};

class Human : public ISpeaker {
private:
	size_t c;
public:
	Human()
		: ISpeaker(1)
		, c(2)
	{}

	virtual void speak() override {
		printf("hello! %llu\n", c);
	}
};

main方法中把Dog*和Human*类型的指针,退化(强转)成ISpeaker*指针,并调用speak()方法:

int main( int argc, char** _argv ) {Human* pHuman = new Human();
	Dog* pDog = new Dog();
	//
	ISpeaker* speaker1 = (ISpeaker*)pHuman;
	ISpeaker* speaker2 = (ISpeaker*)pDog;
	//
	speaker2->speak();
	speaker1->speak();
	//
	return 0;
}

输出:

woof! 0
hello! 2

同样类型的指针(都是ISpeaker*),其函数表现出不同的行为(输出不同),称为多态

编译器如何实现多态

以MSVC编译器为例
在这里插入图片描述
编译器处理虚类ISpeaker时,生成内存模型(结构体)__ispeaker,其成员有虚表指针SpeakerTable* vt

Dog继承ISpeaker,所以对应的虚表类__dog会包含__ispeaker中的成员;
Human继承ISpeaker,所以对应的虚表类__human会包含__ispeaker中的所有数据,此外还有自己的数据size_t c
在这里插入图片描述
而后,编译器为每个类生成唯一的虚表实例
__dogSpeakerTable和__humanSpeakerTable是虚表实例

虚表实例中的函数指针指向对应的函数
在这里插入图片描述
所以Dog和Human,对应的虚表实例 中的函数指针指向的函数不同。

这就是为什么speaker2->speak();speaker1->speak();的输出不同。

在内存中,__dog和__humen的布局如下:

struct __dog {
	const SpeakerTable* vt;
	size_t b;
};

struct __human {
	const SpeakerTable* vt;
	size_t b;
	size_t c;
};

因此,想访问b或者c成员时,是按虚表对象之后的偏移量来访问的。

调用__dog_speak和__human_speak时会传递对象__dog和__human作为参数:

int main( int _argc, char** _argv ) {
    __dog* dog = createDog();
	__human* human = createHuman();
	//
	__ispeaker* speaker1 = (__ispeaker*)dog;
	__ispeaker* speaker2 = (__ispeaker*)human;
	//
	speaker1->vt->speak(speaker1);
	speaker2->vt->speak(speaker2);
	return 0;
}

访问b或c对象时,基于偏移量:

void __dog_speak( void* _ptr ) {uint8_t* p = (uint8_t*)_ptr;
	p+=sizeof(SpeakerTable*);
	size_t b = *((size_t*)p);
	printf("woof! %llu\n", b);
}

void __human_speak( void* _ptr ) {uint8_t* p = (uint8_t*)_ptr;
	p+=sizeof(SpeakerTable*);
	p+=sizeof(size_t);
	size_t b = *((size_t*)p);
	printf("hello! %llu\n", b);
}

例如uint8_t* p = (uint8_t*)_ptr;拿到__dog对象的起始偏移,
p+=sizeof(SpeakerTable*);是越过虚表对象,
size_t b = *((size_t*)p);就找到b对象了。

反编译so时虚函数调用长什么样

在这里插入图片描述


参考:
http://showlinkroom.me/2017/08/21/C-%E9%80%86%E5%90%91%E5%88%86%E6%9E%90/

https://www.bilibili.com/video/BV15g4y1a7F3/?spm_id_from=333.337.search-card.all.click&vd_source=0fcfb2f2f346ba4bccf7f3ee3eb4ae69

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


本文名称:【C++逆向】虚表(Virtualtable)逆向|安卓so虚函数逆向-创新互联
地址分享:http://scyanting.com/article/ephoj.html