pop eip 是合法指令吗?
Is pop eip legal instruction?
我正在做大学的理论考试,被问到这个问题:
经过一些指令后,esp 增长了 4,eip 增长了 20,这可能是什么指令?
我标记了“pop eip”和“ret”。
nasm 32位汇编是否可以执行pop eip指令?
pop eip
不是真正的 x86 指令。 没有 assembler 会 assemble 它,AFAIK。
它是 解释 ret
的伪代码。请参阅操作部分 in the manual。特别是正常的 "near" ret
; far jmp/call/ret 在"normal" 32 位代码中基本不用。
ret
有自己的操作码,与 pop
的任何编码分开,x86 也选择给它一个单独的助记符。 pop eip
也被接受为 0xc3
操作码的另一个名称将是一个有效的设计。 x86 确实为许多不同的操作码重载了 mov
助记符,包括整数寄存器 and/or 内存或立即数之间的 mov
to/from control registers, mov
to/from debug registers, as well as the standard mov
。 (不过,mov
的 "standard" 形式也有几个不同的操作码可供选择。)
但这会有点奇怪,因为 push eip
不存在,除非 call +0
具有跳跃的 side-effect 性能。
EIP 不是 8 个 general-purpose 整数寄存器之一,因此 pop
的正常编码无法编码 ret
。这就是为什么 ret
需要自己的操作码,以及为什么在 asm 源代码中有一个单独的助记符是有意义的。 x86 指令将寄存器编码为 3 位数字,或者在 x86-64 上可选地编码为 4 位。或作为 隐式 源或目标,如 EDX:EAX 用于 mul
或 div
,或 pushf
隐式读取 EFLAGS:它只是隐式通过该操作码,没有任何位 mean EFLAGS.
ret
不是魔术:它所做的只是弹出堆栈并将结果用作跳转目标。由程序员来确保 ESP 指向您要跳转到的地址,通常是 return 地址。
有些新手不理解这一点,认为ret
会神奇return到最后call
,所以不把[=11=上的错联系起来] 他们的代码弄乱了堆栈。
我肯定在 SO 答案和评论中多次写过类似“ret
是我们在 x86 上用于 pop eip
的名称”。
有趣的事实:在 ARM 32 位上,程序计数器 是 16 个整数寄存器之一,r15
,所以你真的可以 pop {r4, pc}
恢复已保存的 R4 并将已保存的 lr
(link 寄存器 = return 地址)弹出到程序计数器中,全部在一条指令中完成。所以 ARM 确实可以使用它用于弹出 general-purpose 整数寄存器的相同操作码来执行 pop eip
的等效操作。
esp grew by 4 and eip grew by 20
是的,我认为 C3 ret
or C2 00 00 ret 0
是唯一可以做到这一点的 2 个操作码,并且都使用 ret
助记符。
如果 EIP 增长了 15 或更少,add esp, 4
或 pop eax
的长编码可以解释它,例如具有多个冗余 rep
and/or fs
前缀和直接 4
.
的 imm32
编码
x86 指令的长度最多为 15 个字节;如果解码在 15 个字节之前没有到达指令末尾,CPU 会出现 #UD
异常,就像其他非法指令一样。所以用一条指令将 EIP 改变 20 个字节只有通过跳转才有可能。 增加 ESP的唯一跳跃是ret
; jmp / jcc 保持不变,call
推送一个 return 地址。
iret
几乎是可能的,但它弹出 CS:IP
和一个 FLAGS 值:你不能让它只弹出 4 个字节。 (尤其是在 32 位模式下。)
sysret
不修改 ESP,仅供内核使用(ring 0)。 sysexit
从 RCX 和 RIP = RDX 设置 RSP,但我很确定这不是他们正在寻找的答案。 :P
我正在做大学的理论考试,被问到这个问题: 经过一些指令后,esp 增长了 4,eip 增长了 20,这可能是什么指令? 我标记了“pop eip”和“ret”。 nasm 32位汇编是否可以执行pop eip指令?
pop eip
不是真正的 x86 指令。 没有 assembler 会 assemble 它,AFAIK。
它是 解释 ret
的伪代码。请参阅操作部分 in the manual。特别是正常的 "near" ret
; far jmp/call/ret 在"normal" 32 位代码中基本不用。
ret
有自己的操作码,与 pop
的任何编码分开,x86 也选择给它一个单独的助记符。 pop eip
也被接受为 0xc3
操作码的另一个名称将是一个有效的设计。 x86 确实为许多不同的操作码重载了 mov
助记符,包括整数寄存器 and/or 内存或立即数之间的 mov
to/from control registers, mov
to/from debug registers, as well as the standard mov
。 (不过,mov
的 "standard" 形式也有几个不同的操作码可供选择。)
但这会有点奇怪,因为 push eip
不存在,除非 call +0
具有跳跃的 side-effect 性能。
EIP 不是 8 个 general-purpose 整数寄存器之一,因此 pop
的正常编码无法编码 ret
。这就是为什么 ret
需要自己的操作码,以及为什么在 asm 源代码中有一个单独的助记符是有意义的。 x86 指令将寄存器编码为 3 位数字,或者在 x86-64 上可选地编码为 4 位。或作为 隐式 源或目标,如 EDX:EAX 用于 mul
或 div
,或 pushf
隐式读取 EFLAGS:它只是隐式通过该操作码,没有任何位 mean EFLAGS.
ret
不是魔术:它所做的只是弹出堆栈并将结果用作跳转目标。由程序员来确保 ESP 指向您要跳转到的地址,通常是 return 地址。
有些新手不理解这一点,认为ret
会神奇return到最后call
,所以不把[=11=上的错联系起来] 他们的代码弄乱了堆栈。
我肯定在 SO 答案和评论中多次写过类似“ret
是我们在 x86 上用于 pop eip
的名称”。
有趣的事实:在 ARM 32 位上,程序计数器 是 16 个整数寄存器之一,r15
,所以你真的可以 pop {r4, pc}
恢复已保存的 R4 并将已保存的 lr
(link 寄存器 = return 地址)弹出到程序计数器中,全部在一条指令中完成。所以 ARM 确实可以使用它用于弹出 general-purpose 整数寄存器的相同操作码来执行 pop eip
的等效操作。
esp grew by 4 and eip grew by 20
是的,我认为 C3 ret
or C2 00 00 ret 0
是唯一可以做到这一点的 2 个操作码,并且都使用 ret
助记符。
如果 EIP 增长了 15 或更少,add esp, 4
或 pop eax
的长编码可以解释它,例如具有多个冗余 rep
and/or fs
前缀和直接 4
.
imm32
编码
x86 指令的长度最多为 15 个字节;如果解码在 15 个字节之前没有到达指令末尾,CPU 会出现 #UD
异常,就像其他非法指令一样。所以用一条指令将 EIP 改变 20 个字节只有通过跳转才有可能。 增加 ESP的唯一跳跃是ret
; jmp / jcc 保持不变,call
推送一个 return 地址。
iret
几乎是可能的,但它弹出 CS:IP
和一个 FLAGS 值:你不能让它只弹出 4 个字节。 (尤其是在 32 位模式下。)
sysret
不修改 ESP,仅供内核使用(ring 0)。 sysexit
从 RCX 和 RIP = RDX 设置 RSP,但我很确定这不是他们正在寻找的答案。 :P