在 x64 asm 中,如何使用相对于 RIP 的跳转恰好跳过 1 条指令?

How do I skip exactly 1 instruction with a jump relative to RIP in x64 asm?

我试图在不使用标签的情况下恰好跳过 1 条指令,带有标签的示例是:

cmp r12, r13
je dest ; skip the jmp
jmp whatever
dest:
nop

然而,我的限制是我不能使用标签,所以我假设我必须创建一个相对于 RIP 寄存器的跳转。例如(伪):

cmp r12, r13
je rip+0x05 ; this would obviously depend on the length of the next instruction
jmp whatever
nop

但是,我缺乏产生任何工作的知识,而且据我所知,read/write RIP 注册没有黑客是不可能的。

编辑: 我只熟悉 Intel 语法,我使用 Keystone 作为汇编器。我将从程序集中获取字节并将其加载到可执行内存位置。我正在使用我自己的网站从程序集中获取字节,如果您查看 it.

,您可能会明白这一点

编辑 2: 我尝试了 Jesters 和 Margaret Blooms 的评论,建议使用:

je .+0x05
; or
je $+0x05

但是,我可以确认两者都不适用于 Keystone!幸运的是,我注意到 Keystone 能够处理这段代码:

je +0x05

有谁能证实这有效吗?

编辑 3: 我用 NASM 试了一下, $ 前缀在我测试的代码中工作正常。我用这个来测试它:

section .text       
global _WinMain@16       

_WinMain@16:
    jmp $+2+2 ; skip this jmp and the next jmp (each 2 bytes)
    jmp $
    ret 16

它按预期工作。 defuse 也产生与 Keystone 相同的输出。唯一的区别是 defuse 使用 $ 前缀,而 Keystone 根本不需要! Keystone 等效值是:

jmp +4
jmp .
ret 16

请注意,在 NASM 和 MASM 中,$ 不是前缀。 It's a stand-alone keyword / symbol which refers to the address of the current line.

je $+5不行:jmp rel32是5个字节,short JCC是2个。所以你需要je $+7来跳过一个5字节的指令,或者$+4 跳过 2 字节指令。

rel8 位移将是 0x050x02,因为跳转编码了它们从指令末尾开始的位移。但是NASM的$标签给出了指令的起始地址,汇编器总是根据目标地址为你计算相对位移。所以对于$+x,你需要包括当前指令的长度。

如果你想自己编码 rel8,你可以使用 db 伪指令来发出你想要的字节。例如db 0xEB, 0x02


你不使用任何标签的整个方法/目标从根本上是有缺陷的,除非你有可靠的方法来知道下一条指令有多长. (例如,通过使用 jmp NEARjmp SHORT 来强制使用长编码或短编码)。您所能做的就是编码向前移动固定字节数的跳转,而不是跳过一条指令的跳转,无论宽度如何。

但真的只是使用标签; x86 跳跃总是相对的,所以让你的汇编器让你的生活更轻松,并计算正确的相对位移。