如何在 x64 asm 中通过相对于 RIP 的跳转准确跳过 1 条指令?

ion*_*des 3 64-bit assembly intel

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

cmp r12, r13
je dest ; skip the jmp
jmp whatever
dest:
nop
Run Code Online (Sandbox Code Playgroud)

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

cmp r12, r13
je rip+0x05 ; this would obviously depend on the length of the next instruction
jmp whatever
nop
Run Code Online (Sandbox Code Playgroud)

但是,我缺乏生产任何工作的知识,据我所知,没有黑客是不可能读/写 RIP 寄存器的。

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

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

je .+0x05
; or
je $+0x05
Run Code Online (Sandbox Code Playgroud)

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

je +0x05
Run Code Online (Sandbox Code Playgroud)

有没有人能够确认这有效?

编辑 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
Run Code Online (Sandbox Code Playgroud)

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

jmp +4
jmp .
ret 16
Run Code Online (Sandbox Code Playgroud)

Pet*_*des 5

请注意,在 NASM 和 MASM$中不是前缀。 它是一个独立的关键字/符号,指的是当前行的地址

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

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

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


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

但真的只是使用一个标签;无论如何,x86 跳转总是相对的,因此让您的汇编程序使您的生活更轻松并计算正确的相对位移。