C++逆向总结

Posted Y0n1an

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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源码和浏览器之类的

以上是关于C++逆向总结的主要内容,如果未能解决你的问题,请参考以下文章

C++逆向总结

逆向工程普及篇

MFC逆向-消息响应函数的定位

我想学C++和逆向工程

逆向程序分析:C++的一个空类,为什么在内存中占1字节

c++和java语言的逆向工程工具 ?