当前位置:网站首页>类的底层机制
类的底层机制
2022-08-05 04:58:00 【小鸡岛~】
提出问题:
- 为什么类函数调用约定必须是this call
- 为什么静态成员函数没有this指针
- 为什么类的静态成员函数不能访问类的非静态成员函数
- 类真的有构造函数吗
C++代码
class T {
int hp;
public:
int Add(int a, int b) {
return hp + a + b;
}
};
int main(int count, char** args)
{
T t1;
t1.Add(100, 200);
}
汇编代码
00241034 push 0C8h
00241039 push 64h
0024103B lea ecx,[ebp-4]
0024103E call 00241000
00241000 55 push ebp
00241001 8B EC mov ebp,esp
00241003 51 push ecx
00241004 89 4D FC mov dword ptr [ebp-4],ecx //ecx值变成了局部变量
00241007 8B 45 FC mov eax,dword ptr [ebp-4]
0024100A 8B 00 mov eax,dword ptr [eax] //this->hp
0024100C 03 45 08 add eax,dword ptr [ebp+8]
0024100F 03 45 0C add eax,dword ptr [ebp+0Ch]
00241012 8B E5 mov esp,ebp
00241014 5D pop ebp
00241015 C2 08 00 ret 8
设计实验:
当函数有使用this->hp时,ecx都会被用来存储类的首地址
当函数没有有使用this->hp时,ecx也会被用来存储类的首地址
发现规律:当调用类的成员函数的时候,ecx会存储类的首地址,然后会被当作局部变量来使用
得出结论
_thiscall 是C++中 类的成员函数访问时 定义的函数调用约定
(1) 寄存器ecx用来存放类的指针
(2) 参数由右到左入栈
(3) 堆栈由 被调用者 负责恢复
类的非静态成员函数都可以使用this指针,this指针本质上来讲就是把对象中的指针通过寄存器ecx来传入成员函数的,因此类中的成员函数访问其成员变量,都是通过指针+偏移的形式来访问的,不管是否明确使用this指针
设计实验
C++代码
class T {
inline static int count;
public:
static int GetCount(int a, int b) {
count++;
return 2;
}
};
int main(int count, char** args)
{
T t1;
t1.GetCount(2, 3);
汇编代码
00D31023 6A 03 push 3
00D31025 6A 02 push 2
00D31027 E8 D4 FF FF FF call T::GetCount (0D31000h)
00D3102C 83 C4 08 add esp,8**
count++;
00A21003 A1 28 31 A2 00 mov eax,dword ptr ds:[00A23128h]
00A21008 83 C0 01 add eax,1
00A2100B A3 28 31 A2 00 mov dword ptr ds:[00A23128h],eax
得出结论:
类的静态成员函数,本质上是采用的_cdecl约定
(1)参数由右到左入栈
(2) 由[调用者]恢复堆栈平衡
答:因为类的静态成员函数本质上就是一个普通函数,所以根本没有传递对象的指针,因此也就不能访问其成员变量;
而类的静态成员变量本质上相当于一个全局变量,有固定的内存地址,与类对象并无关系,所以类的静态成员可以在类没有实例的情况下通过[类::静态成员]这样的形式访问
设计实验:
int hp{
1};
发现规律:
- 当int hp{1}时,反汇编代码调用了构造函数,调用目的是让给hp赋值
- 当int hp 时,反汇编代码没有调用构造函数
得出结论:
类T大部分的时候是没有构造函数的,这是因为C++标准委员会要求每一个类都有默认的构造函数。
但是一个空的构造函数实际上没有任何的意义,所以某些情况下编译器会删除掉没有意义的构造函数,这是编译器优化的结果。
原则上来讲,每一个类都有默认构造函数。
提出问题:
- 为什么AIM的大小为8个字节?
- p->Die()是如何保证调用的是WOLF里面的Die()
class AIM {
public :
int HP;
virtual void Eat() {
std::cout << "AIM" << std::endl;
}
virtual void Die() {
std::cout << "AIM-DIE" << std::endl;
}
};
class WOLF :public AIM{
public:
virtual void Eat() {
std::cout << "WOLF" << std::endl;
}
virtual void Die() {
std::cout << "WOLF-DIE" << std::endl;
}
void Sound() {
std::cout << "aoaoaoaoaoaoao!!!!" << std::endl;
}
};
int main(int count, char** args)
{
AIM* p = new WOLF();
p->Die();
std::cout << sizeof(AIM) << std::endl;
}
猜测+实证:AIM类里应该存储了一个四字节的地址,通过它可以去访问虚函数的地址。通过下面的打印语句可知这个四字节放在了AIM类的首地址
std::cout << p << " " << &p << std::endl;
问题2猜测:调用p->Die()函数时会传递存储了WOLF类的虚函数地址的指针进去,然后计算出对应的虚函数地址
实证:逆向分析
[ebp-4]里面的地址→eax
eax里面的内存地址的前四个字节→edx
[edx+4]这个内存地址的前四个字节→ eax
p->Die();
00D11A5D 8B 45 FC mov eax,dword ptr [p]
00D11A60 8B 10 mov edx,dword ptr [eax]
00D11A62 8B 4D FC mov ecx,dword ptr [p]
00D11A65 8B 42 04 mov eax,dword ptr [edx+4]
00D11A68 FF D0 call eax
发现规律:
- 虚函数的地址会被放在一张虚表里
- 这个虚表的地址会被放在类的首地址,当我们通过指针调用虚函数的时候会通过虚表计算出该地址,然后执行

直接构建
std::cout << std::hex << "vtable地址:" << pTable[0] << std::endl;
int* func = (int*)pTable[0];
std::cout << std::hex << "Eat地址:" << func[0] << std::endl;
std::cout << std::hex << "Die地址:" << func[1] << std::endl;
unsigned* eat = (unsigned*)func[1];
__asm {
call eat
}
得出结论
(1)同一个类的多个实例都指向同一个虚函数表
(2)只有通过指针访问虚函数才会调用虚函数表
边栏推荐
- 请写出SparkSQL语句
- How to solve complex distribution and ledger problems?
- Cron(Crontab)--使用/教程/实例
- upload upload pictures to Tencent cloud, how to upload pictures
- Detailed explanation of Mysql's undo log
- 数字孪生技术在电力系统中的应用现状
- 程序开发的一些常规套路(一)
- Qt制作18帧丘比特表白意中人、是你的丘比特嘛!!!
- dedecms报错The each() function is deprecated
- bytebuffer use demo
猜你喜欢

Qt制作18帧丘比特表白意中人、是你的丘比特嘛!!!

flink reads mongodb data source

creo怎么测量点到面的距离

浅析主流跨端技术方案

Application status of digital twin technology in power system

雷克萨斯lm的安全性到底体现在哪里?一起来看看吧

About the installation of sklearn library

In the WebView page of the UI automation test App, the processing method when the search bar has no search button
![[MRCTF2020] PYWebsite](/img/d4/57e8e5ee45b742894679f3f5671516.png)
[MRCTF2020] PYWebsite

特征预处理
随机推荐
小程序_动态设置tabBar主题皮肤
[8.1] Code Source - [The Second Largest Number Sum] [Stone Game III] [Balanced Binary Tree]
Paparazzi: Surface Editing by way of Multi-View Image Processing
[cesium] 3D Tileset model is loaded and associated with the model tree
【无标题】
[informix] Resolving startup errors and solutions
密码学系列之:PEM和PKCS7,PKCS8,PKCS12
ESP32 485光照度
software management rpm
机器学习概述
AUTOCAD——标注关联
write the story about us
Excel Paint
1007 Climb Stairs (greedy | C thinking)
4T硬盘剩余很多提示“No space left on device“磁盘空间不足
C+ +核心编程
u-boot中的u-boot,dm-pre-reloc
Flutter 父子组件如何都能收到点击事件
Develop your own node package
数字_获取指定位数的小数