从反汇编的角度看C++语法(构造函数)

来源:互联网 发布:数据字典中的条目 编辑:程序博客网 时间:2024/05/01 04:22

 

从反汇编的角度看C++语法(hani199012@gmail.com.cn/QQ :1121797386)

 写在前面的话

写这些文章,主要是对自己在学习C++过程中遇到的一些问题进行总结,同时,利用反汇编工具来看C++语法在实际内存中的体现,因为,C++是一门高级语言,其实是写给人看的,但是机器只认识0  1 ,查看在学习过程中编写的一些练习的程序的反汇编代码,这样,看得更加实际,不那么抽象。

主要的参考书籍是《C++反汇编与逆向分析》(钱林松 赵海旭),需要推荐一下这本书,

这本书确实写的很好,我也是通过这本书来学习的,通过这本书,可以深刻的领会,逆向分析的威力。

其实,我也只是个学习者,通过,写一点总结性的文章,放到网上,希望,能够得到读者的批评,进一步能够相互学习。

用到的工具: VC++6.0

构造函数:

#include "stdafx.h"

#include "stdio.h"

class CNumber

{

public:

 CNumber()

 {

  m_nNumber=1;

 }

private:

 int m_nNumber;

};

int main(int argc, char* argv[])

{

 CNumber number;

 return 0;

}

这段代码很简单,当然,重点不是代码本身,而是反汇编代码,通过查看反汇编代码,来认识在一个类创建了一个对象,也就是类实例化一个对象时,在内存中究竟发生了什么。

学习编程也应该保持这份好奇心,凡是探个究竟,一直深挖到不能再挖为止。

用VC6.0(我用的是英文版的)自带的反汇编工具查看(最好先设置一个端点, Build->

StartDebug->Go)单击右上角disassembly就可以查看反汇编代码

@ILT+0(??0CNumber@@QAE@XZ):

00401005   jmp         CNumber::CNumber (00401060)  ;构造函数

@ILT+5(_main):

0040100A   jmp         main (00401020)              ;主函数

0040100F   int         3

00401010   int         3

13:   private:

14:       int m_nNumber;

15:   };

16:

17:   int main(int argc, char* argv[])

18:   {

00401020   push        ebp

00401021   mov         ebp,esp

00401023   sub         esp,44h

00401026   push        ebx

00401027   push        esi

00401028   push        edi

00401029   lea         edi,[ebp-44h]

0040102C   mov         ecx,11h

00401031   mov         eax,0CCCCCCCCh

00401036   rep stos    dword ptr [edi]

19:       CNumber number;

00401038   lea         ecx,[ebp-4]       取得对象首地址,传入ecx中

0040103B   call        @ILT+0(CNumber::CNumber) (00401005);调用构造函数

20:

21:       return 0;

00401040   xor         eax,eax

22:   }

00401042   pop         edi

00401043   pop         esi

00401044   pop         ebx

00401045   add         esp,44h

00401048   cmp         ebp,esp

0040104A   call        __chkesp (004010a0)

0040104F   mov         esp,ebp

00401051   pop         ebp

00401052   ret

1:    // construct.cpp : Defines the entry point for the console application.

2:    //

3:

4:    #include "stdafx.h"

5:    #include "stdio.h"

6:    class CNumber

7:    {

8:    public:

9:        CNumber()

00401060   push        ebp                    ;一系列的入栈出栈动作

00401061   mov         ebp,esp     ;入栈前保存一系列的寄存器的值

00401063   sub         esp,44h                 ;出栈的时候恢复这些寄存器的值

00401066   push        ebx    ;进入一个函数前,这个函数要借寄存器

00401067   push        esi                ;做到有借有还

00401068   push        edi

00401069   push        ecx

0040106A   lea         edi,[ebp-44h]

0040106D   mov         ecx,11h

00401072   mov         eax,0CCCCCCCCh

00401077   rep stos    dword ptr [edi]      

00401079   pop         ecx   ;   还原ecx,ecx中保存对象的首地址,

0040107A   mov         dword ptr [ebp-4],ecx    [ebp-4]就是this指针

10:       {

11:           m_nNumber=1;

0040107D   mov         eax,dword ptr [ebp-4]

00401080   mov         dword ptr [eax],1

12:       }

00401086   mov         eax,dword ptr [ebp-4]

00401089   pop         edi

0040108A   pop         esi

0040108B   pop         ebx

0040108C   mov         esp,ebp

0040108E   pop         ebp

0040108F   ret

按照内存中地址顺序拷贝出来,可以看到,先执行类的构造函数,在执行main函数,

然而,本来写在一起的类的成员变量和构造函数确实被编译器活生生的分割了,

这里也可以推测,所谓,类(暂时不考虑虚函数静态成员等问题)的大小是类的成员变量的大小,而不包括成员函数,是有一定道理的。

通过分析,可知,this指针是有构造函数产生的,构造函数结束后,会将this指针作为返回值。如果没有构造函数,编译器会默认的提供一个构造函数,而类的成员函数是根据this指针区分每个对象的,所以就算没有构造函数,也不会出现分不清类对象的情况。

接下来的情况没有给出代码,

1、类中有构造函数,但是没有成员变量,依然会执行构造函数,产生tihs指针,

    00401079   pop         ecx

     0040107A   mov         dword ptr [ebp-4],ecx

2、空类,什么都没有生成,但是仍然会有构造函数,而这个地址却什么都没有

@ILT+0(??0CNumber@@QAE@XZ):

00401005   jmp         CNumber::CNumber (00401060)

@ILT+5(_main):

0040100A   jmp         main (00401020)

据说,C++编译时空类的大小是1,可以推测,这个空类的大小和构造函数没有什么关系,可能仅仅是为了保证语法的一致性,编译器就让空类为1了

3, 没有构造函数或其他成员函数,只有成员变量

  同上一个情况类似

4, 有成员函数的时候,会将看到

00401079   pop         ecx

0040107A   mov         dword ptr [ebp-4],ecx

代码的出现,表示this 指针出现,而这唯一的一个成员函数返回this指针,作为这个类的构造函数

13:   int  foo()

14:   {

00401060   push        ebp

00401061   mov         ebp,esp

00401063   sub         esp,44h

00401066   push        ebx

00401067   push        esi

00401068   push        edi

00401069   push        ecx

0040106A   lea         edi,[ebp-44h]

0040106D   mov         ecx,11h

00401072   mov         eax,0CCCCCCCCh

00401077   rep stos    dword ptr [edi]

00401079   pop         ecx

0040107A   mov         dword ptr [ebp-4],ecx

15:    return 3;

0040107D   mov         eax,3

16:   }

00401082   pop         edi

00401083   pop         esi

00401084   pop         ebx

00401085   mov         esp,ebp

00401087   pop         ebp

00401088   ret

综上,构造函数并不是总会存在的。