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

来源:互联网 发布:mac 电源 爆 编辑:程序博客网 时间:2024/05/17 08:07

有这么一段C++代码:

#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 函数:

30:   int main() {00401130   push        ebp00401131   mov         ebp,esp00401133   push        0FFh00401135   push        offset __ehhandler$_main (00413422)0040113A   mov         eax,fs:[00000000]00401140   push        eax00401141   mov         dword ptr fs:[0],esp00401148   sub         esp,5Ch0040114B   push        ebx0040114C   push        esi0040114D   push        edi0040114E   lea         edi,[ebp-68h]00401151   mov         ecx,17h00401156   mov         eax,0CCCCCCCCh0040115B   rep stos    dword ptr [edi]; 以上代码为建立堆栈,EBP = 124500031:     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],032:     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],133:     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 = 124500035:     return 0;004011A7   mov         dword ptr [ebp-24h],0004011AE   mov         byte ptr [ebp-4],0004011B2   lea         ecx,[ebp-1Ch]; EBP - 1Ch 是 B 的内存地址,保存到 ECX 中004011B5   call        @ILT+5(CExample::~CExample) (0040100a); 调用析构函数,销毁对象 B004011BA   mov         dword ptr [ebp-4],0FFFFFFFFh004011C1   lea         ecx,[ebp-14h]; EBP - 14h 是 A 的内存地址,保存到 ECX 中004011C4   call        @ILT+5(CExample::~CExample) (0040100a); 调用析构函数,销毁对象 A004011C9   mov         eax,dword ptr [ebp-24h]36:   }


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

11:     CExample(const CExample& C) {00401270   push        ebp00401271   mov         ebp,esp00401273   sub         esp,44h00401276   push        ebx00401277   push        esi00401278   push        edi00401279   push        ecx0040127A   lea         edi,[ebp-44h]0040127D   mov         ecx,11h00401282   mov         eax,0CCCCCCCCh00401287   rep stos    dword ptr [edi]; 以上为建立堆栈代码,EBP = 124486400401289   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 = 10013:       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 = 200004012A0   mov         dword ptr [eax+4],edx; 同理,Temp.y = 20014:     }004012A3   mov         eax,dword ptr [ebp-4]; EAX 寄存器保存 Temp 的内存空间004012A6   pop         edi004012A7   pop         esi004012A8   pop         ebx004012A9   mov         esp,ebp004012AB   pop         ebp; 恢复EBP,EBP = 1245000004012AC   ret         4; 恢复这一函数的堆栈段

fun 函数:

25:   void fun(CExample E) {00401050   push        ebp00401051   mov         ebp,esp00401053   push        0FFh00401055   push        offset __ehhandler$?fun@@YAXVCExample@@@Z (004133f9)0040105A   mov         eax,fs:[00000000]00401060   push        eax00401061   mov         dword ptr fs:[0],esp00401068   sub         esp,40h0040106B   push        ebx0040106C   push        esi0040106D   push        edi0040106E   lea         edi,[ebp-4Ch]00401071   mov         ecx,10h00401076   mov         eax,0CCCCCCCCh0040107B   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,427:     return ;00401091   mov         dword ptr [ebp-4],0FFFFFFFFh00401098   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],ecx004010AA   pop         edi004010AB   pop         esi004010AC   pop         ebx004010AD   add         esp,4Ch004010B0   cmp         ebp,esp004010B2   call        __chkesp (00401780)004010B7   mov         esp,ebp004010B9   pop         ebp; 恢复EBP,EBP = 1245000004010BA   ret


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



执行“fun(A);”时,首先先调用拷贝构造函数,创建一个实体对象Temp,占8个字节长度的栈空间,内容是拷贝 A 的内容;然后再把对象 Temp 的内存地址压入 fun 函数的堆栈中,调用 fun 函数,当 fun 函数结束时,调用析构函数销毁 Temp 对象,从此 Temp 对象所占的栈空间被回收


原创粉丝点击