1.如果类里面有虚函数,类前4个字节是个虚函数表的指针.(64位是前8字节)。
2.会和结构体一样,存在内存对齐行为(这里就不展开了)。
3.类的成员函数(非虚函数,虚函数会放在虚表里)不会占用类的内存。
1.例子中A和C中都有虚函数,B继承了A和C,B的内存中就有两个虚函数表;
2.内存布局和声明的顺序有对应关系,具体内存排列顺序请看 1的内容。
1自己的虚表还是首地址的地方,它和第一个虚表融合到了一起。(请看下面图片讲解)
1:B类的原型
2:C类的原型
3:C类内存结构,红色是他继承的 B A D 类,绿色是自己成员x内存。
4:查看下第一个也就是 0x000000014001C9A8地址。
你会发现原本B类里面只有三个虚函数此时内存中有4个虚函数,讲解:红色方框内是被C类重写的函数,绿色方框是B类自己两个虚函数(因为不重名所以没被重写),而最后一个蓝色方框是C类自己的boo虚函数。
5:其余两个类 A和D的虚表中只有三个函数(A和D函数同名只是打印内容不同)没有蓝色方块的函数,这里就不贴图了,我相信你们能理解。
6:以上5条都是以继承的形式进行讨论的。别的形式就不准确了。
C类:
1.第一个是自己,接下来依次声明排列。与继承内存布局不同。
此图是32位C++类的结构:
不过在64位下放的就不是指针而是一个偏移。
我目前测试环境是vs2022下,别的编译器就不清楚了.
1.取出虚表地址 0x000000014001c9a8。
2.虚表地址减去void*大小(64位下8字节)也就是箭头指向地址。
3.取出箭头地址转到,再加上12字节也就是方框中的数据(0x0001f3c8)。
4.0x140000000(模块基址)+0x0001f3c8。
5.红色方框就是std::type_infor里面的数据
6.执行代码验证,学过汇编的同学都知道RAX一般情况下是用来存放函数返回值,本文中是(0x00000014001F3C8)。
7.正向代码验证。
1.上图是有虚表的情况,调用了__RTtypeid函数其功能就是 本文中第四条所讲内容。
2.没有虚表的情况,他将会直接std::type_infor对象地址指针赋值给目标。
1.x64下std::type_infor大小固定为24字节。
2.+0 偏移:存放着 type_infor 的虚函数表,里面有一个虚函数 为type_info::`scalar deleting destructor'(unsigned int)。
3.+8 偏移:_UndecoratedName = 0x0000000000000000 <NULL>,如图:
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)