.text:00401010 ; void *__thiscall CtmpApp___vector deleting destructor_(CtmpApp *this, unsigned int)
.text:00401010 ??_ECtmpApp@@UAEPAXI@Z proc near ; DATA XREF: .rdata:004034D8o
.text:00401010
.text:00401010 arg_0 = byte ptr 8
.text:00401010
.text:00401010 this = ecx
.text:00401010 push ebp
.text:00401011 mov ebp, esp
.text:00401013 push esi
.text:00401014 mov esi, this
.text:00401016 call ds:__imp_??1CWinApp@@UAE@XZ ; CWinApp::~CWinApp(void)
.text:0040101C test [ebp+arg_0], 1
.text:00401020 jz short loc_40102C
.text:00401022 push esi
.text:00401023 call ds:__imp_??3@YAXPAX@Z ; operator delete(void *)
.text:00401029 add esp, 4
.text:0040102C
.text:0040102C loc_40102C: ; CODE XREF: CtmpApp::`vector deleting destructor'(uint)+10j
.text:0040102C mov eax, esi
.text:0040102E pop esi
.text:0040102F pop ebp
.text:00401030 retn 4
.text:00401030 ??_ECtmpApp@@UAEPAXI@Z endp


先不说别的,随便贴一段反汇编代码,然后进行分析:


可以看到,一般而言函数都是以

push ebp;

mov  ebp,esp;

开始的因为在函数内部要使用局部变量,而所有的变量都是要有地址记录的,所以要使用ebp,而在新的函数使用ebp之前要压入堆栈,否则会导致父函数崩溃,使用之前先将

其保存到堆栈当中,同时因为esp是指向栈顶的,其次mov ebp,esp是要将之前的栈顶变栈底,因为之后根据函数内部的局部变量的分配,esp不断减小,所以要将函数调用之

前的内容保存起来,也就是,当前函数的起始地址,之后的函数局部变量要在esp之后,随着函数内部变量的不断分配,同时esp的值也会不断减小,我们会看到,在该函数内

部,push和pop并不是成对出现的,但是在

.text:00401022 push esi;

.text:00401023 call ds:operator delete(void*);

这是一步函数调用,push  esi为函数 delete提供参数,在该函数完成后,会自行弹出esi的值,

.text:00401013 push esi;压入堆栈,使用完毕后,在

.text:0040102E pop esi;  弹出堆栈,恢复之前的现场

该函数执行完毕后,.text 将 在函数使用之前压入堆栈的栈顶地址弹出到ebp当中,然后 retn 4说明函数执行完毕,4表示在该函数中弹出4个字节,可能该函数中有占据4个字节

的参数。

所以 函数执行完毕有两种:

pop ebp;                                                                                                      mov esp,ebp;

add esp,64h;//将之前的压入堆栈的空间收回                                         pop ebp;

retn                                                                                                                    retn

函数在执行完毕后通过将堆栈的指针向下移动,恢复ebp,而关闭堆栈,对应之前的mov ebp,esp;打开堆栈,或者通过esp的增加实现关闭堆栈的同样效果。