Assemble 远调用或远跳转(j* 指令)

Assemble far calls or far jumps (j* instructions)

我正在尝试创建一个分派 table,它会更改由 AllocateMemoryOnRemoteProcess 分配的另一个地址中某些指令的位置。

我遇到的问题之一是几乎所有 Calls 和所有类型的 Jumpsnearrelative 只要我加载程序集在新位置,则这些说明将不起作用。

据我所知,我应该将这些说明转换为 far jumpfar call 我在谷歌搜索期间看到的解决方案之一是使用 pushret,例如:

push 0xdeadbeef
ret

或者有人建议使用寄存器进行绝对寻址,例如:

mov %eax,0xdeadbeef
jmp %eax

这些解决方案在我的情况下不起作用,因为只要我在函数例程中,更改堆栈状态或在第二种情况下更改 %eax 之类的寄存器就会导致失败。

this question 中有人写道:

  • call far (with opcode 9A) jumps to an absolute segment and offset. ie, it's like setting CS and ?IP at once.

所以看来我应该将操作码与 9A 一起用于 far calls,但这只适用于调用,我不知道如何使用这种方法转换各种跳转!

我经常使用 objdump disassemble 二进制文件,然后使用 clang 作为 assembler 使用以下命令:

clang -c MyAsm.asm -m32

但是当我 assemble 使用上面的命令时,结果是相对的。

例如,当 MyAsm.asm 是:

call   0x402af2

objdump的结果是:

    MyAsm.o:    file format Mach-O 32-bit i386

Disassembly of section __TEXT,__text:
__text:
       0:   e8 ed 2a 40 00  calll   4205293 <__text+0x402AF2>

这些结果是相对的。

所以我的问题是:

  1. 我如何 assemble far callsfar jumpsj* 指令)与 clang 或任何其他工具(当然,两者都适用) 80x86 和 Amd64 结构)?
  2. 是否有调用或跳转等其他指令使用相对寻址,所以我应该重新assemble以避免问题?

如果你能腾出一个寄存器,我建议你使用

    movabs $addr,%rax
    jmp *%rax

或者,如果您可以确保地址在地址的前 2 GB 之内 space,

    mov $addr,%eax
    jmp *%eax

我强烈建议你不要使用

    push $addr
    ret

因为这破坏了 return 预测,使接下来的几个函数 return 比必要的慢。远距离跳跃和呼叫(ljmplcall)是一个转移注意力的问题。虽然它们在技术上可以使用,但它们不会帮助您实现目标并且实际上意味着不同的目的(更改 cs)并且在现代处理器上作为缓慢的微编码指令实现。

如果你不能腾出一个寄存器,你可以使用这种技巧:

    jmp *0f(%rip)
    ...
0:  .quad addr

第二行可以在程序的任何地方,并且应该在数据段中以获得理想的性能。但是,如果需要,也可以紧跟在跳转指令之后。

这应该可以正常工作,而且不需要您使用额外的寄存器。虽然它比使用寄存器慢。

请注意,条件跳转严格要求跳转目标是立即的。如果你想做一个条件跳转到一个绝对地址,使用像这样的成语:

    # for jz addr
    jnz 1f
    jmp *0f(%rip)
0:  .quad addr
1:  ...

16 位和 32 位模式的特殊注意事项

注意在16位和32位模式下,没有rip-relative寻址模式。所以你必须使用绝对地址并写

    jmp *0f
0:  .long addr

相反。但是,这样就达不到目的了,好像你可以使用绝对寻址方式到达0f,你也可以只使用相对寻址方式到达addr。所以看起来你必须求助于 push + ret 序列,即使它很慢。

在 16 位模式下,很可能使用远跳就可以了。如果不是,则 push + ret 序列是惯用的(那个年份的处理器没有 return 预测)。