RET 与 RETF 指令详解

360影视 动漫周边 2025-09-06 19:32 2

摘要:在 x86 汇编语言中,"RET" 和 "RETF" 是用于 从子程序(函数/过程)返回 的指令,但两者的返回方式不同,主要区别在于 是否涉及段寄存器(CS)的修改。以下是详细解析:

ret 与 RETF 指令详解

在 x86 汇编语言中,"RET" 和 "RETF" 是用于 从子程序(函数/过程)返回 的指令,但两者的返回方式不同,主要区别在于 是否涉及段寄存器(CS)的修改。以下是详细解析:

***

1. RET(Return)

(1) 功能

- 从近调用(Near Call)返回,即 段内返回。

- 仅修改 "EIP/RIP"(指令指针),不修改 "CS"(代码段寄存器)。

- 适用于 同一代码段内的函数调用。

(2) 语法

RET ; 无参数版本(从栈弹出返回地址)

RET imm16 ; 有参数版本(弹出返回地址后,额外调整栈指针)

- "imm16" 是一个 16 位立即数,表示 返回后栈指针(ESP/SP)的调整量(通常用于清理调用者压栈的参数)。

(3) 执行过程

1. 从栈顶弹出返回地址(32/64位模式下为 "EIP/RIP",16位模式下为 "IP")。

2. 如果指定了 "imm16",则 "ESP/SP += imm16"(清理调用者的参数)。

3. 继续执行 返回地址处的代码。

(4) 示例

无参数版本

; 调用者

call my_function ; 近调用(EIP压栈)

...

; 被调用者

my_function:

mov eax, 42

ret ; 弹出EIP,返回到call的下一条指令

有参数版本(清理栈参数)

; 调用者

push 10 ; 参数1

push 20 ; 参数2

call my_function

add esp, 8 ; 调用者清理栈(等价于RET 8在被调用者中)

; 被调用者

my_function:

mov eax, [esp+4] ; 读取参数1

mov ebx, [esp+8] ; 读取参数2

ret 8 ; 弹出EIP后,ESP += 8(清理2个4字节参数)

***

2. RETF(Far Return)

(1) 功能

- 从远调用(Far Call)返回,即 跨段返回。

- 同时修改 "CS"(代码段寄存器)和 "EIP/RIP"。

- 适用于 不同代码段之间的函数调用(如实模式下的跨段调用或保护模式下的特权级切换)。

(2) 语法

RETF ; 无参数版本(从栈弹出CS和EIP)

RETF imm16 ; 有参数版本(弹出CS和EIP后,额外调整栈指针)

- "imm16" 的作用与 "RET" 相同,用于调整栈指针。

(3) 执行过程

1. 从栈顶弹出 "EIP/RIP"(返回地址)。

2. 从栈顶弹出 "CS"(代码段选择子)。

3. 如果指定了 "imm16",则 "ESP/SP += imm16"。

4. 继续执行 "CS:EIP" 指向的代码。

(4) 示例

实模式下的跨段调用

; 调用者(远调用)

push cs ; 压入调用者的CS

push next_inst ; 压入返回地址(next_inst的偏移)

jmp far 0x1000:0 ; 远跳转到目标段(模拟远调用)

next_inst:

...

; 被调用者(在另一个段)

far_function:

mov ax, 1234

retf ; 弹出IP和CS,返回到next_inst

保护模式下的特权级切换

在操作系统内核中,"RETF" 可用于从 内核态(高特权级)返回到用户态(低特权级):

; 内核代码(特权级0)

mov ax, 0x23 ; 用户态数据段选择子(低特权级)

push ax ; 压入用户态SS

push esp ; 压入用户态ESP

pushf ; 压入EFLAGS

mov ax, 0x1B ; 用户态代码段选择子(低特权级)

push ax ; 压入用户态CS

push user_entry ; 压入用户态EIP

iret ; 中断返回(等效于RETF + 特权级切换)

***

3. RET 与 RETF 的关键区别

| 特性 | RET | RETF |

||||

| 返回类型 | 近返回(段内) | 远返回(跨段) |

| 修改的寄存器| "EIP/RIP" | "CS" + "EIP/RIP" |

| 栈操作 | 弹出 "EIP" | 弹出 "EIP" + "CS" |

| 典型用途 | 同一代码段的函数返回 | 跨段调用、特权级切换、实模式 |

| 参数清理 | 支持 "RET imm16" | 支持 "RETF imm16" |

***

4. 常见问题

Q1:何时使用 RETF?

- 实模式:当调用者和被调用者位于 不同的代码段 时(如 "CALL FAR")。

- 保护模式:在 特权级切换(如从内核返回到用户态)或 任务切换 时。

Q2:RET 和 RETF 如何影响栈?

- RET:仅弹出 "EIP"(32位下 4 字节,64位下 8 字节)。

- RETF:弹出 "EIP" + "CS"(32位下 8 字节,64位下 16 字节)。

Q3:如何手动模拟 RETF?

在不支持 "RETF" 的架构(如某些嵌入式系统)中,可以手动弹出 "CS" 和 "EIP":

pop eip ; 实际无此指令,需用间接跳转

pop cs ; 实际无此指令,需用远跳转

***

5. 总结

- "RET":用于 同一代码段内的函数返回,仅修改 "EIP/RIP"。

- "RETF":用于 跨段或特权级切换的返回,修改 "CS" 和 "EIP/RIP"。

- 参数清理:两者均支持 "RET imm16" 或 "RETF imm16" 形式,用于调整栈指针。

掌握 "RET" 和 "RETF" 的区别是理解 x86 调用约定和系统级编程 的关键!

来源:行走江湖的橙子

相关推荐