gpt4 book ai didi

assembly - 在 x86-64 中编码 JMP FAR 和 CALL FAR

转载 作者:行者123 更新时间:2023-12-05 02:15:25 26 4
gpt4 key购买 nike

我熟悉 r/m8、r/m16、imm16 等,但如何对 m16:16、m16:32 和 m16:64 进行编码?这些在 JMP 和 CALL 指令中...

m16:16 是地址位置吗?或者它就像一个即时地址?任何帮助将不胜感激!

最佳答案

“编码”通常表示机器代码字节。但我认为你问的是汇编语法,因为英特尔的手册对机器代码很清楚。 (请参阅 jmp 的条目,或 Intel's vol.2 instruction set reference manual 的其余部分,了解有关条目如何格式化以及内容含义的更多信息。)


jmp m16:64 是一种内存间接远跳转,具有新的 RIP 和 CS 值(按此顺序排列,因为 x86 是小端字节序)。

就像内存间接近跳转一样,您只需提供一种寻址模式,CPU 就会从那里加载内存操作数。但它是一个 10 字节的内存操作数,而不是近跳转的 8 个字节。

您可以使用任何寻址模式。我简单地使用了[rdi]。所有这些都与 call far/lcall 相同。

NASM 语法:

jmp far [rdi]        ; for YASM, you need a manual REX prefix somehow

AT&T 语法:

rex64 ljmp *(%rdi)        # force REX prefix which buggy GAS omits
ljmpq *(%rdi) # clang accepts this, GAS doesn't.

GAS .intel_syntax noprefix `objdump -drwC -Mintel 反汇编:

  400080:       48 ff 2f        rex.W jmp FWORD PTR [rdi]

或者从 llvm-objdump -d 到 AT&T 语法:

  400080:  48 ff 2f             ljmpq   *(%rdi)

GNU Binutils 错误,它需要 48 REX.W 前缀才能将操作数大小设置为 64 位。 (我认为是内存源操作数。)

FWORD (48-bit far-word = m16:32) 实际上可能是没有 REX 前缀的正确反汇编,这就是为什么它不是我们想要的以及为什么它在没有 REX.W 的情况下崩溃的原因内存实际上是一个m16:64。我们需要 48 ff 2f 作为 TWORD (m16:64) 内存操作数。

GAS 不会组装 ljmpq *(%rdi),但 clang 会。


例如设置CS=si和RIP=rdi

; NASM syntax
mov [rsp], rdi
mov [rsp+8], si ; new CS value goes last because x86 is little-endian
jmp far [rsp] ; loads 10 byte from memory

push rsi/push rdi/jmp far [rsp],或您要使用的任何其他内存位置。


NASM 知道 far jmp 需要一个 REX.W 前缀,这与 YASM 和 GNU Binutils 不同。它使用

; assembled by NASM (not YASM), disassembled with objdump -drwC -Mintel
400080: 48 ff 2f rex.W jmp FWORD PTR [rdi]

printf '\xff\x2f' | ndisasm -b64 - 向我们展示了 NASM 的反汇编输出:

; ndisasm -b64 output thinks it's a dword (m16:16)?
00000000 FF2F jmp dword far [rdi]

Intel 的手动条目将 jmp m16:64 列为需要 REX.W 前缀,但 GAS/binutils 错误地认为这不是必需的。另见关于 https://lkml.org/lkml/2012/12/23/164 的讨论关于 lretrex64 ljmp *initial_code(%rip) 的 Linux 内核代码用法,以及关于 AMD CPU 是否支持 FF/5 的猜测带有 REX.W 前缀。由于 AMD 文档没有明确提及它。


实验测试:远跳/调用需要REX前缀

我在 Intel i7-6700k Skylake 上的 GNU/Linux 上的静态可执行文件中测试了这个(因此它会在低 32 位之外加载):

default rel
foo:
mov eax, 231
syscall ; exit_group(edi)

global _start
_start:

mov eax, cs
push rax ; push cs is gone in x86-64
lea rax, [foo]
push rax
call far [rsp]
$ nasm -felf64 farjmp.asm          # or yasm
$ gcc -nostdlib -static-pie farjmp.o -o farjmp
$ ./farjmp
or gdb ./farjmp
  • 由 YASM(没有 REX.W)组装,它在 call far [rsp] 上发生段错误。
  • 由 NASM 组装,(使用 REX.W),它成功到达 foo:

jmp far ptr16:64 不存在,ptr16:32ptr16:16 不可用64 位模式。那将是一个 10 字节的立即(直接)绝对跳转目标。 x86-64 根本无法使用绝对直接跳转:无法将新的 CS 或 RIP 编码为 jmp 指令。

直接近距离跳跃使用rel32rel8,当然他们不能改变CS。 (这就是接近的意思)。

32 位模式有 jmp far ptr16:32(带有 6 字节立即数)。

jmp far 的用例不多,尤其是在 64 位模式下。在内核中,您将使用 iretsysret 返回到 32 位用户空间,并且通常没有其他理由切换代码段。我想您可以在内核中将内核切换到 32 位模式。

关于assembly - 在 x86-64 中编码 JMP FAR 和 CALL FAR,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51832437/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com