#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<Windows.h>
int add(int x,int y)
{
int z = 0;
z = x + y;
return z;
}
int main()
{
int a = 10;
int b = 20;
int ret=add(a,b);
printf("ret=%d",ret );
system("pause");
return 0;
}
int main()
{
001F1470 push ebp
001F1471 mov ebp,esp
001F1473 sub esp,0E4h
001F1479 push ebx
001F147A push esi
001F147B push edi
001F147C lea edi,[ebp-0E4h]
001F1482 mov ecx,39h
001F1487 mov eax,0CCCCCCCCh
001F148C rep stos dword ptr es:[edi]
int a = 10;
001F148E mov dword ptr [a],0Ah
int b = 20;
001F1495 mov dword ptr [b],14h
int ret=add(a,b);
001F149C mov eax,dword ptr [b]
001F149F push eax
int ret=add(a,b);
001F14A0 mov ecx,dword ptr [a]
001F14A3 push ecx
001F14A4 call _add (01F11E5h)
001F14A9 add esp,8
001F14AC mov dword ptr [ret],eax
esp:esp寄存器里存储的是在调用函数之后,栈的栈顶。并且始终指向栈顶。
ebp:ebp寄存器里存储的是是栈的栈底指针,通常叫栈基址,这个是一开始进行函数调用之前,由esp传递给ebp的。(在函数调用前你可以这么理解:esp存储的是栈顶地址,也是栈底地址。)
push:压入操作,把一个32位的操作数压入堆栈中,这个操作在32位机中会使得esp被减4(字节),esp通常是指向栈顶的,这里顶部是地址小的区域,那么,压入堆栈的数据越多,esp也就越来越小
mov:数据传送。第一个参数是目的操作数,第二个参数是源操作数,就是把源操作数拷贝到目的一份。
lea:取得第二个参数地址后放入到前面的寄存器(第一个参数)中。
rep stos:rep指令的目的是重复其上面的指令.ECX的值是重复的次数.
STOS指令的作用是将eax中的值拷贝到ES:EDI指向的地址
mov ebp,esp //将esp赋予ebp ,ebp和esp暂时指向同一位置。
sub esp,0E4h //将esp减去0E4h,0E4h是16进制数,由于开辟的栈空间是由高到低,所以esp往上走0E4h.
push ebx
push esi
push edi //这里的 ebx,esi ,edi,为三个寄存器,分别压栈。此时为main开辟的空间不够了,就再往上加三块。
leaedi,[ebp-0E4h] //看上图,将ebx的地址传到edi中
mov ecx,39h//将39h数值传给ecx寄存器中
mov eax,0CCCCCCCCh//将0CCCCCCCCh传给eax寄存器中
rep stos dword ptr es:[edi]//从edi中的地址(相当于ebx)向下拷贝eax中内容,ecx次
给a,b赋值
int ret=add(a,b);
001F149C mov eax,dword ptr [b]
001F149F push eax
int ret=add(a,b);
001F14A0 mov ecx,dword ptr [a]
001F14A3 push ecx
001F14A4 call _add (01F11E5h)
001F14A9 add esp,8
001F14AC mov dword ptr [ret],eax
printf("ret=%d",ret );
001F14AF mov esi,esp
001F14B1 mov eax,dword ptr [ret]
001F14B4 push eax
001F14B5 push 1F5858h
001F14BA call dword ptr ds:[1F9118h]
001F14C0 add esp,8
001F14C3 cmp esi,esp
001F14C5 call __RTC_CheckEsp (01F1140h)
将b变量的值赋给eax,并将eax压栈,将a变量值赋给ecx,并压栈,
call指令下一条指令的地址,并压栈(函数调用完用它返回)
int add(int x,int y)
{
001F13D0 push ebp
001F13D1 mov ebp,esp
001F13D3 sub esp,0CCh
001F13D9 push ebx
001F13DA push esi
001F13DB push edi
001F13DC lea edi,[ebp-0CCh]
001F13E2 mov ecx,33h
001F13E7 mov eax,0CCCCCCCCh
001F13EC rep stos dword ptr es:[edi]
int z = 0;
001F13EE mov dword ptr [z],0
z = x + y;
001F13F5 mov eax,dword ptr [x]
001F13F8 add eax,dword ptr [y]
001F13FB mov dword ptr [z],eax
return z;
001F13FE mov eax,dword ptr [z]
}
执行前10条指令(和开辟main函数空间类似)
push ebp//ebp压栈,此时ebp为main函数栈帧中的ebp
mov ebp,esp //将esp传给ebp
sub esp,0cch//给Add函数预开辟一块空间
2.给变量z初始化并传值。
mov dword ptr [epb-8],0 //给ebp-8处给sum初始化0,为什么是ebp-8不是ebp-4呢,这是因为VS2010在Debug模式下,int变量占用12个字节。可以这样认为,Debug模式下,在int变量的附近增加了8个字节,用于存储调试信息。当我们把模式设为Release,就会发现栈上连续定义的int变量,地址相差4个字节。
mov eax,dword ptr [epb+8]//把eax+8处的值(看上图ebp下两格处a),传给eax
add eax,dword ptr [epb+0ch]//把eax加上 ebp+0ch处的值(看上图ebp下三格处b),传给eax
mov dword ptr [epb-8],eax //把eax的值传给z
给z传值完成
此时 return z,并且
mov eax,dword ptr [epb-8]//将z的值存在寄存器eax中
接下来出栈
前三行指令,分别将寄存器pop出栈,
mov esp,ebp//将esp栈顶下移
pop ebp//将ebp出栈,此时下面还有一ebp,相当于下移。
ret//回到call指令的下一条指令。相当于将call指令pop出栈。
此时转跳到了esp指向处(call指令下条指令)
add esp,8//esp向下跳两格
mov dword ptr [ebp-20h],eax//将eax,也就是之前的值30传给ebp-20h处,看之前的图,ret就是ebp-20处。所以就将z的值传给了ret