栈的定义及规则:
栈从高位到底位顺序来排列数据, 栈顶<=栈底 && 栈顶>=0, 栈顶始终指向最顶端的数据.
Push(EAX) : TOP--, S[TOP]=EAX; //先减栈顶, 再进数据
Pop(EAX) : S[TOP]=EAX, TOP++; //先出数据,再加栈顶
初始值:
EBP=ESP=20 (EBP-ESP=0,0个数据)
假如有以下栈段(Step0)与函数ST():
STACK 0-19 //定义栈开始位置0,共20字节
0 0 0 0 0 0
1 1 1 1 1 1
2 2 2 2 2 2
3 3 3 3 3 3
4 4 4 4 4 4
5 5 5 5 5 5
6 6 6 6 6 6 z ->ESP
7 7 7 7 7 7 y
8 8 8 8 8 8 x
9 9 9 9 s[3] ->ESP 9 s[3] ->EBP=ESP 9 s[3] ->EBP
10 10 10 10 s[2] 10 s[2] 10 s[2]
11 11 11 11 s[1] 11 s[1] 11 s[1]
12 12 12 12 s[0] 12 s[0] 12 s[0]
13 13 13 r[3] ->ESP 13 r[3] 13 r[3] 13 r[3]
14 14 14 r[2] 14 r[2] 14 r[2] 14 r[2]
15 15 15 r[1] 15 r[1] 15 r[1] 15 r[1]
16 16 16 r[0] 16 r[0] 16 r[0] 16 r[0]
17 17 'a' ->ESP 17 'a' 17 'a' 17 'a' 17 'a'
18 18 'b' 18 'b' 18 'b' 18 'b' 18 'b'
19 19 'c' 19 'c' 19 'c' 19 'c' 19 'c'
Step0 step1 step2 step3 step4 step5
函数定义
function ST (char a, char b, char c)
{
char x=a;
char y=b;
char z=c;
char n=x+y+z;
}
下面让我们来详细描述一下函数调用时,栈是如何使用的:
1.将参数压栈:
push 'c' ESP-- ESP=19 S[19]='c'
push 'b' ESP-- ESP=18 S[18]='b'
push 'a' ESP-- ESP=17 S[17]='a'
执行后如图:step1
2.CALL 函数地址
将下一个指令的地址压栈,作为函数的返回地址char r[4](4字节的地址)
push(r[0]) ESP-- ESP=16 S[16]=r[0]
push(r[1]) ESP-- ESP=15 S[15]=r[1]
push(r[2]) ESP-- ESP=14 S[14]=r[2]
push(r[3]) ESP-- ESP=13 S[13]=r[3]
注意:整形数据压栈的字节顺序是从低字节到高字节压栈,形成的顺序跟内存中的整数顺序相反
执行后如图:step2
3.函数体--保护栈底(EBP)
char s[4]=EBP;
push(s[0]) ESP-- ESP=12 S[12]=s[0]
push(s[1]) ESP-- ESP=11 S[11]=s[1]
push(s[2]) ESP-- ESP=10 S[10]=s[2]
push(s[3]) ESP-- ESP=9 S[9] =s[3]
执行后如图:step3
4.函数体--设置栈底&&保存栈顶(ESP)
MOV EBP,ESP //EBP=ESP
执行后如图:step4
5.函数体--声明局部变量
SUB ESP,0x03 //定义3个局部变量 x,y,z 总长度3个字节,ESP=ESP-3=5
执行后如图:step5
6.函数体--使用参数与局部变量
//参数
a=[EBP+8];
b=[EBP+9];
c=[EBP+10];
//局部变量
x=[EBP-1];
y=[EBP-2];
z=[EBP-3];
7.函数体--恢复栈顶(ESP)
MOV ESP,EBP
8.函数体--恢复栈底(EBP)
POP EBP
9.返回调用函数
RET,相当于下面指令
POP EDX // EDX=r[4] 让EDX等于返回地址
JMP EDX // 跳转到EDX
此时,EBP=ESP=20,又恢复到函数调用前的栈状态
阅读(1645) | 评论(0) | 转发(0) |