0day2安全——笔记3

第二章

函数调用约定

不同的操作系统,语言和编译器调用函数的原理差不多,但是具体的调用约定有差异。

C语言VC++编译的函数传参顺序如下图所示(默认使用__stdcall调用约定)

函数调用步骤(__stdcall约定)
1. 参数入栈:将参数从右向左依次压入系统栈中
2. 返回地址入栈:将当期代码区调用指令的下一条指令地址压入栈中,供函数返回时继续执行
3. 代码区跳转:处理器从当前代码区跳转到被调用函数的入口处
4. 栈帧调整:

  • 保存当前栈帧状态值:已被后面回复本栈帧使用(EBP入栈)
  • 将当前栈帧切换到新栈帧(将ESP值装入EBP,更新栈帧底部)
  • 给新栈帧分配空间(把ESP减去所需要空间的大小,抬高栈帧)

ASM

push 参数3      ;从右到左依次传参
push 参数2
push 参数1
call 函数地址    ;1.向栈中压入当前指令在内存中的位置,即保存返回地址
                ;2.跳转到所调用函数的入口地址
push ebp        ;保存旧栈帧的底部
mov ebp,esp        ;设置新栈帧的底部(栈帧切换)
sub esp,xxx        ;设置新栈帧的顶部(抬高栈帧,为新栈帧开辟空间)

 函数调用时系统栈发生的变化

函数返回步骤(__stdcall约定)

1. 保存返回值:通常将函数的返回值保存在寄存器EAX(累加寄存器)中
2. 弹出当前栈帧,恢复上一个栈帧

  • 在堆栈平衡的基础上,给ESP加上栈帧的大小,降低栈顶,回收当前栈帧的空间
  • 将当期栈帧底部保存的前栈帧ESP值弹如EBP寄存器,恢复出上一个栈帧
  • 将函数返回地址弹给EIP(指令寄存器)

ASM

add esp,xxx ;降低栈顶,回收当前栈帧
pop ebp     ;将上一个栈帧底部位置恢复到ebp
retn        ;1.弹出当前栈帧元素,即弹出栈帧中的返回地址,至此栈帧的恢复工作完成
            ;2.让处理器跳转到弹出的返回地址,恢复到调用前的代码区
原文地址:https://www.cnblogs.com/luocodes/p/11882576.html