首页 > 解决方案 > 一个操作码字节如何根据“寄存器/操作码”字段解码为不同的指令?那是什么?

问题描述

如何确定字节数组将在机器代码中转换成什么?

我知道如果我在开始时看到 0f 它是一个 2 字节指令,但我看到了其他前缀,并且在我的 x64 调试器的一些反汇编中我看到了奇怪的交互,比如 48 83 C4 38 我可以在操作码参考上看到 48 说操作数为 64 字节。

但是83说它可以是 7 条不同的指令,具体取决于称为“寄存器/操作码字段”的字段..什么?

有人可以解释处理器如何使用这些字节来确定背后的逻辑:

  1. 运行什么指令
  2. 指令在哪些寄存器和/或地址上使用(如果有)

标签: assemblyx86-64disassemblymachine-code

解决方案


0x48是一个 REX 前缀,其中 W 字段设置为 1,表示 64操作数大小。(不是 64 字节)。

许多用于立即版本指令的操作码,包括使用ModR/M 字节中83的 3 位/r字段作为 3 个额外的操作码位。英特尔的第 2 卷手册记录了这一点,我认为附录中的操作码表包括它。

这就是为什么大多数原始 8086 立即数指令and r/m, imm仍然只允许 2 个操作数,不像shrd eax, edx, 4imul edx, [rdi], 12345其中两个 ModRM 字段都用于编码操作数,以及操作码隐含的立​​即数操作数。SHRD/SHLD 和加入 386,imul-immediate加入 186。不幸的是,copy-and-AND ( and eax, edx, 0xf) 不可编码,但至少 x86 可以使用 LEA 进行复制和添加/订阅。


每条指令都有自己的文档,例如add(vol2 手册的 html 摘录)
REX.W + 83 /0 ib ,显示了 for之类的编码ADD r/m64, imm8,这就是您所拥有的。

来自 wiki.osdev.org 的 ModRM 位域图

  7                           0
+---+---+---+---+---+---+---+---+
|  mod  |    reg    |     rm    |
+---+---+---+---+---+---+---+---+

0xc4 = 0b11000100,所以 reg 字段 = 0。因此我们的操作码是83 /0,在 Intel 的表示法中。

其余的 ModRM 字段是:

  • mode = 0b11,因此 rm 字段编码一个寄存器操作数,而不是寻址模式的基址寄存器。
  • rm = 0b100。reg #4 = SPL/SP/ESP/RSP。(在这种情况下,RSP 因为它是 64 位操作数大小)。请参阅英特尔的手册,或https://wiki.osdev.org/X86-64_Instruction_Encoding#Registers获取表格。

所以指令是add rsp, 0x38

ndisasm -b64同意:

$ cat > foo.asm
db 0x48, 0x83, 0xC4, 0x38
$ nasm foo.asm     # create a flat binary with those bytes, not an object file
$ ndisasm -b64 foo
00000000  4883C438          add rsp,byte +0x38

推荐阅读