函数的开始和结束标志

.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的增加实现关闭堆栈的同样效果。

经验分享 程序员 微信小程序 职场和发展