16、函数调用间,系统栈,汇编查看
#include <iostream>
using namespace std;
void f1(int a,int b)
{
}
int main()
{
f1(3,4);
system("pause");
}
将上述代码反汇编如下(vs05debug):
int main()
{
004113D0 push ebp ;/保存栈基址
004113D1 mov ebp,esp ;将栈顶指针赋给ebp,此时都指向函数调用的栈顶
004113D3 sub esp,0C0h ;栈顶指针下移0xc0h,为main函数内局部变量分配空间
004113D9 push ebx ;在栈中保存ebx
004113DA push esi
004113DB push edi
004113DC lea edi,[ebp-0C0h] ;将ebp-0c0h值赋给edi
004113E2 mov ecx,30h
004113E7 mov eax,0CCCCCCCCh
004113EC rep stos dword ptr es:[edi];从lea指令到这条指令作用就是把为局部变量分配的内存空间填充CC数据。Stos将eax中数据放入es:[edi]中,同时edi增加4个字节。Rep使指令重复执行ecx次数。
f1(3,4);
004113EE push 4
004113F0 push 3 ;参数入栈
004113F2 call f1 (4111D6h) ;call指令实际动作是push eip;mov eip,4111D6h连续两条指令。
004113F7 add esp,8 ;c的调用约定,调整栈
system("pause");
004113FA mov esi,esp
004113FC push offset string "pause" (415810h)
00411401 call dword ptr [__imp__system (4182B4h)]
00411407 add esp,4
0041140A cmp esi,esp
0041140C call @ILT+315(__RTC_CheckEsp) (411140h)
}
void f1(int a,int b) //f1函数反汇编代码。
{
004113A0 push ebp
004113A1 mov ebp,esp
004113A3 sub esp,0C0h
004113A9 push ebx
004113AA push esi
004113AB push edi
004113AC lea edi,[ebp-0C0h]
004113B2 mov ecx,30h
004113B7 mov eax,0CCCCCCCCh
004113BC rep stos dword ptr es:[edi] ;上述汇编指令每个函数都有主要用来初始化栈和保存一些寄存器的值。
}
004113BE pop edi
004113BF pop esi
004113C0 pop ebx ;函数调用完毕,回复edi等寄存器的值
004113C1 mov esp,ebp
004113C3 pop ebp ;回收为f1函数分配的栈帧。
004113C4 ret ;此指令实际动作是pop eip;call f1时push eip,那时eip的值是call函数下条指令的值,程序跳转main函数执行。
所以通过上述分析,我们得到一下的栈内数据的布局:
由于eip要存入栈中,所以就有可能被修改,导致程序异常的运行,很多病毒就是利用了这一点。
参考:
[1] http://blog.csdn.net/Demon__Hunter/archive/2008/11/30/3414219.aspx
[2] 天书夜读