【问题标题】:Can someone explain this directly assembled x86 JMP opcode?有人可以解释这个直接组装的 x86 JMP 操作码吗?
【发布时间】:2010-10-07 09:39:42
【问题描述】:

在学校,我们一直在使用引导程序来运行没有操作系统的独立程序。我一直在研究这个程序,当启用保护模式时,通过直接将操作码和操作数组装为程序中的数据来执行远跳转。这是给 GNU 汇编器的:


         /* this code immediately follows the setting of the PE flag in CR0 */

.byte   0x66, 0xEA
.long   TARGET_ADDRESS
.word   0x0010          /* descriptor #2, GDT, RPL=0 */

首先,为什么要这样做(而不是指令助记符)?

我一直在查看英特尔手册,但仍然对代码感到有些困惑。特别是在第 2A 卷第 3-549 页中,有一个操作码表。相关条目:

EA *cp* JMP ptr16:32 Inv.有效跳转远,绝对,地址 操作数

实际操作码是显而易见的,但第一个字节 0x66 让我感到困惑。参考英特尔手册中的表格, cp 显然意味着后面会跟着一个 6 字节的操作数。接下来的两行显然是 6 个字节。 0x66 编码一个“操作数大小覆盖前缀”。这和表中的cp有什么关系?我原以为cp会有一些十六进制值,但是有这个覆盖前缀。有人可以帮我解决这个问题吗?

这是来自 od 的转储:

c022 **ea66 0000 0001 0010** ba52 03f2 c030

TARGET_ADDRESS 被定义为 0x00010000。

我也对最后两个字节的重要性感到困惑。然而,这似乎完全是另一个问题。现在已经很晚了,我已经盯着代码和英特尔手册看了好几个小时了,所以我希望我能明白我的意思。

感谢收看!

【问题讨论】:

  • 人们使用操作码(而不是指令)有两个原因。第一个原因是当汇编器“不够充分”并且不支持他们需要的指令时(这在添加新指令并且旧的汇编器还不支持它们时很常见)。第二个原因是当汇编器确实支持他们需要的指令但程序员不知道如何说服汇编器生成它时。基本上,要么是糟糕的工具(包括旧工具、混乱的语法和/或糟糕的文档),要么是糟糕的程序员。
  • 注意:我上面的评论是“一般的”,适用于所有汇编程序。我不使用 GAS,也不知道它是否支持“16 位代码中的 32 位远跳转”指令(或者文档的好坏程度)。

标签: assembly x86 gnu opcode protected-mode


【解决方案1】:

0x66 指定当前代码段大小的操作数大小覆盖。假设当前代码大小是 16 位,新指令指针将是 32 位,而不是 16 位。如果当前代码段大小为 32 位,则 0x66 会将目标指令指针呈现为 16 位。当前代码大小属性取决于使用的 CS 选择器及其从 GDT/LDT 表加载的属性。在实模式下,代码段大小通常为 16 位,“虚幻”模式的特殊情况除外。

【讨论】:

  • 虚幻模式是实模式,缓存描述符仍然设置为限制 > 64k 从切换到保护模式并返回。 32 位 CS 绝对意味着保护模式,但如果禁用分页,它仍然直接使用物理地址。
【解决方案2】:

我遇到了一点。一些汇编程序只会跳转到 LABEL 。在这种情况下,人们想要绝对跳转到特定的硬编码偏移量。我猜 jmp TARGET_ADDRESS 不起作用,所以他们只是把它作为字节来解决这个问题。

【讨论】:

    【解决方案3】:

    0x66 表示 JMP (0xEA) 指的是六个字节。默认值是指实模式下的 64K(16 位)或保护模式下的 32 位(如果我没记错的话)。增加它后,它还包括段描述符,即 GDT 或 LDT 中的段索引,这意味着该代码正在执行传统上所谓的“长跳转”:跨越x86 架构。在这种情况下,该段指向 GDT 上的第二个条目。如果您以前查看该程序,您可能会看到 GDT 是如何根据段起始地址和长度定义的(查看英特尔手册以研究 GDT 和 LDT 表,描述每个段的 32 位条目)。

    【讨论】:

    • 啊,现在说得通了。早些时候,当定义 GDT 时,第一个条目为空(如手册所述),但第二个是代码段。在重新阅读手册的某些部分后,我看到了它是如何工作的。感谢您解决这个问题。
    • 话说回来,我还是很好奇作者为什么选择这样做而不是使用助记词。
    • 它是操作数大小的前缀,但它从jmp ptr16:16 into jmp ptr16:32 改变了它。这个答案声称无前缀版本是jmp rel16jmp rel32,但这是一个不同的操作码,E9 不是EAEA 始终是具有立即偏移量和段的 far-jmp。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-09
    相关资源
    最近更新 更多