欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创]从逆向分析角度看C++拷贝构造函数

[原创]从逆向分析角度看C++拷贝构造函数

一段C++代码:


//: HowMany_2.cpp

#include <iostream>

using namespace std;

class HowMany {

  static int objectCount;

  

public:

  HowMany() {

    ++objectCount;

    print("HowMany()");

  }

  ~HowMany() {

    --objectCount;

    print("~HowMany()");

  }

  HowMany(const HowMany& h) {

    ++objectCount;

    print("HowMany(const HowMany&)");

  }

  

  void print(const char ss[]) {

    cout << ss << ": ";

    cout << "objectCount = " << objectCount << endl;

    return ;

  }

};

int HowMany::objectCount = 0;

HowMany f(HowMany x) {

  x.print("x argument inside f()");

  cout << "Return From f()" << endl;

  return x;  // 有返回值 x

}

int main() {

  {

    HowMany h;

    cout << "Entering f()" << endl;

    HowMany h2 = f(h);

  }

  return 0;

} ///:~


运行结果:

Assembly Code:

38:   int main() {

39:     {

40:       HowMany h;

004017FD   lea         ecx,[h]          ; [h] 为对象 h 的内存地址

00401800   call        @ILT+685(HowMany::HowMany) (004012b2)  ; 调用构造函数

00401805   mov         dword ptr [ebp-4],0

41:       cout << "Entering f()" << endl;

0040180C   push        offset @ILT+200(std::endl) (004010cd)

00401811   push        offset string "Entering f()" (0046f090)

00401816   push        offset std::cout (0047ce98)

0040181B   call        @ILT+660(std::operator<<) (00401299)  ; 题外话,观察一下进栈顺序

00401820   add         esp,8

00401823   mov         ecx,eax

00401825   call        @ILT+480(std::basic_ostream<char,std::char_traits<char> >::operator<<) (004011e5)

42:       HowMany h2 = f(h);

0040182A   push        ecx

0040182B   mov         ecx,esp          ; 当前 ESP 所指的栈块作为临时对象Temp的内存地址

0040182D   mov         dword ptr [ebp-18h],esp

00401830   lea         eax,[h]          

00401833   push        eax          ; 将h的内存地址[h]压入堆栈

00401834   call        @ILT+0(HowMany::HowMany) (00401005)  ; 调用拷贝构造函数,把h的内容拷贝到Temp的内存中

00401839   mov         dword ptr [ebp-1Ch],eax

0040183C   lea         ecx,[h2]  

0040183F   push        ecx          ; 将h2的内存地址[h2]压入堆栈

00401840   call        @ILT+640(f) (00401285)      ; 调用f()函数

00401845   add         esp,8

00401848   mov         dword ptr [ebp-20h],eax

43:     }

0040184B   lea         ecx,[h2]

0040184E   call        @ILT+500(HowMany::~HowMany) (004011f9)  ; 调用析构函数,销毁h2

00401853   mov         dword ptr [ebp-4],0FFFFFFFFh

0040185A   lea         ecx,[h]

0040185D   call        @ILT+500(HowMany::~HowMany) (004011f9)  ; 调用析构函数,销毁h

44:     // getchar();

45:     return 0;

00401862   xor         eax,eax

46:   } ///:~


fun()函数的工作机制:

32:   HowMany f(HowMany x) {

33:     x.print("x argument inside f()");

004015DB   push        offset string "x argument inside f()" (0046f030)

004015E0   lea         ecx,[ebp+0Ch]

004015E3   call        @ILT+575(HowMany::print) (00401244)

34:     cout << "Return From f()" << endl;

004015E8   push        offset @ILT+200(std::endl) (004010cd)

004015ED   push        offset string "Return From f()" (0046f01c)

004015F2   push        offset std::cout (0047ce98)

004015F7   call        @ILT+660(std::operator<<) (00401299)

004015FC   add         esp,8

004015FF   mov         ecx,eax

00401601   call        @ILT+480(std::basic_ostream<char,std::char_traits<char> >::operator<<) (004011e5)

35:     return x;

00401606   lea         eax,[ebp+0Ch]        ; [ebp+0C]为Temp的内存地址

00401609   push        eax

0040160A   mov         ecx,dword ptr [ebp+8]      ; [ebp+8]为h2的内存地址,ecx指向h2内存块

0040160D   call        @ILT+0(HowMany::HowMany) (00401005)  ; 调用拷贝构造函数,将Temp的内容拷贝到h2的内存中

00401612   mov         ecx,dword ptr [ebp-10h]

00401615   or          ecx,1

00401618   mov         dword ptr [ebp-10h],ecx

0040161B   mov         byte ptr [ebp-4],0

0040161F   lea         ecx,[ebp+0Ch]        ; ecx保存Temp的内存地址

00401622   call        @ILT+500(HowMany::~HowMany) (004011f9)  ; 调用析构函数,销毁Temp

00401627   mov         eax,dword ptr [ebp+8]      ; eax保存h2的内存地址

36:   }


对于运行结果

解析如下:
1. 对象h调用构造函数
2. 输出字符串“Entering f()”
3. 在进入f()函数之前,创建了一个临时对象Temp,调用拷贝构造函数,将对象h的内容拷贝到Temp中
4. 进入f()函数,在执行“return x”语句后,调用拷贝构造函数,将对象Temp的内容拷贝到h2中(完成了h2 = f(h)的工作)
5. 在f()函数结束前,为Temp调用析构函数,销毁Temp对象
6. 退出f()函数,在main函数结束前,先销毁对象h2,最后销毁对象h
----------------------------------------------------------------------------
逆向分析时执行“HowMany h2 = f(h);”语句过程中栈分布的记录:
*****
进入f()函数前(创建了一个临时对象Temp,调用拷贝构造函数,将对象h的内容拷贝到Temp中)

进入f()函数后

*********************************************************
再看<<Thinking in C++>>中的一段代码,更为清晰的讲解了拷贝构造函数的机制:

//: HowMany_2.cpp

#include <string>

#include <iostream>

using namespace std;

class HowMany {

  string name;

  static int objectCount;

  

public:

  HowMany(const string& id = "") {

    name = id;

    ++objectCount;

    print("HowMany()");

  }

  ~HowMany() {

    --objectCount;

    print("~HowMany()");

  }

  HowMany(const HowMany& h) {

    name = h.name + "copy";

    ++objectCount;

    print("HowMany(const HowMany&)");

  }

  

  void print(const string& msg = "") {

    if (msg.length() != 0) {

      cout << msg << endl;

    }

    cout << '\t' << name << ": "

     << "objectCount = " << objectCount << endl;

    

    return ;

  }

};

int HowMany::objectCount = 0;

HowMany f(HowMany x) {

  x.print("x argument inside f()");

  cout << "Return From f()" << endl;

  return x;

}

int main() {

  {

    HowMany h("h");

    cout << "Entering f()" << endl;

    HowMany h2 = f(h);

    cout << "Call f(), no return value" << endl;

    f(h);

    cout << "After call to f()" << endl;

  }

  // getchar();

  return 0;

} ///:~


运行结果:

解释如下:
1. 创建对象h,并调用构造函数
2. 输出“Entering f()”字符串
3. 进入f()函数前,创建一个临时对象Temp,并调用拷贝构造函数,将对象h的内容拷贝到Temp中
4. 进入f()函数后,在“return x”时,调用拷贝构造函数,将对象Temp的内容拷贝到h2中,在函数结束前调用析构函数,销毁对象Temp
5. 输出字符串
6.进入f()函数前,创建一个临时对象Temp,并调用拷贝构造函数,将对象h的内容拷贝到Temp中
7. 进入f()函数后,创建一个临时对象x,在“return x”时,调用拷贝构造函数,将对象Temp的内容拷贝到x中,在函数结束前调用析构函数,先销毁对象Temp,再销毁对象x
8. 在main函数结束前,调用析构函数,先销毁对象h2,再销毁对象h

上传的附件 CopyConstructor_1.png
CopyConstructor_2.png
CopyConstructor_3.jpg
CopyConstructor_4.jpg
CopyConstructor_5.png

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途。
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功