C++逆向总结

本篇文章适用于有一定C语言基础,但是对C++零基础的同学看,讲解了C++里的一些概念

C在定义一个结构体的时候,就是定义一个新的数据类型
而C++在定义一个结构体,会有一个this指针,指向本结构体的地址,传的this指针的值一般给到了ecx。主要应用如下

typedef struct
{
    int x = 1;
    int y = 2;
    void function(int x, int y)
    {
        this->x = x;
        this->y = y;
    }
}str;
int main()
{
    str p;
    p.function(3,4);
}

这样可以修改结构体里的变量值
在C++中,如果有一些struct的东西是重复的,那我们就懒得再写,直接copy过来,c++提供一个很方便的东西继承

struct person
{
    int age;
    int sex;
};

struct A
{
    int age;
    int sex;
    char name;
}

struct A:person
{
    char name;
}

两种方式都差不多,person就是A的爹,称为父类,父类指针可以用来访问子类里父类的东西。
再说一点,如果子类和父类有定义相同的变量,那么需要在子类变量前加入一个类名加两个冒号才行
构造函数就是声明类后可以直接启动的函数,可以重载,可以有参数,无返回值

class test
{

    public:
    test(int x,int y)
    {
        printf("%d\n%d\n",x,y);
    }
};

int main()
{
    test base(2,3);
}
~   

析构函数,这个类不用后执行的一个函数,可以用来free,不能重载,不能有参数,无返回值
有父类子类,先父构造、子构造、子构析,父构析
虚函数表
如果在一个函数前加virtual,就是虚函数虚函数调用:

mov ecx,dword ptr [ebp-10]
mov edx,dword ptr [ecx];虚表地址给到edx
mov esi,esp
mov ecx,dword ptr [ebp-10]
call dword ptr [edx];取虚表地址里的数据,就是虚表

底层上从this指针处会多存一个4字节(32位),这个4字节就是存储着虚表的地址
这个虚表是一个数组,里面有函数地址
有一个父类的话,是直接把父类的虚表拿过来,子类接着写,如果子类和父类虚函数名重复,子类中copy来的父类虚表上的函数会被改写,这就是动态绑定。在这个基础上子类每多继承一个父类,就会多一个虚表,也就是多4字节(32位)而子类的虚函数放在第一张虚表了,其他虚表放其他父类的虚函数(不推荐使用)
所以取虚表:

class base
........
base b;
printf("虚表的地址是%d",*(int*)&b);

多态

#include<stdio.h>
class base
{
        public:
                int x;
                int y;
        public:
                base()
                {
                        x = 1;
                        y = 2;
                }
                virtual void print()
                {
                        printf("base:%x %x\n",x,y);
                }
};
class sub1:public base
{
public:
        int A;
        sub1()
        {
                x = 4;
                y = 5;
                A = 6;
        }
        virtual void print()
        {
                printf("sub1:%x %x %x\n",x,y,A);
        }

};
class sub2:public base
{
public:
        int b;
        sub2()
        {
                x = 7;
                y = 8;
                b = 9;
        }
        virtual void print()
        {
                printf("sub2:%x %x %x",x,y,b);
        }

};

void test()
{
        base b;
        sub1 s1;
        sub2 s2;

        base* arr[] = {&b,&s1,&s2};
        for(int i = 0;i<3;i++)
        {
                arr[i]->print();
        }
}
int main()
{
        test();
}

上面的运行结果

base:1 2
base:4 5
base:7 8//前面没有加virtual

base:1 2//后面加了virtual
sub1:4 5 6
sub2:7 8 9

这样也指明了:析构函数最好定义成虚函数.

我们之前写了一个函数是这样传参的

func(int* a)
{
    *a = 10;
    //a = (int*)203020;
}

我们知道一般而言函数不能更改参数的值,假如传参进去是ebp+8,然后ebp+8给eax,这样我们在函数里修改的值是eax的值,不是参数的值,但我们观察一下上面的反汇编

*a = 10;
mov eax,dword ptr [ebp+8]
mov dword ptr [eax],0ah

可以看到,假如传进去的是个地址,直接把地址里的数据给改了,也就是能修改参数
所以C++里面有个引用符号

void Test(int &x)
{
    x = 1;
    //x = 2;
}
int main()
{
    Test(a);
}

在底层传参是一样的

lea eax,[ebp-4]
push eax
call@ILT+0(Test)

有什么不一样的地方,就是有一种情况指针的值被修改,比如上面的第一行注释的代码,就是指针乱指
如果用引用的话,不能够进行乱指,比如x = 2,只是普通地把2给到这个变量,编译器不允许引用指向别的地方,引用中那个x永远代表a
同样的数据类型给指针就是指向另一个,不管原来的,而引用就是把指向的那个东西的值修改一下
还有就是传结构体的话参数太多,直接传引用好一点

我们都知道class里面的成员,数据成员都是私有的

class Person
{
public:
    Person(int x,int y)
    {
        this->x = x;
        this->y = y;
    }
//friend void print(const Person& refPer);
};
void Print(Person &p)
{
    printf("%d %d\n",p.x,p.y);
}

如果这样的话是不能打出来的,因为私有的,只有类型的其他成员能访问,比如自己的方法啥的,但是我们就要访问咋整(通过指针访问内存可以)
那我们可以把注释中的代码写出来,告诉这个类,这个print是它的朋友,通过这个函数访问类就可以访问私有成员了

在C++中,通常说我们要new一个对象,在对象不用的时候delete他。其实new是把malloc再封装一遍的,比malloc多了一层。比如int* pi = new int;就分配了int大小的空间,所以申请对象person* pa = new person(1,2);并执行构造函数,也就是说,new一个对象的目的其实就是把他放到堆里,不然的话我们只能放到全局变量(data )或者局部变量(stack)里了

要注意delete和new一定配套,如果new int[10]; 一定要delete[];

还有标准输入流与输出流可以看看,这里不再赘述,C++写很多东西要方便很多。可以尝试看看AFL源码和浏览器之类的

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值