E40.【C语言】练习:有关栈帧的讨论(反汇编分析)
目录
1.题目
2.解
反汇编查看F1函数
3.改编
解:
反汇编分析
内存图
1.题目
下列代码中,F1的a和F2的b是否使用同一块栈帧空间?
#include <stdio.h>
void F1()
{int a = 0;printf("%p\n", &a);
}void F2()
{int b = 0;printf("%p", &b);
}int main()
{F1();F2();
}
2.解
程序运行时,VS先为main函数申请栈帧,执行到F1函数时再为其申请栈帧空间,但注意以下写法:
F1();F2();
F1()运行完后,其栈帧被销毁,交换操作系统后,再执行F2()函数
反汇编查看F1函数
void F1()
{push ebp mov ebp,esp sub esp,0D0h push ebx push esi push edi lea edi,[ebp-10h] mov ecx,4 mov eax,0CCCCCCCCh rep stos dword ptr es:[edi] mov eax,dword ptr [__security_cookie (02FA040h)] xor eax,ebp mov dword ptr [ebp-4],eax mov ecx,offset _2D923C74_FileName@c (02FC009h) call @__CheckForDebuggerJustMyCode@4 (02F1334h) nop int a = 0;mov dword ptr [a],0 printf("%p\n", &a);lea eax,[a] push eax push offset string "%p\n" (02F7B30h) call _printf (02F10DCh) add esp,8
}push edx mov ecx,ebp push eax lea edx,ds:[2F20F8h] call @_RTC_CheckStackVars@8 (02F11F4h) pop eax pop edx pop edi pop esi pop ebx mov ecx,dword ptr [ebp-4] xor ecx,ebp call @__security_check_cookie@4 (02F1159h) add esp,0D0h cmp ebp,esp call __RTC_CheckEsp (02F1258h) mov esp,ebp pop ebp ret
注意到最后两行: pop ebp和ret(返回),之后交还操作系统
VS再为F2申请栈帧空间,用的空间是原来为F1申请的地方(空间可以重复利用),也就有了下面的结果,a与b存储的地址相同
3.改编
修改代码后,F1的a和F2的b是否使用同一块栈帧空间?
#include <stdio.h>
void F2();
void F1()
{int a = 0;printf("%p\n", &a);F2();
}void F2()
{int b = 0;printf("%p", &b);
}int main()
{F1();
}
解:
显然改成了嵌套调用,在F1()的内部调用F2(),导致F1()函数在F2()执行完之前无法返回
反汇编分析
void F1()
{push ebp mov ebp,esp sub esp,0D0h push ebx push esi push edi lea edi,[ebp-10h] mov ecx,4 mov eax,0CCCCCCCCh rep stos dword ptr es:[edi] mov eax,dword ptr [__security_cookie (0F7A040h)] xor eax,ebp mov dword ptr [ebp-4],eax mov ecx,offset _2D923C74_FileName@c (0F7C009h) call @__CheckForDebuggerJustMyCode@4 (0F71334h) nop int a = 0;mov dword ptr [a],0 printf("%p\n", &a);lea eax,[a] push eax push offset string "%p\n" (0F77B30h) call _printf (0F710DCh) add esp,8 F2();call _F2 (0F71037h) nop
}push edx mov ecx,ebp push eax lea edx,ds:[0F747D0h] call @_RTC_CheckStackVars@8 (0F711F4h) pop eax pop edx pop edi pop esi pop ebx mov ecx,dword ptr [ebp-4] xor ecx,ebp call @__security_check_cookie@4 (0F71159h) add esp,0D0h cmp ebp,esp call __RTC_CheckEsp (0F71258h) mov esp,ebp pop ebp ret
尽管在F1函数的最后有ret返回指令,但是call _F2 (0F71037h)指令在前,导致先去执行F2()函数,待F2结束后再执行call后的指令
再看看F2函数的反汇编指令:
void F2()
{push ebp mov ebp,esp sub esp,0D0h push ebx push esi push edi lea edi,[ebp-10h] mov ecx,4 mov eax,0CCCCCCCCh rep stos dword ptr es:[edi] mov eax,dword ptr [__security_cookie (0F7A040h)] xor eax,ebp mov dword ptr [ebp-4],eax mov ecx,offset _2D923C74_FileName@c (0F7C009h) call @__CheckForDebuggerJustMyCode@4 (0F71334h) nop int b = 0;mov dword ptr [b],0 printf("%p", &b);lea eax,[b] push eax push offset string "%p" (0F77BD0h) call _printf (0F710DCh) add esp,8
}push edx mov ecx,ebp push eax lea edx,ds:[0F742A8h] call @_RTC_CheckStackVars@8 (0F711F4h) pop eax pop edx pop edi pop esi pop ebx mov ecx,dword ptr [ebp-4] xor ecx,ebp call @__security_check_cookie@4 (0F71159h) add esp,0D0h cmp ebp,esp call __RTC_CheckEsp (0F71258h) mov esp,ebp pop ebp ret
注意到最后的ret返回指令,其返回后,来到了call _F2 (0F71037h)下面的nop空指令
此后才能对F1()函数返回,也就有了下面的结果,a与b存储的地址不一样
内存图
如果画张内存图来表示main,F1,F2这三个函数