最近整理印象笔记的笔记,找到以前在深信服做病毒逆向分析时的笔记,总结分享下,算是比较好的入门汇编的材料,强烈建议想掌握C和C++本质的同学,动手写些简单的例子代码,再Debug模式下(注意不要用Release模式,因为很多细节会被优化掉),对照源码看汇编代码,能让你对C和C++有更本质的认识,另外建议先看懂CSAPP中的汇编入门章节,再来看我的博客比较好。
局部变量:通过减小或增大栈指针来分配或回收变量内存。
宏和typedef:编译阶段替换成变量的实际数据或函数的汇编代码。
全局变量:在main函数前被初始化,被写入.data节,由绝对地址寻址。
静态变量:仅能被初始化一次,汇编中可看到防止重复初始化的标记位。若初始化时是常量则会被优化。
看汇编代码可以发现,数组和普通的变量都是栈上分配的内存,只是数组是连续的。若数组太大,会导致栈溢出。数组中的中括号会编译成(数组首地址+索引*偏移量)计算具体地址。所以数组和指针本质是相同的,都是操作地址。类似的多维数组和一唯数组也没区别。
可把结构体看作特殊数组,成员变量再内存中也是连续的,但偏移量不同,由偏移量确定结构体的成员。因此,结构体类型只是提升代码可读性,汇编代码和通过"首地址+偏移量"确定变量本质是一样的。
共用体中的成员都共享同一内存空间,所以后面的成员会把前面的成员覆盖掉,导致再次访问时会出现奇怪的数据,所以一般很少用共用体变量
C++语言本身再编译器层面做了很多底层的工作,如果对C++的理解只限于语言层面,我相信很难成为优秀的C++开发者,因此,面向对象分析是本博客的重点,需要认真读,最好是写写简单的代码,对照我的博客看汇编和源代码,对深入理解C++很有帮助!
每个对象都有个指向该对象的this指针,对象通过this指针调用成员或函数。注意构造函数是有返回值的,返回的是构造的对象的指针,用eax寄存器存储。注意this指针和指向对象的指针不同!
查看构造函数,可看到先获取this指针,然后初始化this指针指向的对象(这里只有一个对象成员m_pBuff,故地址也就是this指针)为0,最后返回该指向该对象的指针(也是this指针,想想为什么?)
观察内存布局,先定义的类成员会放在低地址,后定义的成员放高地址。
this指针:ecx中保存的值,但并不是一定就用ecx保存this指针,但大部分情况下是这样的。
类成员:ecx保存的地址加上对应偏移量的地址,指向的数据。
有参构造函数和无参相比只是多了参数传递, 都是在调用构造前,将this指针放入ecx中
对象调用相同的析构函数,但传递的this指针不同,故能正确释放对象。
在main函数之前定义,在程序加载前就存在。其构造和析构与普通构造、析构略有不同。
通过栈图可看到,全局对象都在_cinit()中预先初始化,然后调用_inititerm并通过二级指针遍历函数指针,找到全局对象的构造函数并调用
查看栈试图,可以看到调用流程。
doexit中通过函数指针遍历循环,通过委托函数调用全局函数的析构函数。
也是将this指针通过ecx传递,通过this指针寻找成员变量。如图
拷贝构造函数比普通构造函数多个将g_MyString地址作为参数的过程
拷贝构造函数将要复制的类的地址作为参数,被拷贝的对象和新对象是不同对象,只不过再拷贝过程中,使用引用获取拷贝内容,不要把拷贝函数和对象引用搞混。
先调用new申请堆空间(汇编代码中有检测是否new成功)再调用构造
析构刚好相反先析构对象(对象指针的析构会有个中间代理,由代理决定是否调用析构函数),再释放堆空间
子类的构造函数中会将this指针传给父类,并使用该this指针调用父类的成员或函数。
且父类成员变量在低地址,子类成员变量在高地址,根据定义顺序依次排列。
在子类析构函数中,和构造函数一样利用子类的this指针调用父类的析构函数。
有虚函数的类的构造函数中会初始化一个虚函数表,这个虚函数表有对应的虚函数地址。再有了虚函数情况下,会在虚表未初始化情况下调用父类构造函数
不能在构造、析构中调用虚函数,不起作用(子类的构造函数未将虚函数表初始化就调用父类构造函数,故调用父类构造后this指针指向的是子类对象,所以调用函数还是普通调用没有经过虚函数表查询;析构类似)
类中有虚函数的类,会提供默认的构造函数初始化虚函数表指针,否则就没有默认构造函数
一个类同时继承多个类,具有继承类中所有类成员、类方法。按照继承顺序将父类的虚函数表、成员变量及自身虚函数表、成员变量在内存中按顺序初始化。如图所示:
取对象地址给到父类指针中,取出虚表指针,调用对应的虚函数,或成员变量(调整偏移)
对象的强制类型转换实际是通过拷贝构造函数实现(最终还要析构掉生成的对象 ),从而实现调用父类成员变量。
析构是谁先被构造谁就最后被析构
构造时对this指针做加法,有类成员就跳过类成员加虚表长度,析构时,对this指针做减法,就是多重继承最大的特点,只有多重继承中,才会使用this指针做减法
将祖父类带上virtual,表示告诉编译器,如果祖父类没有被构造。则构造一份祖父类的拷贝,否则使用已经构造的那个。再汇编代码中通过标记来判断是否已经被构造
再菱形结构中,内存分布顺序是父类、自己、祖父类
先简单分析C语言的变量、复合变量、程序结构,接着再深入分析面向对象,包括构造函数、析构函数、全局对象、成员函数、拷贝构造函数、对象指针、继承、虚函数、多继承、虚继承。相信看懂博客并动手实践的朋友,会对C++有较深的认识和理解。