目录
一. 预备知识
1.1 常见的寄存器
寄存器名称 | 功能 |
---|---|
esp | 栈顶指针 |
ebp | 栈底指针 |
eax | 累加寄存器 |
ebx | 基地址寄存器,在内存中寻址并存放基地址 |
ecx | 计数寄存器 |
edx | 多功能寄存器(多用于存放整数除法产生的余数) |
esi | 源索引寄存器 |
edi | 目标索引寄存器 |
eip | 存放下一条指令的CPU地址 |
1.2 相关的汇编命令
汇编命令 | 功能 |
---|---|
push | 压栈,从栈顶增加元素 |
move | 将一个数据从原地址传送至操作地址,原地址内容不变 |
pop | 出栈,从栈顶删除元素 |
call | 将本条指令的下一条指令压入栈中,并跳转到子函数 |
sub | 从寄存器中减去数值,并将结果保存在寄存器中 |
add | 从寄存器中加上数值,然后将结果保存在寄存器中 |
lea | 将地址赋给目标寄存器 |
rep | 重复指令 |
1.3 内存区域的划分
在计算机中,内存划分为三个区域:栈区、堆区、静态区。其中,栈区用于存放临时变量及函数的形参,堆区用于动态分配内存,静态区用于存放全局变量、static修饰的均布变量等。
二. 函数栈帧的创建
以下面的代码为例,说明函数栈帧创建的过程
#include<stdio.h>
int Add(int x, int y)
{
int z = 0;
z = x + y;
return z;
}
int main()
{
int a = 10;
int b = 20;
int c = 0;
c = Add(a, b);
printf("%d\n", c);
return 0;
}
2.1 栈帧创建及压栈的过程
int main()
{
00DD19E0 push ebp //压栈,将ebp指向的地址存放
00DD19E1 mov ebp,esp //将栈顶指针指向的内容移动至栈底指针ebp,ebp指向esp
00DD19E3 sub esp,0E4h //将esp的地址向低位移动4位
00DD19E9 push ebx //压栈(基地址寄存器)
00DD19EA push esi //压栈(源索引寄存器)
00DD19EB push edi //压栈(目标索引寄存器)
00DD19EC lea edi,[ebp-24h] //将[ebp-24h]的地址赋给edi
00DD19EF mov ecx,9
00DD19F4 mov eax,0CCCCCCCCh
00DD19F9 rep stos dword ptr es:[edi] //将以edi为起点的9个地址的内容均赋为0CCCCCCCCh
00DD19FB mov ecx,offset _65FB160D_test@c (0DDC003h)
00DD1A00 call @__CheckForDebuggerJustMyCode@4 (0DD1316h)
int a = 10;
00DD1A05 mov dword ptr [a],0Ah //将a赋值为10
int b = 20;
00DD1A0C mov dword ptr [b],14h //将b赋值为20
int c = 0;
00DD1A13 mov dword ptr [c],0 //将c赋值为0
c = Add(a, b);
00DD1A1A mov eax,dword ptr [b] //将b的值移动到eax寄存器中
00DD1A1D push eax //将b的形参压入栈中
00DD1A1E mov ecx,dword ptr [a] //将a的值移动到ecx寄存器中
00DD1A21 push ecx //将a的形参压入栈中
00DD1A22 call _Add (0DD13BBh) //为Add函数开辟栈帧,跳转到Add函数
00DD1A27 add esp,8 //返回主函数,找回栈顶地址,Add销毁,esp向高地址移动8位
00DD1A2A mov dword ptr [c],eax //将寄存器eax的值(Add函数返回值)赋给c
printf("%d\n", c);
00DD1A2D mov eax,dword ptr [c] //将c的值赋给exa寄存器
00DD1A30 push eax //将exa压入栈中
00DD1A31 push offset string "%d\n" (0DD7CF8h)
00DD1A36 call _print2 (0DD10CDh)
00DD1A3B add esp,8 //栈顶指针向高地址移动8位
return 0;
00DD1A3E xor eax,eax
}
对于main函数,首先要在栈中为其本身开辟一块空间,因为要在main函数中调用add函数,在main的开辟栈帧后,还要将先后将b的形参和a的形参压入栈中。
对于Add函数,将a和b的值放入到相应的寄存器,并压入栈中,将整个Add函数的返回结果放入eax中,Add函数销毁,对应的栈帧也销毁。
三. main函数栈帧的销毁
00DD1A40 pop edi //edi出栈
00DD1A41 pop esi //esi出栈
00DD1A42 pop ebx //ebx出栈
00DD1A43 add esp,0E4h //将栈顶指针向高地址移动OE4h位
00DD1A49 cmp ebp,esp
00DD1A4B call __RTC_CheckEsp (0DD123Fh)
00DD1A50 mov esp,ebp //ebp的地址指向esp
00DD1A52 pop ebp //栈底指针ebp出栈
00DD1A53 ret