时间:2022-10-23 11:16:23 | 栏目:C代码 | 点击:次
堆栈帧(stack frame)是一块堆栈保留区域,用于存放被传递的实际参数,子程序的返回值、局部变量以及被保存的寄存器。
(1)被传递的实际参数。如果有,则压入堆栈;
(2)当子程序被调用时,使该子程序的返回值压入堆栈。如果使用寄存器保存返回值,则跳过此步;
(3)子过程返回地址入栈;
(4)子程序开始执行时,EBP被压入堆栈;
(5)设置EBP等于ESP。从现在开始,EBP就变成了该子程序所有参数的引用基址;
(6)如果有局部变量,修改ESP以便在堆栈中为这些变量预留空间;
(7)如果需要保存寄存器,则将它们入栈;
我们来看一段code👇
#include<stdio.h> int print_string(const char * str) { /* EBP被压入堆栈,对应上述步骤(4) */ //00FE18A0 push ebp /* 设置EBP等于ESP,对应上述步骤(5) */ //00FE18A1 mov ebp,esp /* 虽然我们没有局部变量,但是编译器还是预留了空间,对应上述步骤(6) */ //00FE18A3 sub esp,0C0h /* 保存寄存器,对应上述步骤(7) */ //00FE18A9 push ebx //00FE18AA push esi //00FE18AB push edi printf("%s\n", str); /* 将printf函数的入参压入堆栈 */ //00FE18C1 mov eax,dword ptr [str] //00FE18C4 push eax //00FE18C5 push offset string "%s\n" (0FE7B30h) //00FE18CA call _printf (0FE10CDh) //00FE18CF add esp,8 return 1; /* 使用寄存器存储返回值 */ //00FE18D2 mov eax,1 } int main() { char* str = "Hello World"; /* 将"Hello World"的地址存在str变量中 */ //00FE1865 mov dword ptr [str],offset string "Hello World" (0FE7B34h) print_string(str); /* 将str 中的值存在eax寄存器中 */ //00FE186C mov eax,dword ptr [str] /* eax压栈,对应上述步骤(1) */ //00FE186F push eax /* 这里我们使用寄存器存储返回值,所以没有步骤(2) */ /* call指令会自动将返回地址压栈,对应上述步骤(3) */ //00FE1870 call _print_string (0FE13B1h) /* 清空 print_string函数的入参空间 */ //00FE1875 add esp,4 return 0; //00FE1878 xor eax,eax }
我们再通过一张图来解释一下:
//我们的汇编code如下 print_string PROC push ebp // 保存基址寄存器 mov ebp,esp // 堆栈帧基址 push ecx push edx // 保存寄存器 mov eac,[ebx+8] // 取堆栈参数 . . pop edx // 恢复被保存的寄存器 pop ecx pop ebp // 恢复基址指针 ret // 清除堆栈 print_string ENDP
函数print_string() 对应的堆栈帧如下图👇