首页
社区
课程
招聘
[原创]底层分析C++虚函数、this、多态
发表于: 2023-9-3 22:16 10642

[原创]底层分析C++虚函数、this、多态

2023-9-3 22:16
10642

1.引言

本文介绍以下4个部分:
(1)虚函数
(2)虚函数表
(3)直接调用private修饰的虚函数、传递任意this指针给调用的函
(4)多态、this、虚函数表

2.虚函数

定义:简单地说,那些被virtual关键字修饰的成员函数,就是虚函数。虚函数的作用,用专业术语来解释就是实现多态性(Polymorphism),多态性是将接口与实现进行分离;用形象的语言来解释就是实现以共同的方法,但因个体差异,而采用不同的策略。

3.虚函数表

虚函数表首地址存在于class对象的起始位置。虚函数可以被继承,子类重写的虚函数会覆盖虚函数表中父类的虚函数的地址。
通过this来查对应的虚函数表来实现多态。

4.接调用private修饰的虚函数

4.1无this指针

通过函数指针指向虚函数表中需要调用的虚函数地址,作为普通函数进行调用。

4.2修复this指针

(1)在调用成员函数的时候,编译器隐式地传递了this指针这个参数,this不是通过栈进行的传递,而是通过ecx寄存器传值。

(2)修复this指针,在函数中读取class A的成员变量a

(3)传递任意class的this给调用的虚函数

调用A的AF函数,传递B的this,实现AF函数中读取出B的成员变量。

需要注意的是,在传递B的this指针时,要算好this+offset

5.多态

静态多态:子类重写父类的相同函数

动态多态:父类指针指向子类对象,调用子类虚函数方法

6.总结

(1)多态实现的灵魂就是this指针,this指针指向了虚函数表的首地址。this指针决定了调用虚函数时查哪个类的虚函数表。

(2)调用虚函数是间接call,调用普通函数是直接call。

(3)虚函数表首地址位于class的起始地址,这样是为了继承时查表方便。

(4)a = B(); a.AF();不是多态,因为,this传递的是a的,并且是直接call

#include "stdio.h"
 
class A
{
private:
    virtual void AF() {
        printf("AF run...\n");
    }
};
 
int main()
{
    A a;
    typedef void(*Myfunction)();
    Myfunction f = (Myfunction)(*(int*)(*(int*)(&a)));
    f();
}
运行结果:AF run...
#include "stdio.h"
 
class A
{
private:
    virtual void AF() {
        printf("AF run...\n");
    }
};
 
int main()
{
    A a;
    typedef void(*Myfunction)();
    Myfunction f = (Myfunction)(*(int*)(*(int*)(&a)));
    f();
}
运行结果:AF run...
#include "stdio.h"
class A
{
public:
    int a = 1;
private:
    virtual void AF() {
        printf("AF run...\n");
        printf("a=%d\n", this->a);
    }
public:
    virtual void test(int a) {
        printf("test run...%x\n", a);
    }
};
 
int main()
{
    A a;
    a.test(0x12345678);
}
运行结果:test run...12345678
 
——————————————————————————————汇编代码————————————————————————————————
    A a;
00B51B4F 8D 4D F0             lea         ecx,[a]           // 将a的地址(this指针)赋值给ecx
00B51B52 E8 AA F8 FF FF       call        A::A (0B51401h)    // 调用A的构成函数A
    a.test(0x12345678);
00B51B57 68 78 56 34 12       push        12345678h         // 将参数a=0x12345678压入栈
00B51B5C 8D 4D F0             lea         ecx,[a]           // 将this复制给ecx
00B51B5F E8 01 F9 FF FF       call        A::test (0B51465h) // 调用函数test
#include "stdio.h"
class A
{
public:
    int a = 1;
private:
    virtual void AF() {
        printf("AF run...\n");
        printf("a=%d\n", this->a);
    }
public:
    virtual void test(int a) {
        printf("test run...%x\n", a);
    }
};
 
int main()
{
    A a;
    a.test(0x12345678);
}
运行结果:test run...12345678
 
——————————————————————————————汇编代码————————————————————————————————
    A a;
00B51B4F 8D 4D F0             lea         ecx,[a]           // 将a的地址(this指针)赋值给ecx
00B51B52 E8 AA F8 FF FF       call        A::A (0B51401h)    // 调用A的构成函数A
    a.test(0x12345678);
00B51B57 68 78 56 34 12       push        12345678h         // 将参数a=0x12345678压入栈
00B51B5C 8D 4D F0             lea         ecx,[a]           // 将this复制给ecx
00B51B5F E8 01 F9 FF FF       call        A::test (0B51465h) // 调用函数test
#include "stdio.h"
class A
{
public:
    int a = 1;
private:
    virtual void AF() {
        printf("AF run...\n");
        printf("a=%d\n", this->a);
    }
};
 
int main()
{
    A a;
    A* pA = &a;
    typedef void(*Myfunction)();
    Myfunction f = (Myfunction)(*(int*)(*(int*)(&a)));
    __asm
    {
        mov ecx, pA;    // a的地址(this指针)赋值给eax
    }
    f();
}
运行结果:
AF run...
a=1
#include "stdio.h"
class A
{
public:
    int a = 1;
private:
    virtual void AF() {
        printf("AF run...\n");
        printf("a=%d\n", this->a);
    }
};
 
int main()
{
    A a;
    A* pA = &a;
    typedef void(*Myfunction)();
    Myfunction f = (Myfunction)(*(int*)(*(int*)(&a)));
    __asm
    {
        mov ecx, pA;    // a的地址(this指针)赋值给eax
    }
    f();
}
运行结果:
AF run...
a=1
#include "stdio.h"
class A
{
public:
    int a = 1;
private:
    virtual void AF() {
        printf("AF run...\n");
        printf("a=%d\n", this->a);
    }
};
 
class B: public A
{
public:
    int a = 2;
private:
    virtual void AF() {
        printf("BF run...\n");
        printf("a=%d\n", this->a);
    }
};
 
int main()
{
    A a;
    B b;
    typedef void(*Myfunction)();
    Myfunction f = (Myfunction)(*(int*)(*(int*)(&a)));
    B* pA = &b;
    __asm
    {
        mov ecx, pA;    // 调用A的AF函数,传递B的this
        add ecx, 4;     // 这里需要this+4,如果不+4,则a=1
    }
    f();
}
运行结果:
AF run...
a=2
#include "stdio.h"
class A
{
public:
    int a = 1;
private:
    virtual void AF() {
        printf("AF run...\n");
        printf("a=%d\n", this->a);
    }
};
 
class B: public A
{
public:
    int a = 2;
private:
    virtual void AF() {
        printf("BF run...\n");
        printf("a=%d\n", this->a);
    }

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2023-9-3 22:24 被ATrueMan编辑 ,原因: 格式
收藏
免费 6
支持
分享
最新回复 (3)
雪    币: 3090
活跃值: (30881)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
感谢分享
2023-9-3 23:07
1
雪    币: 1069
活跃值: (1010)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
好文章,谢谢分享
2024-2-19 11:34
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
4
好文章
2024-3-13 10:43
0
游客
登录 | 注册 方可回帖
返回
//