Call和Retf指令

Reverse

X86汇编系列到此应该结束🍻

指令简介

ret 和 retf 指令

  • ret 指令使用栈中的数据修改 ip 的内容,从而实现近转移
  • retf 指令使用栈中的数据修改 cs 和 ip 的内容,从而实现远转移。

CPU 执行 ret 指令时所进行的操作:

  1. (ip)=((ss)*16+(sp))
  2. (sp)=(sp)+2

CPU 执行 retf 指令所进行的操作:

  1. (ip)=((ss)*16+(sp))
  2. (sp)=(sp)+2
  3. (cs)=((ss)*16+(sp))
  4. (sp)=(sp)+2

以汇编语言去解释 ret 指令

pop ip

以汇编语言去解释 retf 指令

pop ip

pop cs

举个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
assume cs:code

stack segment
db 16 dup(0)
stack ends

code segment
mov ax,4c00h
int 21h

start:mov ax,stack
mov ss,ax
mov sp,16 //将 sp 指向栈底-1的位置
mov ax,0
push ax
mov bx,0
ret
code ends

end start

执行完 ret 指令之后,(ip)=0,cs:ip 指向代码段第一条命令。

Call 指令

CPU 执行 call 指令时所执行的操作:

  1. 将当前的 ip 或 cs 和 ip 压入栈中
  2. 转移

call 指令不实现短转移,用汇编语言解释就是 jmp short 8位位移,因为短转移的转移位移是8位位移。

位移转移的 call 指令

格式: call 标号(将当前的 ip 压栈后,转移到标号处执行指令)

CPU 执行这种格式的 call 指令时的操作:

  1. (sp)=(sp)-2
  2. ((ss)*16+(sp))=(ip)
  3. (ip)=(ip)+16位位移

用汇编语言解释就是:

push ip

jmp near ptr 标号

目的地址转移的 call 指令

格式: call far ptr 标号(将当前的 cs 和 ip 压入栈后,转移到标号处执行操作)

CPU执行这种格式的 call 指令时的操作:

  1. (sp)=(sp)-2

    ((ss)*16+(sp))=(cs)

  2. (sp)=(sp)-2

    ((ss)*16+(sp))=(ip)

  3. (cs)=标号所在的段地址

    (ip)=标号中的偏移地址

用汇编语言解释就是:

push cs

push ip

jmp far ptr 标号

转移地址位于寄存器中的 call 指令

格式: call 16位寄存器(将ip 压入栈后,转移到 cs:16位寄存器处 执行)

CPU执行这种格式的 call 指令时的操作:

  1. (sp)=(sp)-2
  2. ((ss)*16+(sp))=(ip)
  3. (ip)=(16位寄存器)

用汇编语言解释就是:

push ip

jmp 16位reg

转移地址位于内存中的 call 指令

格式:

  • call word ptr 内存单元地址
  • call dword ptr 内存单元地址

和前面的都类似,word 涉及到的内存单元大小是1个字,只改变 ip 的值;dword 涉及到的内存单元大小是2个字,同时改变 cs 和 ip 的值。

用汇编语言解释“call word ptr 内存单元地址” 指令

push ip

jmp word ptr 内存单元地址

用汇编语言解释“call dword ptr 内存单元地址” 指令

push cs

push ip

jmp dword ptr 内存单元地址

mul 指令

乘法指令,要求如下:

  • 两个相乘的数要么为8位,要么为16位。
    • 8位:一个默认放在 al 中,另一个放在8位 reg 或内存字节单元中
    • 16位:一个默认放在 ax 中,另一个放在16位 reg 或内存单元字节中
  • 相乘后的结果
    • 8位:结果默认放在 ax 中
    • 16位:默认高位放在 dx 中,低位放在 ax 中

格式如下:

  • mul reg
  • mul 内存单元

image-20220527111056621

image-20220527111105425

image-20220527111119085

call 和 ret 的配合使用

image-20220527111503765

批量参数的传递

之前的参数我们都是使用寄存器来存放,当我们的参数变多,寄存器不够用时我们就需要将批量的数据存放在内存中,然后将内存空间的首地址放在寄存器中,然后再将数据传输给需要的子程序。对于有批量数据的返回结果我们也是使用相同的方法。

本文作者:GhDemi

本文链接: https://ghdemi.github.io/2022/06/09/Call%E5%92%8CRetf%E6%8C%87%E4%BB%A4/

文章默认使用 CC BY-NC-SA 4.0 协议进行许可,使用时请注意遵守协议。