函数调用原理–完善篇(栈帧)

1、参考链接:
1.1 函数调用原理–参数篇
1.2 函数调用原理–局部变量

2、局部变量空间分配后没有初始化,可能会出现异常访问而出现不可预知的错误,因为分配的临时空间必须初始化;
2.1、将每个字节都初始化为CC其中一种安全策略,因为CC在汇编指令中为int 3,即中断指令。
2.2、stosb/stosw指定能将es:di指向的空间写入ax寄存器中的值,并在执行结束后将di自加1字节/2字节
2.3、rep会循环指定后一条指令,循环次数从cx寄存器中取,如果为0则退出循环

3、汇编代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
assume cs:code, ds:data, ss:stack
 
stack segment
    db 20h dup(1)
stack ends
 
data segment
    db 20h dup(2)
data ends
 
code segment
start:
    ;初始化
    mov ax, stack
    mov ss, ax
    mov sp, 20h
    mov ax, data
    mov ds, ax  
    ;----业务代码----start
    ;(函数局部变量实现) 
    push 3344h
    push 4455h
    call sum;计算结果保存在ax中
    add sp, 4 
    ;----业务代码----end
    ;退出             
    mov ah, 4ch
    int 21h
sum:
    ;1、保护寄存器
    push bp;保护bp
    mov bp, sp;保护sp
    sub sp, 10h;空出10个字节给局部变量使用
    ;保护其他寄存器
    push bx
    push si
    push di 
 
    ;2、局部变量空间安全初始化
    ;stosw:将ax的值拷贝到es:di中,同时di自+2(stosb自+1)
    ;2.1、初始化内容
    mov ax, 0cccch;使用cccc来初始化,因为cc对应的汇编指令为int 3,即中断指令
    ;2.2、设置es值
    ;将ss的值赋给es
    mov bx, ss
    mov es, bx
    ;2.3、设置di值 
    ;将di的值设置为bp-10h,回到局部变量空间的最小端(因为di会自加)
    mov di, bp
    sub di, 10h
    ;2.4、循环赋值初始化所有局部变量空间
    ;rep 循环执行,cx为循环次数,循环一次自-1
    mov cx, 10h;循环次数为局部变量空间的大小(字操作则除2)
    rep stosb
 
    ;----业务代码------start   
    ;取参数并计算,由于push bp占用了2个字节,参数从bp+4开始
    mov ax, [bp+6]
    add ax, [bp+4]
    mov [bp-2], 2324h;局部变量1
    mov [bp-4], 3435h;局部变量2  
    ;----业务代码-------end
 
    ;3、恢复函数调用前各数值,释放空间
    ;恢复受保护的寄存器
    pop di
    pop si
    pop bx
 
    mov sp, bp;恢复sp
    pop bp;恢复bp
    ret 
code ends
end start

4、C++源代码及反汇编结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// VCTest.cpp : Defines the entry point for the console application.
//
 
#include "stdafx.h"
 
int sum(int a, int b)
{
	int c = 20;
	int d = 30;
	return a + b;
}
int main(int argc, char* argv[])
{
	sum(2, 3);
	return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
6:    int sum(int a, int b)
7:    {
0040D7E0   push        ebp;保护bp
0040D7E1   mov         ebp,esp;保护bp
0040D7E3   sub         esp,48h;分配局部变量临时空间
0040D7E6   push        ebx;保护bx
0040D7E7   push        esi;保护si
0040D7E8   push        edi;保护di
;循环初始化将局变量为CCCC
0040D7E9   lea         edi,[ebp-48h]
0040D7EC   mov         ecx,12h
0040D7F1   mov         eax,0CCCCCCCCh
0040D7F6   rep stos    dword ptr [edi]
8:        int c = 20;
0040D7F8   mov         dword ptr [ebp-4],14h
9:        int d = 30;
0040D7FF   mov         dword ptr [ebp-8],1Eh
10:       return a + b;
0040D806   mov         eax,dword ptr [ebp+8]
0040D809   add         eax,dword ptr [ebp+0Ch];函数返回值保存在ax中
11:   }
;恢复受保护的寄存器,与保护时的顺序相反(因为栈)
0040D80C   pop         edi;恢复di
0040D80D   pop         esi;恢复si
0040D80E   pop         ebx;恢复bx
0040D80F   mov         esp,ebp;恢复sp
0040D811   pop         ebp;恢复bp
0040D812   ret;返回

Leave a Reply