stdcall调用约定分析,以c语言为分析对象
-、编译平台
本文中C代码的编译平台为VC6.0,编译版本为Debug版本。
二、研究的C代码
int __stdcall stdcallFun(int Num1 , int Num2)
{
if (Num1 > Num2)
return Num1 ;
else
return Num2 ;
}
void main(int argc, char *argv[])
{
int x1 , x2 , nBigger;
x1 = 5 ;
x2 = 6 ;
nBigger = stdcallFun(x1,x2) ;
return ;
}
三、利用VC调试时,查看Debug版本的汇编代码(如看不懂该部分代码可直接跳到第四点的分析部分)
stdcallFun函数
stdcallFun:
004010A0 push ebp ;保存ebp,
004010A1 mov ebp,esp
004010A3 sub esp,40h ; stdcallFun分配临时变量
004010A6 push ebx
004010A7 push esi
004010A8 push edi ;保护现场
004010A9 lea edi,[ebp-40h]
004010AC mov ecx,10h
004010B1 mov eax,0CCCCCCCCh
004010B6 rep stos dword ptr [edi] ;初始化临时变量
004010B8 mov eax,dword ptr [ebp+8] ;mov eax, Num1
004010BB cmp eax,dword ptr [ebp+0Ch] cpm Num1,Num2
004010BE jle stdcallFun+25h (004010c5)
004010C0 mov eax,dword ptr [ebp+8]
004010C3 jmp stdcallFun+28h (004010c8)
004010C5 mov eax,dword ptr [ebp+0Ch]
004010C8 pop edi
004010C9 pop esi
004010CA pop ebx
004010CB mov esp,ebp
004010CD pop ebp
004010CE ret 8
main函数
main:
004010EC mov ecx,13h
004010F1 mov eax,0CCCCCCCCh
004010F6 rep stos dword ptr [edi]
004010F8 mov dword ptr [ebp-4],5 ;x1 = 5
004010FF mov dword ptr [ebp-8],6 ;x2 = 6
00401106 mov eax,dword ptr [ebp-8]
00401109 push eax ;变量x2的值入栈
0040110A mov ecx,dword ptr [ebp-4]
0040110D push ecx ;变更x1的值入栈
0040110E call @ILT+25(_stdcallFun@8) (0040101e)
00401113 mov dword ptr [ebp-0Ch],eax
00401116 pop edi
00401117 pop esi
00401118 pop ebx
00401119 add esp,4Ch
0040111C cmp ebp,esp
0040111E call __chkesp (00401280) ;调试代码
00401123 mov esp,ebp
00401125 pop ebp
00401126 ret
四、阶段分析
1.理解stdcall参数传递规则的关键是C语言的变量内存分配规则。对于全局变量和static变量分配的内存空间为全局内存空间;对于函数的局部变量执行函数时分配,退出执行函数收回分配空间。变量的内存空间为栈(stack)。而程序执行中程序员动态审请的内存则位于全局的堆(heap)中。所以我们以下分析函数调用传递规则时,将结合栈的变化图分析。
2.阶段分析
a.main函数局部变量分配初始化
void main()
{
int x1 , x2 , nBigger;
程序执行到此处
对应汇编代码为:
00401060 push ebp
00401061 mov ebp,esp
00401063 sub esp,4Ch ;main函数分配局部变量在stack里边分配
00401066 push ebx
00401067 push esi
00401068 push edi ;保护现场
00401069 lea edi,[ebp-4Ch]
0040106C mov ecx,13h
00401071 mov eax,0CCCCCCCCh