从逆向分析角度看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 对象所占的栈空间被回收
- 从逆向分析角度看C++的析构函数
- 从逆向分析角度看C++拷贝构造函数
- 从逆向分析角度看C++虚函数
- 从逆向分析角度看C++ 中的引用
- 从内存角度看C函数的调用过程
- 从编译器的角度看C代码
- 从汇编角度看Linux C函数的调用约定和参数传递的细节
- C++的virtual函数(从编译器的角度看)
- 从编译器的角度看C++的virtual函数
- 从反汇编的角度看C++语法(构造函数)
- Swift要点:从Objective-C开发者的角度看Swift
- 从编译器的角度分析继承中的虚函数
- 从汇编角度分析C语言的本质(一)
- 网站分析:从客户的角度看网站分析的实施
- 从OOP的角度重看C++(四)——从程序中看OOP
- 从gdb角度分析,为什么C语言中的scanf函数的变量要用“&”表示首地址
- 从汇编的角度看inline函数与非inline函数
- 从FrameWork的角度看Creational Pattern
- Android实现推送方式解决方案
- ZOJ 2724/HDU 1509 Windows Message Queue(优先队列)
- [算法导论]归并排序
- ubuntu10.10 配置android开发环境
- 压缩感知有关稀疏矩阵(中国压缩感知资源)
- 从逆向分析角度看C++的析构函数
- Linux存储实验一:在硬盘上创建文件系统
- c++this指针
- Unity3D读取Ios中的动态UIImage图片
- 从程序员到项目经理(7):程序员加油站 -- 完美主义也是一种错
- java的学习路程之启程。
- デリバティブ講義
- MFC线程(三):线程同步事件(event)与互斥(mutex)
- POJ 1556 线段相交+最短路