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++逆向总结的主要内容,如果未能解决你的问题,请参考以下文章