前言
- 首先知道release下就算关闭优化也和dubug下的汇编差了很多,主要是debug 要有很多调试信息存在;
- 如果只需要分析函数调用过程,那么直接看关闭了优化的release 的代码即可;
- 栈空间的开辟与释放,是没有默认初始化的过程的,直接减sp指针或者恢复sp指针即可;
- 所以会存在报错:局部变量不初始化就直接使用;
常见指令
- rep 指令主要是循环(repeat)
汇编指令STOS(Store String Data)
用法:stos dst ;将寄存器(AX,EAX)里的内容存储(store)到内存单元地址(ES:DI),同时CPU自动修改DI,以指向下一元素;
待分析代码
int func(int a, int b)
{
int temp = a + b;
return temp;
}
int main()
{
func(1, 2);
}
release 模式下分析 (单纯的压栈弹栈过程)
func函数
调用压栈过程分析;
- 首先将参数从右向左压栈;再调用函数;(压栈过程在下面main 的部分)
- 将ebp就上一轮调用的函数栈基地址压栈;(用于后面恢复现场)(这里主要是恢复上一个函数的函数栈: ebp、esp)
- 这里有个疑问点
push ecx
ecx 并没有被用到,我自己得理解是push后这里得栈帧占用,否则后面得 [ebp-4] // 疑问点
func函数
返回弹栈过程分析;
- 将当前ebp替换esp;(即直接退出栈空间,这一步就可以直接声明之前的栈空间已经被释放了)
- 弹栈给ebp;(当前栈顶指针已经是当前函数的栈底指针了,当前位置弹栈,就是之前压入的上一个调用函数的栈底指针,所以这里其实恢复了现场)
add esp,8
为了恢复esp,因为之前压了两个参数,所以恢复回去;
- 能看到,栈空间的使用和释放没有默认初始化和清空的过程,这也是栈空间速度快的原因之一;同时也是栈空间不初始化就是用编译器报错的原因;
- 同时可以看到,栈空间中的变量,和传进来的实参的访问,都是根据栈底指针+偏移来用的;
004C1570 push ebp
004C1571 mov ebp,esp
004C1573 push ecx
004C1574 mov eax,dword ptr [ebp+8]
004C1577 add eax,dword ptr [ebp+0Ch]
004C157A mov dword ptr [ebp-4],eax
004C157D mov eax,dword ptr [ebp-4]
004C1580 mov esp,ebp
004C1582 pop ebp
004C1583 ret
00F3A328 push 2
00F3A32A push 1
00F3A32C call func (0F32063h)
00F3A331 add esp,8
debug 模式下分析 (主要是看rep 指令初始化的问题)
func函数
调用压栈过程分析;
- 首先将参数从右向左压栈;再调用函数;(压栈过程在下面main 的部分)
- 将ebp就上一轮调用的函数栈基地址压栈;(用于后面恢复现场)
- 用新的栈顶指针来更新当前ebp;(即从这里开始是当前函数的函数栈帧基地址)
- 更新栈顶指针;(sub esp,0CCh ;0CCh 表示的就是当前函数的栈大小)
- 之前的寄存器压栈保存;ebx 、esi 、edi ;
- 循环的将整个函数栈空间初始化 0x CCh;(这里只有debug情况下才会有,0xCC是int 3指令的机器码,好像叫调试中断指令,如果CPU意外执行这样的指令,证明程序哪里出错了,产生中断)
- 只需要分析到这里即可;因为剩下的更多的都是debug调试信息;
00F35260 push ebp
00F35261 mov ebp,esp
00F35263 sub esp,0CCh
00F35269 push ebx
00F3526A push esi
00F3526B push edi
00F3526C lea edi,[ebp+0CCh]
00F35272 mov ecx,33h
00F35277 mov eax,0CCCCCCCCh
00F3527C rep stos dword ptr es:[edi]
00F3527E mov ecx,offset _CB9A9D65_test@cpp (0F5A04Ah)
00F35283 call @__CheckForDebuggerJustMyCode@4 (0F31726h)
00F35288 mov eax,dword ptr [ebp+8]
00F3528B add eax,dword ptr [ebp+0Ch]
00F3528E mov dword ptr [ebp-8],eax
00F35291 mov eax,dword ptr [ebp-8]
00F35294 pop edi
00F35295 pop esi
00F35296 pop ebx
00F35297 add esp,0CCh
00F3529D cmp ebp,esp
00F3529F call 00F31767
00F352A4 mov esp,ebp
00F352A6 pop ebp
00F3A328 push 2
00F3A32A push 1
00F3A32C call func (0F32063h)
00F3A331 add esp,8
总结
- 函数调用的压栈弹栈过程是简单的, 平时使用看到的很复杂的,很大一部分原因是存在大量debug信息;
- 栈空间的使用十分遍历、快捷;