如何在x86中编码相对较短的jmp

20
假设我想要使用EB操作码进行短跳转,jmp rel8表示短跳转
Intel手册中的相关条目:

EB CBJMP rel8

"短跳转,RIP = RIP + 8位位移符号扩展到64位"

(其中CB是一个有符号的字节,表示与EIP寄存器中方向相关的相对偏移量)

在这个短跳转中,可能总是偏移会是 offset+2 ,因为执行时间中的EIP(参考方向)是双字节指令的基础,但加数总是发生

  • eb 30 = jmp 0x00000032 (+30)
  • eb e2 = jmp 0xffffffe4 (-30)

那么,EIP可以故意保持相同的方向,因为fe + 2是00EIP

  • eb fe = jmp 0x00000000

我发现令人惊讶的是,overoffset发生了分叉,尽管数字是负数。但在Intel手册中没有提到(也许因为有3000页)。

Intel® 64 and IA-32 Architectures Software Developer’s Manual: Vol. 2A 3-423

近跳转的跳转范围从当前的EIP值开始限制为-128到+127。

然后我考虑了三种可能性:

  1. 因为在执行时EIP的后/未来值,所以加上了+2。
  2. 编码值不是二进制补码表示的有符号数。
  3. 这出现在手册中,但我没看到,因为我很蠢。
3个回答

24

无论是否短跳,公式始终为目标地址 - (源地址 + sizeof(instruction))

换言之,即为目标地址 - 跳跃指令末尾地址

在此情况下(短跳),sizeof(instruction)为2。

添加这个值的原因是因为一旦CPU执行了指令获取阶段,指令指针已经指向分支后面的指令。rel8或rel32分支位移相对于EIP/RIP值。


1
我对术语感到困惑:什么是“短跳”?我在网上找不到这个定义。 - Anderson Green
4
@AndersonGreen 短跳指令被编码为 jmp rel8 (EB XX),其中相对距离(目标-源)小于0x80。另一个被称为长跳指令,编码为 jmp rel32 (E9 XXXXXXXX)。请注意,这可以使用66H前缀进行编码,该前缀将操作数更改为rel16 - JosephH
我必须调整这个公式才能使其工作。我使用了 destination - source - sizeof(instruction) - byxor
@byxor:好的,已修正。实际上最容易记住的是 dst - end_of_jmp - Peter Cordes

19

rel8 相对于下一条指令的内存地址,可以通过创建两个可执行文件并对其进行反汇编来轻松确认:

@label:
    jmp @label
    nop

这个指令可以使用 ndisasm 进行反汇编,无论是在 16 位、32 位还是 64 位代码中都一样:

EBFE jmp short 0x0
90   nop

然后,另一个可执行文件:

    jmp @label
@label:
    nop

EB00 jmp short 0x2
90   nop

所以,rel8 始终相对于 jmp 指令之后的下一条指令进行编码。然而,反汇编器(至少是 ndisasmudcli)将其相对于 jmp 指令本身显示。这可能会造成一些混乱。


1
反汇编器在将目标地址解码为汇编语法时显示绝对目标地址。ndisasm 默认从地址零开始解析平面二进制文件。ndisasm -o 0x7c00 将使用偏移量 7C00 加载二进制文件并使用相应的地址。 - Peter Cordes

7
跳转短语句相对于跳转指令的结尾取一个EIP,该指令长度为两个字节,并且需要一个一字节操作数,该操作数会被符号扩展并加到EIP上。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接