看流星社区

 找回密码
 注册账号
查看: 2062|回复: 0

从逆向分析角度看C++的析构函数

[复制链接]

该用户从未签到

发表于 2013-3-17 09:30:22 | 显示全部楼层 |阅读模式
有这么一段C++代码:

  [cpp] view plaincopyprint?

  01.#include <cstdio>

  02.

  03.class CExample {

  04.  int x, y;

  05.

  06.public:

  07.  CExample(int va, int vb) {

  08.    x = va;

  09.    y = vb;

  10.  }

  11.  CExample(const CExample& C) {

  12.    x = C.x;

  13.    y = C.y;

  14.  }

  15.  ~CExample() {

  16.    printf("~CExample()\n");

  17.  }

  18.

  19.  void Show () {

  20.    printf("%d, %d\n", x, y);

  21.    return ;

  22.  }

  23.};

  24.

  25.void fun(CExample E) {

  26.  printf("In F(CExample)\n");

  27.  return ;

  28.}

  29.

  30.int main() {

  31.  CExample A(100, 200);

  32.  CExample B = A;

  33.  B.Show();

  34.  fun(A);

  35.  return 0;

  36.}

  #include <cstdio>

  class CExample {

  int x, y;

  public:

  CExample(int va, int vb) {

  x = va;

  y = vb;

  }

  CExample(const CExample& C) {

  x = C.x;

  y = C.y;

  }

  ~CExample() {

  printf("~CExample()\n");

  }

  void Show () {

  printf("%d, %d\n", x, y);

  return ;

  }

  };

  void fun(CExample E) {

  printf("In F(CExample)\n");

  return ;

  }

  int main() {

  CExample A(100, 200);

  CExample B = A;

  B.Show();

  fun(A);

  return 0;

  }

  运行结果:





  编译调试的环境是 VC 6.0,注释如下:

  main 函数:

  [plain] view plaincopyprint?

  01.30:   int main() {

  02.00401130   push        ebp

  03.00401131   mov         ebp,esp

  04.00401133   push        0FFh

  05.00401135   push        offset __ehhandler$_main (00413422)

  06.0040113A   mov         eax,fs:[00000000]

  07.00401140   push        eax

  08.00401141   mov         dword ptr fs:[0],esp

  09.00401148   sub         esp,5Ch

  10.0040114B   push        ebx

  11.0040114C   push        esi

  12.0040114D   push        edi

  13.0040114E   lea         edi,[ebp-68h]

  14.00401151   mov         ecx,17h

  15.00401156   mov         eax,0CCCCCCCCh

  16.0040115B   rep stos    dword ptr [edi]              ; 以上代码为建立堆栈,EBP = 1245000

  17.31:     CExample A(100, 200);

  18.0040115D   push        0C8h                 ; 将 200 压入堆栈中

  19.00401162   push        64h                  ; 将 100 压入堆栈中

  20.00401164   lea         ecx,[ebp-14h]                ; EBP-14h 是 A 的内存地址,占 8 个字节,将该地址保存到 ECX 寄存器中

  21.00401167   call        @ILT+0(CExample::CExample) (00401005)    ; 调用构造函数 CExample(int, int)

  22.0040116C   mov         dword ptr [ebp-4],0

  23.32:     CExample B = A;

  24.00401173   lea         eax,[ebp-14h]                ; 将 A 的内存地址保存到 EAX 寄存器中

  25.00401176   push        eax                  ; 将 EAX 压入堆栈中

  26.00401177   lea         ecx,[ebp-1Ch]                ; EBP-1Ch 是 B 的内存地址,占 8 个字节,将该地址保存到 ECX 寄存器中

  27.0040117A   call        @ILT+20(CExample::CExample) (00401019)   ; 调用拷贝构造函数,将A中的值复制到 B 的内存空间中,

  28.

  29.                                ; 细心观察一下栈地址就能明白

  30.0040117F   mov         byte ptr [ebp-4],1

  31.33:     B.Show();

  32.00401183   lea         ecx,[ebp-1Ch]

  33.00401186   call        @ILT+25(CExample::Show) (0040101e)

  34.34:     fun(A);

  35.0040118B   sub         esp,8                    ; ESP = 1244884, ESP - 8 = 1244876;ESP-8 是开辟的一块内存,

  36.                                ; 在调用fun之前,先调用拷贝构造函数对 A 进行拷贝,拷贝的内容就

  37.                                ; 存放在这块内存空间中(一个CExample对象,记为Temp)

  38.0040118E   mov         ecx,esp                  ; ECX 寄存器保存 Temp 对象的内存地址

  39.00401190   mov         dword ptr [ebp-20h],esp          ; EBP-20h 这块空间保存 Temp 对象的内存地址

  40.00401193   lea         edx,[ebp-14h]                ; EDX 寄存器保存 A 对象的内存地址

  41.00401196   push        edx                  ; ESP = 1244872,将 A 的内存地址压入栈

  42.00401197   call        @ILT+20(CExample::CExample) (00401019)   ; ESP = 1244868,函数返回地址压入栈,调用拷贝构造函数

  43.0040119C   mov         dword ptr [ebp-28h],eax          ; ESP = 1244876, EBP - 28h = 1244876,保存 Temp 的内存地址

  44.0040119F   call        @ILT+15(fun) (00401014)          ; ESP = 1244872,函数返回地址压入栈,调用fun函数

  45.004011A4   add         esp,8                    ; __cdecl 规则,恢复堆栈段,EBP = 1245000

  46.35:     return 0;

  47.004011A7   mov         dword ptr [ebp-24h],0

  48.004011AE   mov         byte ptr [ebp-4],0

  49.004011B2   lea         ecx,[ebp-1Ch]                ; EBP - 1Ch 是 B 的内存地址,保存到 ECX 中

  50.004011B5   call        @ILT+5(CExample::~CExample) (0040100a)   ; 调用析构函数,销毁对象 B

  51.004011BA   mov         dword ptr [ebp-4],0FFFFFFFFh

  52.004011C1   lea         ecx,[ebp-14h]                ; EBP - 14h 是 A 的内存地址,保存到 ECX 中

  53.004011C4   call        @ILT+5(CExample::~CExample) (0040100a)   ; 调用析构函数,销毁对象 A

  54.004011C9   mov         eax,dword ptr [ebp-24h]

  55.36:   }

  30:   int main() {

  00401130   push        ebp

  00401131   mov         ebp,esp

  00401133   push        0FFh

  00401135   push        offset __ehhandler$_main (00413422)

  0040113A   mov         eax,fs:[00000000]

  00401140   push        eax

  00401141   mov         dword ptr fs:[0],esp

  00401148   sub         esp,5Ch

  0040114B   push        ebx

  0040114C   push        esi

  0040114D   push        edi

  0040114E   lea         edi,[ebp-68h]

  00401151   mov         ecx,17h

  00401156   mov         eax,0CCCCCCCCh

  0040115B   rep stos    dword ptr [edi]; 以上代码为建立堆栈,EBP = 1245000

  31:     CExample A(100, 200);

0040115D   push        0C8h; 将 200 压入堆栈中

  00401162   push        64h; 将 100 压入堆栈中

  00401164   lea         ecx,[ebp-14h]; EBP-14h 是 A 的内存地址,占 8 个字节,将该地址保存到 ECX 寄存器中

  00401167   call        @ILT+0(CExample::CExample) (00401005); 调用构造函数 CExample(int, int)

  0040116C   mov         dword ptr [ebp-4],0

  32:     CExample B = A;

  00401173   lea         eax,[ebp-14h]; 将 A 的内存地址保存到 EAX 寄存器中

  00401176   push        eax; 将 EAX 压入堆栈中

  00401177   lea         ecx,[ebp-1Ch]; EBP-1Ch 是 B 的内存地址,占 8 个字节,将该地址保存到 ECX 寄存器中

  0040117A   call        @ILT+20(CExample::CExample) (00401019); 调用拷贝构造函数,将A中的值复制到 B 的内存空间中,

  ; 细心观察一下栈地址就能明白

  0040117F   mov         byte ptr [ebp-4],1

  33:     B.Show();

  00401183   lea         ecx,[ebp-1Ch]

  00401186   call        @ILT+25(CExample::Show) (0040101e)

  34:     fun(A);

  0040118B   sub         esp,8; ESP = 1244884, ESP - 8 = 1244876;ESP-8 是开辟的一块内存,

  ; 在调用fun之前,先调用拷贝构造函数对 A 进行拷贝,拷贝的内容就

  ; 存放在这块内存空间中(一个CExample对象,记为Temp)

  0040118E   mov         ecx,esp; ECX 寄存器保存 Temp 对象的内存地址

  00401190   mov         dword ptr [ebp-20h],esp; EBP-20h 这块空间保存 Temp 对象的内存地址

  00401193   lea         edx,[ebp-14h]; EDX 寄存器保存 A 对象的内存地址

  00401196   push        edx; ESP = 1244872,将 A 的内存地址压入栈

  00401197   call        @ILT+20(CExample::CExample) (00401019); ESP = 1244868,函数返回地址压入栈,调用拷贝构造函数

  0040119C   mov         dword ptr [ebp-28h],eax; ESP = 1244876, EBP - 28h = 1244876,保存 Temp 的内存地址

  0040119F   call        @ILT+15(fun) (00401014); ESP = 1244872,函数返回地址压入栈,调用fun函数

  004011A4   add         esp,8; __cdecl 规则,恢复堆栈段,EBP = 1245000

  35:     return 0;

  004011A7   mov         dword ptr [ebp-24h],0

  004011AE   mov         byte ptr [ebp-4],0

  004011B2   lea         ecx,[ebp-1Ch]; EBP - 1Ch 是 B 的内存地址,保存到 ECX 中

  004011B5   call        @ILT+5(CExample::~CExample) (0040100a); 调用析构函数,销毁对象 B

  004011BA   mov         dword ptr [ebp-4],0FFFFFFFFh

  004011C1   lea         ecx,[ebp-14h]; EBP - 14h 是 A 的内存地址,保存到 ECX 中

  004011C4   call        @ILT+5(CExample::~CExample) (0040100a); 调用析构函数,销毁对象 A

  004011C9   mov         eax,dword ptr [ebp-24h]

  36:   }

  拷贝构造函数 CExample(const CExample& C):

  [plain] view plaincopyprint?

  01.11:     CExample(const CExample& C) {

  02.00401270   push        ebp

  03.00401271   mov         ebp,esp

  04.00401273   sub         esp,44h

  05.00401276   push        ebx

  06.00401277   push        esi

  07.00401278   push        edi

  08.00401279   push        ecx

  09.0040127A   lea         edi,[ebp-44h]

  10.0040127D   mov         ecx,11h

  11.00401282   mov         eax,0CCCCCCCCh

  12.00401287   rep stos    dword ptr [edi]          ; 以上为建立堆栈代码,EBP = 1244864

  13.00401289   pop         ecx              ; 恢复 ECX 寄存器的内容,即为 Temp 的内存地址

  14.0040128A   mov         dword ptr [ebp-4],ecx        ; EBP - 4 = 1244860,这块栈内存保存 Temp 的内存地址

  15.12:       x = C.x;

  16.0040128D   mov         eax,dword ptr [ebp-4]        ; EBP - 4 = 1244860,EAX 获得 Temp 的内存地址(EAX 指向 Temp)

  17.00401290   mov         ecx,dword ptr [ebp+8]        ; EBP + 8 = 1244872,ECX 获得 A 的内存地址(ECX 指向 A)

  18.00401293   mov         edx,dword ptr [ecx]      ; EDX = 100(A 的首地址是 100,100 后于 200 进栈)

  19.00401295   mov         dword ptr [eax],edx      ; Temp.x = 100

  20.13:       y = C.y;

  21.00401297   mov         eax,dword ptr [ebp-4]

  22.0040129A   mov         ecx,dword ptr [ebp+8]

  23.0040129D   mov         edx,dword ptr [ecx+4]        ; EDX = 200

  24.004012A0   mov         dword ptr [eax+4],edx        ; 同理,Temp.y = 200

  25.14:     }

  26.004012A3   mov         eax,dword ptr [ebp-4]        ; EAX 寄存器保存 Temp 的内存空间

  27.004012A6   pop         edi

  28.004012A7   pop         esi

  29.004012A8   pop         ebx

  30.004012A9   mov         esp,ebp

  31.004012AB   pop         ebp              ; 恢复EBP,EBP = 1245000

  32.004012AC   ret         4                ; 恢复这一函数的堆栈段

  11:     CExample(const CExample& C) {

  00401270   push        ebp

  00401271   mov         ebp,esp

  00401273   sub         esp,44h

  00401276   push        ebx

  00401277   push        esi

  00401278   push        edi

  00401279   push        ecx

  0040127A   lea         edi,[ebp-44h]

  0040127D   mov         ecx,11h

  00401282   mov         eax,0CCCCCCCCh

  00401287   rep stos    dword ptr [edi]; 以上为建立堆栈代码,EBP = 1244864

  00401289   pop         ecx; 恢复 ECX 寄存器的内容,即为 Temp 的内存地址

  0040128A   mov         dword ptr [ebp-4],ecx; EBP - 4 = 1244860,这块栈内存保存 Temp 的内存地址

  12:       x = C.x;

  0040128D   mov         eax,dword ptr [ebp-4]; EBP - 4 = 1244860,EAX 获得 Temp 的内存地址(EAX 指向 Temp)

  00401290   mov         ecx,dword ptr [ebp+8]; EBP + 8 = 1244872,ECX 获得 A 的内存地址(ECX 指向 A)

  00401293   mov         edx,dword ptr [ecx]; EDX = 100(A 的首地址是 100,100 后于 200 进栈)

  00401295   mov         dword ptr [eax],edx; Temp.x = 100

  13:       y = C.y;

  00401297   mov         eax,dword ptr [ebp-4]

  0040129A   mov         ecx,dword ptr [ebp+8]

  0040129D   mov         edx,dword ptr [ecx+4]; EDX = 200

  004012A0   mov         dword ptr [eax+4],edx; 同理,Temp.y = 200

  14:     }

  004012A3   mov         eax,dword ptr [ebp-4]; EAX 寄存器保存 Temp 的内存空间

  004012A6   pop         edi

  004012A7   pop         esi

  004012A8   pop         ebx

  004012A9   mov         esp,ebp

  004012AB   pop         ebp; 恢复EBP,EBP = 1245000

  004012AC   ret         4; 恢复这一函数的堆栈段

  fun 函数:

  [plain] view plaincopyprint?

  01.25:   void fun(CExample E) {

  02.00401050   push        ebp

  03.00401051   mov         ebp,esp

  04.00401053   push        0FFh

  05.00401055   push        offset __ehhandler$?fun@@YAXVCExample@@@Z (004133f9)

  06.0040105A   mov         eax,fs:[00000000]

  07.00401060   push        eax

  08.00401061   mov         dword ptr fs:[0],esp

  09.00401068   sub         esp,40h

  10.0040106B   push        ebx

  11.0040106C   push        esi

  12.0040106D   push        edi

  13.0040106E   lea         edi,[ebp-4Ch]

  14.00401071   mov         ecx,10h

  15.00401076   mov         eax,0CCCCCCCCh

  16.0040107B   rep stos    dword ptr [edi]

  17.0040107D   mov         dword ptr [ebp-4],0              ; EBP = 1244868,以上为建立堆栈代码

  18.26:     printf("In F(CExample)\n");

  19.00401084   push        offset string "In F(CExample)\n" (0042501c)

  20.00401089   call        printf (00401320)

  21.0040108E   add         esp,4

  22.27:     return ;

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

  24.00401098   lea         ecx,[ebp+8]                  ; EBP+8 = 1244876,是 Temp 的内存地址

  25.                                    ; ECX 寄存器保存

  26.0040109B   call        @ILT+5(CExample::~CExample) (0040100a)       ; 调用析构函数,销毁 Temp 对象(生命期结束)

  27.28:   }

  28.004010A0   mov         ecx,dword ptr [ebp-0Ch]

  29.004010A3   mov         dword ptr fs:[0],ecx

  30.004010AA   pop         edi

  31.004010AB   pop         esi

  32.004010AC   pop         ebx

  33.004010AD   add         esp,4Ch

  34.004010B0   cmp         ebp,esp

  35.004010B2   call        __chkesp (00401780)

  36.004010B7   mov         esp,ebp

  37.004010B9   pop         ebp                      ; 恢复EBP,EBP = 1245000

  38.004010BA   ret

  25:   void fun(CExample E) {

00401050   push        ebp

  00401051   mov         ebp,esp

  00401053   push        0FFh

  00401055   push        offset __ehhandler$?fun@@YAXVCExample@@@Z (004133f9)

  0040105A   mov         eax,fs:[00000000]

  00401060   push        eax

  00401061   mov         dword ptr fs:[0],esp

  00401068   sub         esp,40h

  0040106B   push        ebx

  0040106C   push        esi

  0040106D   push        edi

  0040106E   lea         edi,[ebp-4Ch]

  00401071   mov         ecx,10h

  00401076   mov         eax,0CCCCCCCCh

  0040107B   rep stos    dword ptr [edi]

  0040107D   mov         dword ptr [ebp-4],0; EBP = 1244868,以上为建立堆栈代码

  26:     printf("In F(CExample)\n");

  00401084   push        offset string "In F(CExample)\n" (0042501c)

  00401089   call        printf (00401320)

  0040108E   add         esp,4

  27:     return ;

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

  00401098   lea         ecx,[ebp+8]; EBP+8 = 1244876,是 Temp 的内存地址

  ; ECX 寄存器保存

  0040109B   call        @ILT+5(CExample::~CExample) (0040100a); 调用析构函数,销毁 Temp 对象(生命期结束)

  28:   }

  004010A0   mov         ecx,dword ptr [ebp-0Ch]

  004010A3   mov         dword ptr fs:[0],ecx

  004010AA   pop         edi

  004010AB   pop         esi

  004010AC   pop         ebx

  004010AD   add         esp,4Ch

  004010B0   cmp         ebp,esp

  004010B2   call        __chkesp (00401780)

  004010B7   mov         esp,ebp

  004010B9   pop         ebp; 恢复EBP,EBP = 1245000

  004010BA   ret

  下面重点分析一下调用 fun 函数时程序的工作机制:

  执行“fun(A);”时,首先先调用拷贝构造函数,创建一个实体对象Temp,占8个字节长度的栈空间,内容是拷贝 A 的内容;然后再把对象 Temp 的内存地址压入 fun 函数的堆栈中,调用 fun 函数,当 fun 函数结束时,调用析构函数销毁 Temp 对象,从此 Temp 对象所占的栈空间被回收
点击按钮快速添加回复内容: 支持 高兴 激动 给力 加油 苦寻 生气 回帖 路过 感恩
您需要登录后才可以回帖 登录 | 注册账号

本版积分规则

小黑屋|手机版|Archiver|看流星社区 |网站地图

GMT+8, 2024-5-5 20:43

Powered by Kanliuxing X3.4

© 2010-2019 kanliuxing.com

快速回复 返回顶部 返回列表