在 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 位移将是 0x05
或 0x02
,因为跳转编码了它们从指令末尾开始的位移。但是NASM的$
标签给出了指令的起始地址,汇编器总是根据目标地址为你计算相对位移。所以对于$+x
,你需要包括当前指令的长度。
如果你想自己编码 rel8,你可以使用 db
伪指令来发出你想要的字节。例如db 0xEB, 0x02
你不使用任何标签的整个方法/目标从根本上是有缺陷的,除非你有可靠的方法来知道下一条指令有多长. (例如,通过使用 jmp NEAR
或 jmp SHORT
来强制使用长编码或短编码)。您所能做的就是编码向前移动固定字节数的跳转,而不是跳过一条指令的跳转,无论宽度如何。
但真的只是使用标签; x86 跳跃总是相对的,所以让你的汇编器让你的生活更轻松,并计算正确的相对位移。
我试图在不使用标签的情况下恰好跳过 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 位移将是 0x05
或 0x02
,因为跳转编码了它们从指令末尾开始的位移。但是NASM的$
标签给出了指令的起始地址,汇编器总是根据目标地址为你计算相对位移。所以对于$+x
,你需要包括当前指令的长度。
如果你想自己编码 rel8,你可以使用 db
伪指令来发出你想要的字节。例如db 0xEB, 0x02
你不使用任何标签的整个方法/目标从根本上是有缺陷的,除非你有可靠的方法来知道下一条指令有多长. (例如,通过使用 jmp NEAR
或 jmp SHORT
来强制使用长编码或短编码)。您所能做的就是编码向前移动固定字节数的跳转,而不是跳过一条指令的跳转,无论宽度如何。
但真的只是使用标签; x86 跳跃总是相对的,所以让你的汇编器让你的生活更轻松,并计算正确的相对位移。