吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 3226|回复: 23
收起左侧

[C&C++ 原创] c++反汇编(x86)

[复制链接]
vstk 发表于 2023-2-12 17:01
本帖最后由 vstk 于 2023-2-12 17:04 编辑

1.类型大小

1. 基本数据类型

printf("%d\n", sizeof(char));       //1
printf("%d\n", sizeof(short int));  //2
printf("%d\n", sizeof(int));        //4
printf("%d\n", sizeof(long int));   //4
printf("%d\n", sizeof(__int64));    //8
printf("%d\n", sizeof(float));      //4
printf("%d\n", sizeof(double));     //8

2. 数组类型

char arr1[10] = { 0 };
short arr2[10] = { 0 };
int arr3[10] = { 0 };

printf("%d\n", sizeof(arr1));   //10
printf("%d\n", sizeof(arr2));   //20
printf("%d\n", sizeof(arr3));   //40

printf("%d\n", sizeof(arr1[10]));  //sizeof(char)   1
printf("%d\n", sizeof(arr2[10]));  //sizeof(short)  2
printf("%d\n", sizeof(arr3[10]));  //sizeof(int)    4

3. 数据对齐

  1. 为什么要有数据对齐?

本质:效率还是空间,二选一的结果;

#pragma pack的基本用法为:

#pragma pack( n )
结构体...
#pragma pack(  )

对齐参数:n为字节对齐数,其取值为1、2、4、8,默认是8。如果这个值比结构体成员的sizeof值小,那么该成员的偏移量应该以此值为准,
即是说,结构体成员的偏移量应该取二者的最小值.

  1. 结构对齐大小

如果这个值比结构体成员的sizeof值小,那么该成员的偏移量应该以此值为准,即是说,结构体成员的偏移量应该取二者的最小值;

对齐原则:

  • 原则一:数据成员对齐规则:结构的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储).

  • 原则二:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。

  • 原则三:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。

    (struct a里存有struct b,b里有char,int,double等元素,那b应该从8的整数倍开始存储.)

原则四:对齐参数如果比结构体成员的sizeof值小,该成员的偏移量应该以此值为准.也就是说,结构体成员的偏移量应该取二者的最小值

  1. 建议:

按照数据类型由小到大的顺序进行书写  

  1. 实例
//16
struct S1
{
    char c;   //8
    double i; //8
};

//40
struct S2
{
    char c1;  //8
    S1 s;     //16 => char c; double i;
    char c2;  //8
    double c3;  //8
};

//32
struct S3
{
    char c1;  //8
    S1 s;     //16 => char c; double i;
    char c2;  // => sizeof(c2) + sizeof(c3) = 2 + 6 = 8
    char c3;
};

//12
struct S4
{
    int c1;     //sizeof(int) + sizeof(char) * 5 =》 4 + 4 + 1 + 3(填充) = 12
    char c2[5];
};

//16
struct S5
{
    int c1;     //sizeof(int) + sizeof(char) * 10 =》 4 + (4 * 2 + 2 + 2(填充)) = 16
    char c2[10];
};

int main() {
    int a = sizeof(S1);
    int b = sizeof(S2);
    int c = sizeof(S3);
    int d = sizeof(S4);
    int e = sizeof(S5);

    return 0;
}

2. 面向对象

2.1 对象

  1. 绕过编译器,修改对象的私有成员:
#include <iostream>

class Test {
public:
    Test(int x, int y) {
        this->x = x;
        this->y = y;
    }

    void pri() {
        std::cout << "x : " << x << "\ny : " << y << std::endl;;
    }

private:
    int x;
    int y;
};

int main() {

    Test t(1, 2);

    int* p = (int*)&t;

    int n = *p;
    int m = *(p + 1);

    *p = 3;
    *(p + 1) = 4;

    std::cout << "n : " << n << "\nm : " << m << std::endl;;

    t.pri();

    system("pause");

    return 0;
}
  1. 绕过编译器,调用对象的私有成员函数:
#include <iostream>

struct Base
{
private:
    virtual void Function_1()
    {
        printf("Base:Function_1...\n");
    }
    virtual void Function_2()
    {
        printf("Base:Function_2...\n");
    }
    virtual void Function_3()
    {
        printf("Base:Function_3...\n");
    }
};

int main() {

    Base b;
    Base* ptr = &b;

    void(*f1)(void) = (void(*)(void))(*((int*)(*(int*)&b + 0)));
    void(*f2)(void) = (void(*)(void))(*((int*)(*(int*)&b + 4)));
    void(*f3)(void) = (void(*)(void))(*((int*)(*(int*)&b + 8)));

    /*
    0. 函数指针:(void(*)(void))
    1. 指向虚表的地址:(*(int *)&b)
    2. 虚函数的地址(或第一个虚函数的地址):(*((int*)(*(int *)&b + 0)))
    */

    f1();
    f2();
    f3();

    system("pause");

    return 0;
}

总结:

  • private修饰的成员与普通的成员没有区别 只是编译器会检测。
  • private修饰的成员只有自己的其他成员才能访问;
  1. struct 与 class
  • 编译器默认class中的成员为private,而struct中的成员为public
  • class 默认private继承,struct 默认public继承
  1. 指针与引用
#include <iostream>

int main() {
    int a = 12;

    int* ptr = &a;
    int& ref = a;

    system("pause");

    return 0;
}
    int a = 12;
008F1728  mov         dword ptr [ebp-0Ch],0Ch  

    int* ptr = &a;
008F172F  lea         eax,[ebp-0Ch]  
008F1732  mov         dword ptr [ebp-18h],eax  
    int& ref = a;
008F1735  lea         eax,[ebp-0Ch]  
008F1738  mov         dword ptr [ebp-24h],eax  

总结:指针与引用在汇编层次,没有任何区别;

2.2 虚表

继承

  • 父类中的私有成员是会被继承的
  • 只是编译器不允许直接进行访问
  1. 当类中有虚函数时,会多一个属性,4个字节(单一继承)
  2. 多出的属性是一个地址,指向一张表,里面存储了所有虚函数的地址

在x86中,指针类型大小都为4个字节

2.2.1 非继承虚函数表

//普通继承,非覆盖
struct Base
{
public:
    virtual void Function_1()
    {
        printf("Base:Function_1...\n");
    }
    virtual void Function_2()
    {
        printf("Base:Function_2...\n");
    }
    virtual void Function_3()
    {
        printf("Base:Function_3...\n");
    }
};

int main() {
    Base base;
    Base* ptr = &base;

    //断点到此处
    ptr->Function_1();
    ptr->Function_2();
    ptr->Function_3();

    system("pause");

    return 0;
}

汇编如下:

    Base base;
009D51D8  lea         ecx,[ebp-0Ch]  
009D51DB  call        009D103C  
    Base* ptr = &base;
009D51E0  lea         eax,[ebp-0Ch] ;获得base的地址 
009D51E3  mov         dword ptr [ebp-18h],eax 

    //断点到此处
    ptr->Function_1();
009D51E6  mov         eax,dword ptr [ebp-18h]  ;&base
009D51E9  mov         edx,dword ptr [eax]  ;base的虚表地址
009D51EB  mov         esi,esp  
009D51ED  mov         ecx,dword ptr [ebp-18h]  
009D51F0  mov         eax,dword ptr [edx]  ;第一个函数的地址
009D51F2  call        eax  
009D51F4  cmp         esi,esp  
009D51F6  call        009D1140  
    ptr->Function_2();
009D51FB  mov         eax,dword ptr [ebp-18h]  
009D51FE  mov         edx,dword ptr [eax]  
009D5200  mov         esi,esp  
009D5202  mov         ecx,dword ptr [ebp-18h]  
009D5205  mov         eax,dword ptr [edx+4]  ;第二个函数的地址,偏移4个字节
009D5208  call        eax  
009D520A  cmp         esi,esp  
009D520C  call        009D1140  
    ptr->Function_3();
009D5211  mov         eax,dword ptr [ebp-18h]  
009D5214  mov         edx,dword ptr [eax]  
009D5216  mov         esi,esp  
009D5218  mov         ecx,dword ptr [ebp-18h]  
009D521B  mov         eax,dword ptr [edx+8]  ;第三个函数的地址,偏移8个字节
009D521E  call        eax  
009D5220  cmp         esi,esp  
009D5222  call        009D1140  

验证:

void TestMethod()
{
    //查看 Sub 的虚函数表          
    Base base;
    Base* ptr = &base;  //包含一个虚表指针

    printf("base 的地址为:%x\n", &base);
    //对象的前四个字节就是虚函数表         
    printf("base 的虚函数表地址为:%x\n", *(int*)&base);
    for (int i = 0; i < 3; i++) {
        printf("虚函数%d的地址:%x\n", i + 1, *((int*)(*(int*)&base) + i));
    }

    //通过函数指针调用函数,验证正确性        
    typedef void(*pFunction)(void);
    pFunction pFn;

    for (int i = 0; i < 3; i++)
    {
        int temp = *((int*)(*(int*)&base) + i);
        pFn = (pFunction)temp;
        pFn();
    }
}

2.2.2 继承非覆盖虚函数表

父类的虚函数表和子类不相同,每个类都独有自己的一份虚函数表;

通过调试不难发现,Sub子类的的虚表就是:Function_1、Function_2、Function_3、Function_4、Function_5、Function_6 这个6个函数的地址;Base基类的虚表就是:Function_1、Function_2、Function_3 这个3个函数地址;

#include <iostream>

//普通继承,非覆盖
struct Base
{
public:
    virtual void Function_1()
    {
        printf("Base:Function_1...\n");
    }
    virtual void Function_2()
    {
        printf("Base:Function_2...\n");
    }
    virtual void Function_3()
    {
        printf("Base:Function_3...\n");
    }
};
struct Sub :Base
{
public:
    virtual void Function_4()
    {
        printf("Sub:Function_4...\n");
    }
    virtual void Function_5()
    {
        printf("Sub:Function_5...\n");
    }
    virtual void Function_6()
    {
        printf("Sub:Function_6...\n");
    }
};

void TestMethod() {
    //查看 Sub, Base 的虚函数表        
    Sub sub;
    Base base;

    Base* ptr1 = ⊂  //包含一个虚表指针
    Base* ptr2 = &base;

    printf("base 的地址为:%x\n", &base);
    //对象的前四个字节就是虚函数表         
    printf("base 的虚函数表地址为:%x\n", *(int*)&base);
    for (int i = 0; i < 3; i++) {
        printf("Base虚函数%d的地址:%x\n", i + 1, *((int*)(*(int*)&base) + i));
    }
    //通过函数指针调用函数,验证正确性        
    typedef void(*pFunction)(void);
    pFunction p1Fn;

    for (int i = 0; i < 3; i++)
    {
        int temp = *((int*)(*(int*)&base) + i);
        p1Fn = (pFunction)temp;
        p1Fn();
    }

    std::cout << std::endl;

    printf("Sub 的地址为:%x\n", &sub);
    //对象的前四个字节就是虚函数表         
    printf("Sub 的虚函数表地址为:%x\n", *(int*)&sub);
    for (int i = 0; i < 6; i++) {
        printf("Sub虚函数%d的地址:%x\n", i + 1, *((int*)(*(int*)&sub) + i));
    }
    //通过函数指针调用函数,验证正确性        
    pFunction p2Fn;
    for (int i = 0; i < 6; i++)
    {
        int temp = *((int*)(*(int*)&sub) + i);
        p2Fn = (pFunction)temp;
        p2Fn();
    }
}

int main() {

    TestMethod();

    system("pause");

    return 0;
}

2.2.3 继承覆盖虚函数表

子类优先按照基类声明的顺序添加虚函数;

#include <iostream>

//普通继承,覆盖
struct Base
{
public:
    virtual void Function_1()
    {
        printf("Base:Function_1...\n");
    }
    virtual void Function_2()
    {
        printf("Base:Function_2...\n");
    }
    virtual void Function_3()
    {
        printf("Base:Function_3...\n");
    }
};
struct Sub :Base
{
public:
    virtual void Function_1()
    {
        printf("Sub:Function_1...\n");
    }
    virtual void Function_5()
    {
        printf("Sub:Function_5...\n");
    }
    virtual void Function_3()
    {
        printf("Sub:Function_3...\n");
    }
};

//查看 Sub, Base 的虚函数表
void TestMethod()
{
    //查看 Sub, Base 的虚函数表        
    Sub sub;
    Base base;

    Base* ptr1 = ⊂  //包含一个虚表指针
    Base* ptr2 = &base;

    printf("base 的地址为:%x\n", &base);
    //对象的前四个字节就是虚函数表         
    printf("base 的虚函数表地址为:%x\n", *(int*)&base);
    for (int i = 0; i < 3; i++) {
        printf("Base虚函数%d的地址:%x\n", i + 1, *((int*)(*(int*)&base) + i));
    }
    //通过函数指针调用函数,验证正确性        
    typedef void(*pFunction)(void);
    pFunction p1Fn;

    for (int i = 0; i < 3; i++)
    {
        int temp = *((int*)(*(int*)&base) + i);
        p1Fn = (pFunction)temp;
        p1Fn();
    }

    std::cout << std::endl;

    printf("Sub 的地址为:%x\n", &sub);
    //对象的前四个字节就是虚函数表         
    printf("Sub 的虚函数表地址为:%x\n", *(int*)&sub);
    for (int i = 0; i < 4; i++) {
        printf("Sub虚函数%d的地址:%x\n", i + 1, *((int*)(*(int*)&sub) + i));
    }

    //通过函数指针调用函数,验证正确性        
    pFunction p2Fn;
    for (int i = 0; i < 4; i++)
    {
        int temp = *((int*)(*(int*)&sub) + i);
        p2Fn = (pFunction)temp;
        p2Fn();
    }
}

int main() {

    TestMethod();

    system("pause");

    return 0;
}

2.2.4 多继承非覆盖虚函数表

#include <iostream>

struct Base1
{
public:
    virtual void Fn_1()
    {
        printf("Base1:Fn_1...\n");
    }
    virtual void Fn_2()
    {
        printf("Base1:Fn_2...\n");
    }
};
struct Base2
{
public:
    virtual void Fn_3()
    {
        printf("Base2:Fn_3...\n");
    }
    virtual void Fn_4()
    {
        printf("Base2:Fn_4...\n");
    }
};
struct Sub :Base1, Base2
{
public:
    virtual void Fn_5()
    {
        printf("Sub:Fn_5...\n");
    }
    virtual void Fn_6()
    {
        printf("Sub:Fn_6...\n");
    }
};

int main() {
    //查看 Sub 的虚函数表                 
    Sub sub;

    //通过函数指针调用函数,验证正确性               
    typedef void(*pFunction)(void);

    //对象的前四个字节是第一个虚表: 经过编译器优化:Base1 + Sub 的虚函数组成的表                 
    printf("Sub的第一个虚表的虚函数表地址为:%x\n", *(int*)&sub);
    pFunction pFn;
    for (int i = 0; i < 4; i++)
    {
        int temp = *((int*)(*(int*)&sub) + i);
        if (temp == 0)
        {
            break;
        }
        pFn = (pFunction)temp;
        pFn();
    }

    //对象的第二个四字节是第二个虚表, 根据编译器的优化 =》 Base2的虚表               
    printf("Sub的第二个虚表的虚函数表地址为:%x\n", *(int*)((int*)&sub + 4));
    pFunction pFn1;
    for (int k = 0; k < 2; k++)
    {
        int temp = *((int*)(*(int*)((int)&sub + 4)) + k);
        pFn1 = (pFunction)temp;
        pFn1();
    }

    system("pause");

    return 0;
}

结论:Sub的虚表由两个虚表组成:

  1. Base1 + Sub 的虚函数组成的虚表;
  2. Base2组成的虚表

猜测:之后的继承的每个类有单独的虚表,如Base2一样;

2.2.5 多继承覆盖虚函数表

#include <iostream>

struct Base1
{
public:
    virtual void Fn_1()
    {
        printf("Base1:Fn_1...\n");
    }
    virtual void Fn_2()
    {
        printf("Base1:Fn_2...\n");
    }
};
struct Base2
{
public:
    virtual void Fn_3()
    {
        printf("Base2:Fn_3...\n");
    }
    virtual void Fn_4()
    {
        printf("Base2:Fn_4...\n");
    }
};

struct Base3
{
public:
    virtual void Fn_6()
    {
        printf("Base3:Fn_6...\n");
    }
    virtual void Fn_7()
    {
        printf("Base3:Fn_7...\n");
    }
};

struct Sub :Base1, Base2, Base3
{
public:
    virtual void Fn_1()
    {
        printf("Sub:Fn_1...\n");
    }
    virtual void Fn_3()
    {
        printf("Sub:Fn_3...\n");
    }
    virtual void Fn_5()
    {
        printf("Sub:Fn_5...\n");
    }

    virtual void Fn_6()
    {
        printf("Sub:Fn_6...\n");
    }

    virtual void Fn_8()
    {
        printf("Sub:Fn_8...\n");
    }
};

int main() {
    //查看 Sub 的虚函数表                 
    Sub sub;

    //通过函数指针调用函数,验证正确性               
    typedef void(*pFunction)(void);

    //对象的前四个字节是第一个虚表: 经过编译器优化:Base1 + Sub 的虚函数组成的表                
    printf("第一个虚表 的虚函数表地址为:%x\n", *(int*)&sub);
    pFunction pFn;
    for (int i = 0; i < 4; i++)
    {
        int temp = *((int*)(*(int*)&sub) + i);
        if (temp == 0)
        {
            break;
        }
        pFn = (pFunction)temp;
        pFn();
    }

    //对象的第二个四字节是第二个虚表, 根据编译器的优化 =》 Base2的虚表             
    printf("第二个虚表 的虚函数表地址为:%x\n", *(int*)((int)&sub + 4));
    pFunction pFn1;
    for (int k = 0; k < 2; k++)
    {
        int temp = *((int*)(*(int*)((int)&sub + 4)) + k);
        pFn1 = (pFunction)temp;
        pFn1();
    }

    //对象的第三个四字节是第三个虚表, 根据编译器的优化 =》 Base3的虚表             
    printf("第三个虚表 的虚函数表地址为:%x\n", *(int*)((int)&sub + 8));
    for (int k = 0; k < 2; k++)
    {
        int temp = *((int*)(*(int*)((int)&sub + 8)) + k);
        pFn1 = (pFunction)temp;
        pFn1();
    }

    system("pause");

    return 0;
}

结论:Sub的虚表由三个虚表组成,按照继承顺序组合:

  1. Base1 + Sub 的虚函数组成的虚表:Sub::Fn_1(), Base1::Fn_2(), Sub::Fn_5(), Sub::Fn_8();
  2. Base2 + Sub 的虚函数组成的虚表:Sub::Fn_3(), Base2::Fn_4();
  3. Base3 + Sub 的虚函数组成的虚表:Sub::Fn_6(), Base3::Fn_7();

2.2.6 单一连续继承,无函数覆盖

#include <iostream>

struct Base1
{
public:
    virtual void Fn_1()
    {
        printf("Base1:Fn_1...\n");
    }
    virtual void Fn_2()
    {
        printf("Base1:Fn_2...\n");
    }
};
struct Base2 :Base1
{
public:
    virtual void Fn_3()
    {
        printf("Base2:Fn_3...\n");
    }
    virtual void Fn_4()
    {
        printf("Base2:Fn_4...\n");
    }
};
struct Sub :Base2
{
public:
    virtual void Fn_5()
    {
        printf("Sub:Fn_5...\n");
    }
    virtual void Fn_6()
    {
        printf("Sub:Fn_6...\n");
    }
};
int main(int argc, char* argv[])
{
    //查看 Sub 的虚函数表            
    Sub sub;

    //观察大小:虚函数表只有一个           
    printf("%x\n", sizeof(sub));

    //通过函数指针调用函数,验证正确性          
    typedef void(*pFunction)(void);

    //对象的前四个字节是就是虚函数表            
    printf("Sub 的虚函数表地址为:%x\n", *(int*)&sub);

    pFunction pFn;

    for (int i = 0; i < 6; i++)
    {
        int temp = *((int*)(*(int*)&sub) + i);
        if (temp == 0)
        {
            break;
        }
        pFn = (pFunction)temp;
        pFn();
    }

    return 0;
}

结论:单一继承,虚表只有一个;

2.2.7 单一连续继承,有函数覆盖

#include <iostream>

struct Base1
{
public:
    virtual void Fn_1()
    {
        printf("Base1:Fn_1...\n");
    }
    virtual void Fn_2()
    {
        printf("Base1:Fn_2...\n");
    }
};
struct Base2 :Base1
{
public:
    virtual void Fn_1()
    {
        printf("Base2:Fn_1...\n");
    }
    virtual void Fn_3()
    {
        printf("Base2:Fn_3...\n");
    }
};
struct Sub :Base2
{
public:
    virtual void Fn_1()
    {
        printf("Sub:Fn_1...\n");
    }

    virtual void Fn_5()
    {
        printf("Sub:Fn_5...\n");
    }
};
int main(int argc, char* argv[])
{
    //查看 Sub 的虚函数表            
    Sub sub;

    //观察大小:虚函数表只有一个           
    printf("%x\n", sizeof(sub));

    //通过函数指针调用函数,验证正确性          
    typedef void(*pFunction)(void);

    //对象的前四个字节是就是虚函数表            
    printf("Sub 的虚函数表地址为:%x\n", *(int*)&sub);

    pFunction pFn;

    for (int i = 0; i < 6; i++)
    {
        int temp = *((int*)(*(int*)&sub) + i);
        if (temp == 0)
        {
            break;
        }
        pFn = (pFunction)temp;
        pFn();
    }

    return 0;
}

结论:单一继承,虚表只有一个;

2.2.8 例子

  1. 定义一个父类:Base 有两个成员X,Y 有一个函数Print(非virtul)
    能够打印X,Y的值。
#include <iostream>

class Base {
public:
    Base(int x, int y) : x(x), y(y) {
    };

private:
    int x, y;

};

void prt(void* ptr) {
    printf("X: %d Y: %d\n", *((int*)ptr), *((int*)ptr + 1));
}

int main() {

    Base base(1, 2);

    prt(&base);

    system("pause");

    return 0;
}
  1. 定义3个子类:Sub1有一个成员A, Sub2有一个成员B, Sub3  有一个成员C; 每个子类有一个函数Print(非virtul),打印所有成员。
  • Sub1:打印X Y A
  • Sub2:打印X Y B
  • Sub3:打印X Y C
#include <iostream>

class Base {
public:
    Base(int x, int y) : x(x), y(y) {
    };

private:
    int x, y;
};

class  Sub1 : public Base {
public:
    Sub1(int x, int y, int n) : Base(x, y), a(n) {
    }

    void prt(void* ptr) {
        printf("X: %d Y: %d #: %d\n", *((int*)ptr), *((int*)ptr + 1), *((int*)ptr + 2));
    }

private:
    int a;
};

class  Sub2 : public Base {
public:
    Sub2(int x, int y, int n) : Base(x, y), b(n) {
    }

    void prt(void* ptr) {
        printf("X: %d Y: %d #: %d\n", *((int*)ptr), *((int*)ptr + 1), *((int*)ptr + 2));
    }

private:
    int b;
};

class  Sub3 : public Base {
public:
    Sub3(int x, int y, int n) : Base(x, y), c(n) {
    }

    void prt(void* ptr) {
        printf("X: %d Y: %d #: %d\n", *((int*)ptr), *((int*)ptr + 1), *((int*)ptr + 2));
    }

private:
    int c;
};

int main() {

    Sub1 sub1(1, 2, 3);
    Sub1 sub2(4, 5, 6);
    Sub1 sub3(7, 8, 9);

    sub1.prt(&sub1);
    sub2.prt(&sub2);
    sub3.prt(&sub3);

    system("pause");

    return 0;
}

总结:子类的成员 = 基类的成员 + 子类的成员

  1. 将上面所有的Print函数改成virtul 继续观察效果.
#include <iostream>

class Base {
public:
    Base(int x, int y) : x(x), y(y) {
    };

private:
    int x, y;

};

class  Sub1 : public Base {
public:
    Sub1(int x, int y, int n) : Base(x, y), a(n) {
    }

    virtual void prt(void* ptr) {
        printf("X: %d Y: %d #: %d\n", *((int*)ptr + 1), *((int*)ptr + 2), *((int*)ptr + 3));
    }

private:
    int a;
};

class  Sub2 : public Base {
public:
    Sub2(int x, int y, int n) : Base(x, y), b(n) {
    }

    virtual void prt(void* ptr) {
        printf("X: %d Y: %d #: %d\n", *((int*)ptr + 1), *((int*)ptr + 2), *((int*)ptr + 3));
    }

private:
    int b;
};

class  Sub3 : public Base {
public:
    Sub3(int x, int y, int n) : Base(x, y), c(n) {
    }

    virtual void prt(void* ptr) {
        printf("X: %d Y: %d #: %d\n", *((int*)ptr + 1), *((int*)ptr + 2), *((int*)ptr + 3));
    }

private:
    int c;
};

int main() {

    Sub1 sub1(1, 2, 3);
    Sub1 sub2(4, 5, 6);
    Sub1 sub3(7, 8, 9);

    sub1.prt(&sub1);
    sub2.prt(&sub2);
    sub3.prt(&sub3);

    system("pause");

    return 0;
}

总结:子类成员在对象的前4字节(虚表地址)之后,子类的成员 = 基类的成员 + 子类的成员

免费评分

参与人数 9吾爱币 +18 热心值 +7 收起 理由
springwillow + 1 谢谢@Thanks!
likoaong + 1 + 1 我很赞同!
pvucci + 1 谢谢@Thanks!
debug_cat + 1 + 1 用心讨论,共获提升!
theStyx + 2 + 1 我很赞同
ajian168 + 1 + 1 我很赞同!
asdfghjk_0001 + 1 + 1 谢谢@Thanks!
ICEY + 3 + 1 谢谢@Thanks!
侃遍天下无二人 + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

周易 发表于 2023-2-12 19:32

C++中还存在虚基类。对于微软的编译器,可能有vbtable。可以使用/d1reportSingleClassLayoutClassName确定布局,其中ClassName为特定类名称。

免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
侃遍天下无二人 + 1 + 1 用心讨论,共获提升!

查看全部评分

thechrisroger 发表于 2023-2-27 17:37
周易 发表于 2023-2-12 19:32
[md]C++中还存在虚基类。对于微软的编译器,可能有`vbtable`。可以使用`/d1reportSingleClassLayoutClassNa ...

虚函数表(即vtable)在类中定义了虚函数即会生成,由vptr指向,在虚函数的多继承场景下,可能还会存在多个虚函数表,可使用vs 在反汇编界面逐指令调试查看具体vptr指向与虚函数载运行时动态绑定情况
侃遍天下无二人 发表于 2023-2-12 17:51
如果有可能的话,加几行带注释的反汇编就更好了
社会主義パンク 发表于 2023-2-13 01:07
楼主的帖子难度好大,不像入门的支持一下
yonghu99999 发表于 2023-2-13 06:38
讲得很好啊
头像被屏蔽
tlf 发表于 2023-2-13 09:20
提示: 作者被禁止或删除 内容自动屏蔽
losingstars 发表于 2023-2-13 10:09
收藏,谢谢楼主。
debug_cat 发表于 2023-2-13 16:29
收藏,谢谢楼主。
libaogui2022 发表于 2023-2-26 21:46
感谢分享
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-5-10 06:22

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表