1: // test.cpp : Defines the entry point for the console application.
2: //
3:
4: #include "stdafx.h"
5:
6: struct complex{
7: int i; //x
8: int j; //y
9: int k; //z
10: int t; //time. w=-ict
11: };
12:
13: complex test( int a, int b, int c , int d);
14:
15: int main(int argc, char* argv[])
16: {
0040D440 push ebp
0040D441 mov ebp,esp
0040D443 sub esp,80h
0040D449 push ebx
0040D44A push esi
0040D44B push edi
0040D44C lea edi,[ebp-80h]
0040D44F mov ecx,20h
0040D454 mov eax,0CCCCCCCCh
0040D459 rep stos dword ptr [edi]
17: int a, b, c, d;
18: struct complex z;
19:
20: a = 0x11;
0040D45B mov dword ptr [ebp-4],11h
21: b = 0x30;
0040D462 mov dword ptr [ebp-8],30h
22: c = 0x88;
0040D469 mov dword ptr [ebp-0Ch],88h
23: d = 0x9302;
0040D470 mov dword ptr [ebp-10h],9302h
24:
25: z = test( a, b, c, d);
0040D477 mov eax,dword ptr [ebp-10h]
0040D47A push eax
0040D47B mov ecx,dword ptr [ebp-0Ch]
0040D47E push ecx
0040D47F mov edx,dword ptr [ebp-8]
0040D482 push edx
0040D483 mov eax,dword ptr [ebp-4]
0040D486 push eax
0040D487 lea ecx,[ebp-40h]
0040D48A push ecx
0040D48B call @ILT+15(test) (00401014)
0040D490 add esp,14h
0040D493 mov edx,dword ptr [eax]
0040D495 mov dword ptr [ebp-30h],edx
0040D498 mov ecx,dword ptr [eax+4]
0040D49B mov dword ptr [ebp-2Ch],ecx
0040D49E mov edx,dword ptr [eax+8]
0040D4A1 mov dword ptr [ebp-28h],edx
0040D4A4 mov eax,dword ptr [eax+0Ch]
0040D4A7 mov dword ptr [ebp-24h],eax
0040D4AA mov ecx,dword ptr [ebp-30h]
0040D4AD mov dword ptr [ebp-20h],ecx
0040D4B0 mov edx,dword ptr [ebp-2Ch]
0040D4B3 mov dword ptr [ebp-1Ch],edx
0040D4B6 mov eax,dword ptr [ebp-28h]
0040D4B9 mov dword ptr [ebp-18h],eax
0040D4BC mov ecx,dword ptr [ebp-24h]
0040D4BF mov dword ptr [ebp-14h],ecx
26:
27: return 0;
0040D4C2 xor eax,eax
28: }
0040D4C4 pop edi
0040D4C5 pop esi
0040D4C6 pop ebx
0040D4C7 add esp,80h
0040D4CD cmp ebp,esp
0040D4CF call __chkesp (0040d400)
0040D4D4 mov esp,ebp
0040D4D6 pop ebp
0040D4D7 ret
29:
30:
31: struct complex test( int a, int b, int c, int d)
32: {
0040D4E0 push ebp
0040D4E1 mov ebp,esp
0040D4E3 sub esp,50h
0040D4E6 push ebx
0040D4E7 push esi
0040D4E8 push edi
0040D4E9 lea edi,[ebp-50h]
0040D4EC mov ecx,14h
0040D4F1 mov eax,0CCCCCCCCh
0040D4F6 rep stos dword ptr [edi]
33: struct complex z;
34: z.i = a;
0040D4F8 mov eax,dword ptr [ebp+0Ch]
0040D4FB mov dword ptr [ebp-10h],eax
35: z.j = b;
0040D4FE mov ecx,dword ptr [ebp+10h]
0040D501 mov dword ptr [ebp-0Ch],ecx
36: z.k = c;
0040D504 mov edx,dword ptr [ebp+14h]
0040D507 mov dword ptr [ebp-8],edx
37: z.t = d;
0040D50A mov eax,dword ptr [ebp+18h]
0040D50D mov dword ptr [ebp-4],eax
38:
39: return z;
0040D510 mov ecx,dword ptr [ebp+8]
0040D513 mov edx,dword ptr [ebp-10h]
0040D516 mov dword ptr [ecx],edx
0040D518 mov eax,dword ptr [ebp-0Ch]
0040D51B mov dword ptr [ecx+4],eax
0040D51E mov edx,dword ptr [ebp-8]
0040D521 mov dword ptr [ecx+8],edx
0040D524 mov eax,dword ptr [ebp-4]
0040D527 mov dword ptr [ecx+0Ch],eax
0040D52A mov eax,dword ptr [ebp+8]
40: }
0040D52D pop edi
0040D52E pop esi
0040D52F pop ebx
0040D530 mov esp,ebp
0040D532 pop ebp
0040D533 ret
哈哈,终于没办法优化了。现在的方法就是把常规的4个32bits的返回值放到stack里面去,指向这个返回值的地址放到EAX返回。
额外的话题,几种函数调用的方式:
★__cdecl
cb的默认值,它会在输出函数名前加_,并保留此函数名不变,参数按照从右到左的顺序依次传递给栈,也可以写成_cdecl和cdecl形式。和_stdcall最大的不同是,返回的时候栈由调用者(Caller)而非被调用子程序(Callee)来调整。这样的好处是支持变长度参数列表,因为Callee并不方便知道变量的长度。
★__fastcall
她修饰的函数的参数将尽可能地使用寄存器来处理,其函数名前加@,参数按照从左到右的顺序压栈;
★__pascal
它说明的函数名使用Pascal格式的命名约定。这时函数名全部大写。参数按照从左到右的顺序压栈;
★__stdcall
使用标准约定的函数名。函数名不会改变。使用__stdcall修饰时。参数按照由右到左的顺序压栈,也可以是_stdcall;
对于_cdecl, 函数调用的堆栈变化的解说:
1.将函数参数入栈,第一个参数在栈顶,最后一个参数在栈底。
2.将CALL指令下一行代码的地址入栈。
2.跳转--eip的值被重新设置为被调函数的起始地址。
3.进入函数代码空间后,将基址指针EBP入栈,然后让基址指针EBP指向当前堆栈栈顶,并使用它访问存在堆栈中的函数输入参数及堆栈中的其他数据。
4.sub esp,N
堆栈指针ESP减少一个值,如44H,向上移动一个距离,留出一个空间给该函数作为临时存储区。也就是说,子函数的变量都是存储在栈中。这里N是函数内局部变量的总字节数加上一个整数,一般为40。此后esp即为被调函数的堆栈指针了。
5.初始化esp ~ esp-N之间的N字节空间
这是对堆栈中已分配给局部变量使用的内存空间的初始化,一般全部设置为0xcc。
6. 顺序执行函数内语句。
此时函数的堆栈位于所有局部变量的内存空间之后,二者之间一般有40字节的隔离带。
{
// 以上准备工作做好后,函数正式被执行,如下所示。
1.将其他指针或寄存器中的值入栈,以便在函数中使用这些寄存器。
2.执行代码。
3.执行return()返回执行结果,将要返回的值存入EAX中或栈中。
4.步骤6.1中的指针出栈。
}
7.将EBP的值传给堆栈指针ESP,使ESP复原为2.c之前的值。此时进入函数时EBP的值在栈顶。
8.基址指针EBP出栈,复原为2.b之前的EBP的值。
9.执行RET指令,“调用函数”的地址出栈,本函数返回到CALL指令的下一行。
3.函数返回到CALL指令下一行,将堆栈指针加一个数值,以使堆栈指针恢复到以上步骤1执行之前的值。该数值是上面第一步入栈参数的总长度。
最后的那个check ESP的函数我也打印出来,其实就是检查堆栈调整后EBP是否和ESP相等。如果不等,调用错误处理代码。
__chkesp:
0040D400 jne __chkesp+3 (0040d403)
0040D402 ret
0040D403 push ebp
0040D404 mov ebp,esp
0040D406 sub esp,0
0040D409 push eax
0040D40A push edx
0040D40B push ebx
0040D40C push esi
0040D40D push edi
0040D40E push offset string "The value of ESP was not properl"... (00422e90)
0040D413 push offset string "" (00422c50)
0040D418 push 2Ah
0040D41A push offset string "i386//chkesp.c" (00422e80)
0040D41F push 1
0040D421 call _CrtDbgReport (00408b10)
0040D426 add esp,14h
0040D429 cmp eax,1
0040D42C jne __chkesp+2Fh (0040d42f)
0040D42E int 3
0040D42F pop edi
0040D430 pop esi
0040D431 pop ebx
0040D432 pop edx
0040D433 pop eax
0040D434 mov esp,ebp
0040D436 pop ebp
0040D437 ret
0040D438 int 3