Assembly call和ret指令

时间:2022-05-09 00:40:01

很多人不知道call和ret的具体动作,只知道call的时候会跳转到被调用函数的地址继续执行指令,ret会直接跳转到返回地址,至于寄存器和栈上的变化不是很了解。

0x00 定义

  • CALL pushes the return address onto the stack and transfers control to a procedure.
  • RET pops the return address off the stack and returns control to that location.

0x01 解释

call指令隐含操作push EIP,ret指令隐含操作 pop EIP,两条指令完全对应起来.

EIP寄存器用于保存下一个要执行的命令地址。在函数返回的过程最后执行ret,就是把之前入栈的返回地址出栈,并放入到EIP寄存器。这样指令就不会顺序继续执行了,而是跳了一下,跳到EIP指向的地方。

相应的,调用call指令的时候,会把下一个要执行的命令地址入栈。

0x02 汇编代码

函数调用

 30: print_out(0, 2);
013D155E push 2 //第二个实参压栈
013D1560 push 0 //第一个实参压栈
013D1562 call print_out (13D10FAh)//返回地址压栈,本例中是013D1567,然后调用print_out函数
013D1567 add esp,8  //两个实参出栈
//注意在call命令中,隐含操作是把下一条指令的地址压栈,也就是所谓的返回地址

函数返回

013D1431 mov esp,ebp //ebp的值传给esp,也就是恢复调用前esp的值
013D1433 pop ebp //弹出ebp,恢复ebp的值
013D1434 ret  //把返回地址写入EIP中,相当于pop EIP

0x03 堆栈变化

函数调用
Assembly call和ret指令

函数调用的过程是EBP和ESP分别向栈顶移动的过程,注意EBP是实参和返回地址入栈以后相对于ESP先入栈的。

函数返回
Assembly call和ret指令

明显能看到在ret的时候,esp自动减了一个栈指针的大小(不知道这样描述是否准确,不是一个栈帧的大小,因为函数执行的指令集合是一个栈帧),返回地址出栈,进入EIP寄存器(这里没有画出来)。

Assembly call和ret指令

0x04 参考文献

函数调用–函数栈