gpt4 book ai didi

assembly - 从操作码中查找指令中的操作数

转载 作者:行者123 更新时间:2023-12-04 05:06:58 26 4
gpt4 key购买 nike

我打算编写自己的小型反汇编程序。我想对读取可执行文件时得到的操作码进行解码。我看到以下操作码:

69 62 2f 6c 64 2d 6c
必须与以下内容相对应:
imul   $0x6c2d646c,0x2f(%edx),%esp
现在,“imul”指令可以具有两个或三个操作数。如何从我那里的操作码中找出答案?
它基于Intel的i386指令集。

最佳答案

尽管x86指令集非常复杂(无论如何都是CISC),并且我看到这里的许多人不鼓励您尝试理解它,但是我会说相反的话:它仍然可以理解,您可以从中学习为什么如此复杂,以及英特尔如何设法将其从8086一直扩展到现代处理器。

x86指令使用可变长度编码,因此它们可以由多个字节组成。每个字节用于编码不同的事物,其中一些是可选的(无论是否使用这些可选字段,它都在操作码中进行编码)。

例如,每个操作码可以在0到4个前缀字节之前,这是可选的。通常,您无需担心它们。它们用于更改操作数的大小,或者用作转义码,并带有现代CPU(MMX,SSE等)的扩展指令到操作码表的“第二层”。

然后是实际的操作码,通常为1个字节,但对于扩展指令,最多可以为3个字节。如果仅使用基本指令集,则也不必担心它们。

接下来,有一个所谓的ModR/M字节(有时也称为mode-reg-reg/mem),它对寻址模式和操作数类型进行编码。仅由具有任何此类操作数的操作码使用。它具有三个位字段:

  • 前两位(从左到右)是编址模式(4个可能的位组合)。
  • 接下来的三个位对第一个寄存器进行编码(8个可能的位组合)。
  • 后三位可以编码另一个寄存器,或扩展寻址模式,具体取决于前两位的设置。

  • ModR/M字节之后,可能还有另一个可选字节(取决于寻址模式),称为 SIB( S cale I ndex B ase)。它用于更特殊的寻址模式,以对比例因子(1x,2x,4x),基址/寄存器和索引寄存器进行编码。顾名思义,它的布局与 ModR/M字节相似,但是左起(最高有效)的前两位用于对小数位进行编码,后三位和后三位用于对索引和基址寄存器进行编码。

    如果使用了任何位移,它就会紧随其后。它可以是0、1、2或4个字节长,具体取决于寻址模式和执行模式(16位/32位/64位)。

    最后一个始终是即时数据(如果有)。它也可以是0、1、2或4个字节长。

    因此,现在,当您知道x86指令的总体格式时,您只需要知道所有这些字节的编码是什么。并且有一些模式,与普遍的信念相反。

    例如,所有寄存器编码都遵循整洁的模式 ACDB。也就是说,对于8位指令,寄存器代码的最低两位对A,C,D和B寄存器进行相应编码:
    00 = A寄存器(累加器) 01 = C寄存器(计数器) 10 = D寄存器(数据) 11 = B寄存器(基本)

    我怀疑他们的8位处理器仅使用这四个以这种方式编码的8位寄存器:
           second
    +---+---+
    f | 0 | 1 | 00 = A
    i +---+---+---+ 01 = C
    r | 0 | A : C | 10 = D
    s +---+ - + - + 11 = B
    t | 1 | D : B |
    +---+---+---+

    然后,在16位处理器上,他们将该寄存器组加倍,并在寄存器编码中再增加一位以选择该组,方法如下:
           second                second         0 00  =  AL
    +----+----+ +----+----+ 0 01 = CL
    f | 0 | 1 | f | 0 | 1 | 0 10 = DL
    i +---+----+----+ i +---+----+----+ 0 11 = BL
    r | 0 | AL : CL | r | 0 | AH : CH |
    s +---+ - -+ - -+ s +---+ - -+ - -+ 1 00 = AH
    t | 1 | DL : BL | t | 1 | DH : BH | 1 01 = CH
    +---+---+-----+ +---+----+----+ 1 10 = DH
    0 = BANK L 1 = BANK H 1 11 = BH

    但是现在您也可以选择将这两个寄存器的两个部分一起使用,作为完整的16位寄存器。这是由操作码的最后一位(最低有效位,最右边的一位)完成的:如果是 0,这是一条8位指令。但是,如果该位置1(即操作码为奇数),则该指令为16位。在这种模式下,两位像以前一样对 ACDB寄存器之一进行编码。模式保持不变。但是,它们现在对完整的16位寄存器进行编码。但是,如果还设置了第三个字节(最高字节),它们将切换到另一整个寄存器组,称为索引/指针寄存器,它们是: SP(堆栈指针), BP(基本指针), SI(源索引) , DI(目标/数据索引)。因此,寻址方式如下:
           second                second         0 00  =  AX
    +----+----+ +----+----+ 0 01 = CX
    f | 0 | 1 | f | 0 | 1 | 0 10 = DX
    i +---+----+----+ i +---+----+----+ 0 11 = BX
    r | 0 | AX : CX | r | 0 | SP : BP |
    s +---+ - -+ - -+ s +---+ - -+ - -+ 1 00 = SP
    t | 1 | DX : BX | t | 1 | SI : DI | 1 01 = BP
    +---+----+----+ +---+----+----+ 1 10 = SI
    0 = BANK OF 1 = BANK OF 1 11 = DI
    GENERAL-PURPOSE POINTER/INDEX
    REGISTERS REGISTERS

    当引入32位CPU时,他们再次将这些存储体加倍。但是模式保持不变。刚才,奇数操作码表示32位寄存器,偶数操作码表示8位寄存器。我将奇数操作码称为“长”版本,因为根据CPU及其当前操作模式使用16/32位版本。当它以16位模式操作时,奇数(“长”)操作码表示16位寄存器,但是当它以32位模式操作时,奇数(“长”)操作码表示32位寄存器。可以通过在整个指令前加上 66前缀(操作数大小覆盖)来翻转它。偶数操作码(“短”操作码)始终为8位。因此,在32位CPU中,寄存器代码为:
    0 00 = EAX      1 00 = ESP
    0 01 = ECX 1 01 = EBP
    0 10 = EDX 1 10 = ESI
    0 11 = EBX 1 11 = EDI

    如您所见, ACDB模式保持不变。 SP,BP,SI,SI模式也保持不变。它只使用较长版本的寄存器。

    操作码中也有一些模式。我已经描述了其中之一(偶数与奇数= 8位“短”与16/32位“长”的东西)。您可以在此操作码映射中看到更多它们,我为快速引用和手工组装/拆卸的东西而制作了一次:

    (这还不是一张完整的 table ,缺少一些操作码。也许我有一天会更新它。)

    如您所见,算术和逻辑指令大部分位于表的上半部分,左右两半的布局相似。数据移动指令位于下半部分。所有分支指令(条件跳转)都在 7*行中。还有一整行 B*保留给 mov指令,这是将立即值(常量)加载到寄存器中的简写形式。它们都是紧随其后的立即数的一字节操作码,因为它们在操作码中编码目标寄存器(它们由该表中的列号选择)以其三个最低有效字节(最右边的字节)进行编码。它们遵循相同的模式进行寄存器编码。第四位是“短”/“长”选择一个。
    您可以看到您的 imul指令在表中恰好位于 69位置(呵呵; J)。

    对于许多指令,“短/长”位之前的位是对操作数的顺序进行编码: ModR/M字节中编码的两个寄存器之一是源,而目的地是哪一个(这适用于带有两个寄存器操作数的指令)。

    关于 ModR/M字节的寻址模式字段,以下是对它的解释:
  • 11是最简单的:它对寄存器到寄存器的传输进行编码。一个寄存器由该字节的后三个位(reg字段)编码,另一个寄存器由该字节的其他三个位(R/M字段)编码。
  • 01表示在此字节之后,将出现一个字节的位移。
  • 10的含义相同,但是使用的位移为4字节(在32位CPU上)。
  • 00是最棘手的:它表示间接寻址或简单替换,具体取决于R/M字段的内容。

  • 如果存在 SIB字节,则通过 100位中的 R/M位模式来发出信号。还有一个仅用于32位位移模式的代码 101,它根本不使用 SIB字节。

    以下是所有这些寻址模式的摘要:
    Mod R/M
    11 rrr = register-register (one encoded in `R/M` bits, the other one in `reg` bits).
    00 rrr = [ register ] (except SP and BP, which are encoded in `SIB` byte)
    00 100 = SIB byte present
    00 101 = 32-bit displacement only (no `SIB` byte required)
    01 rrr = [ rrr + disp8 ] (8-bit displacement after the `ModR/M` byte)
    01 100 = SIB + disp8
    10 rrr = [ rrr + disp32 ] (except SP, which means that the `SIB` byte is used)
    10 100 = SIB + disp32

    现在让我们解码您的 imul:
    69是它的操作码。它对 imul的版本进行编码,该版本不对8位操作数进行符号扩展。 6B版本会对其进行符号扩展。 (如果有人要求,它们的区别在于操作码中的位1。)
    62RegR/M字节。二进制形式是 0110 001001 100 010。前两个字节( Mod字段)表示间接寻址模式,位移为8位。接下来的三位( reg字段)为 100,并将 SP寄存器(在本例中为 ESP,因为我们处于32位模式)编码为目标寄存器。最后三位是 R/M字段,我们在其中有 010,它将 D寄存器(在本例中为 EDX)编码为所使用的其他(源)寄存器。

    现在我们期望一个8位的位移。就是这样: 2f是位移,为正数(十进制为+47)。

    最后一部分是立即数的四个字节,这是 imul指令所必需的。在您的情况下,这是 6c 64 2d 6c,在little-endian中是 $6c2d646c

    这就是cookie崩溃的方式; -J

    关于assembly - 从操作码中查找指令中的操作数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6924912/

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