assembly - 一个操作码字节如何根据“寄存器/操作码”字段解码为不同的指令?那是什么?
问题描述
如何确定字节数组将在机器代码中转换成什么?
我知道如果我在开始时看到 0f 它是一个 2 字节指令,但我看到了其他前缀,并且在我的 x64 调试器的一些反汇编中我看到了奇怪的交互,比如 48 83 C4 38 我可以在操作码参考上看到 48 说操作数为 64 字节。
但是83
说它可以是 7 条不同的指令,具体取决于称为“寄存器/操作码字段”的字段..什么?
有人可以解释处理器如何使用这些字节来确定背后的逻辑:
- 运行什么指令
- 指令在哪些寄存器和/或地址上使用(如果有)
解决方案
0x48
是一个 REX 前缀,其中 W 字段设置为 1,表示 64位操作数大小。(不是 64 字节)。
许多用于立即版本指令的操作码,包括使用ModR/M 字节中83
的 3 位/r
字段作为 3 个额外的操作码位。英特尔的第 2 卷手册记录了这一点,我认为附录中的操作码表包括它。
这就是为什么大多数原始 8086 立即数指令and r/m, imm
仍然只允许 2 个操作数,不像shrd eax, edx, 4
或imul 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
,这就是您所拥有的。
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
推荐阅读
- linux - Bash 消失的数组元素
- powershell - New-ScheduledTaskAction -Execute 中的 Powershell 字符数限制
- typescript - 怎么样?运算符不同于在类型中包含未定义?
- python - 未定义的变量“YouTube”python
- infinispan - 通过缓存迭代期间的 Infinispan-8 阻塞状态
- excel - 如何使用 VBA Excel 代码向拥有过期信息的用户显示弹出消息
- navigation - Excel VBA 用户表单导航难题
- javascript - nuxt-link:使用哈希重定向到同一个锚点
- c# - NJsonSchema - 如何将自定义属性添加到从 json 模式生成的 c# 类
- python - 基于标签聚类的networkx颜色