Assembly 从操作码查找指令中的操作数

Assembly 从操作码查找指令中的操作数,assembly,x86,disassembly,opcode,Assembly,X86,Disassembly,Opcode,我正计划编写自己的小型反汇编程序。我想对读取可执行文件时得到的操作码进行解码。我看到以下操作码: 69 62 2f 6c 64 2d 6c 必须符合: imul $0x6c2d646c,0x2f(%edx),%esp 现在,“imul”指令可以有两个或三个操作数。我如何从那里的操作码中找出这个 它基于Intel的i386指令集。手册确实描述了如何区分一个、两个或三个操作数版本 F6/F7:一个操作数;0F AF:两个操作数;6B/69:三个操作数。如果您不想在操作码表/手册中切换,那么

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

69 62 2f 6c 64 2d 6c
必须符合:

imul   $0x6c2d646c,0x2f(%edx),%esp
现在,“imul”指令可以有两个或三个操作数。我如何从那里的操作码中找出这个


它基于Intel的i386指令集。

手册确实描述了如何区分一个、两个或三个操作数版本


F6/F7:一个操作数;0F AF:两个操作数;6B/69:三个操作数。

如果您不想在操作码表/手册中切换,那么向其他项目学习总是有帮助的,比如开源反汇编程序,您可能会发现,您甚至不需要创建自己的操作数,这取决于您的目的。

一些建议,首先,获取所有您可以获得的指令集文档。对于这个x86案例,请尝试使用一些旧的8088/86手册以及英特尔最近的手册,以及网络上丰富的操作码表。各种解释和文档可能首先会有细微的文档错误或差异,其次一些人可能会以不同的、更容易理解的方式呈现信息

第二,如果这是您的第一个反汇编程序,我建议您避免使用x86,这是非常困难的。正如您的问题所暗示的,可变字长指令集很难实现远程成功的反汇编程序,您需要按照代码的执行顺序,而不是内存顺序。因此,反汇编程序必须使用某种方案,不仅要解码和打印指令,还要解码跳转指令并将目标地址标记为指令的入口点。例如,ARM是固定指令长度的,您可以编写一个ARM反汇编程序,从ram的开头开始,直接反汇编每个单词(当然,假设它不是ARM和thumb代码的混合体)。thumb(不是thumb2)可以通过这种方式进行反汇编,因为只有一种32位指令,其他所有指令都是16位指令,并且这一种指令可以在简单的状态机中处理,因为这两条16位指令成对显示

您将无法反汇编所有内容(使用可变长度指令集),并且由于某些手动编码的细微差别或防止反汇编的有意策略,您按执行顺序遍历代码的预先代码可能会发生我称之为冲突的情况,例如,您的上述指令。假设有一条路径将您带到0x69作为指令的入口点,然后您根据该路径确定这是一条7字节指令,但假设在其他地方有一条分支指令,其目标计算为0x2f作为指令的操作码,尽管非常聪明的编程可能会成功,更可能的情况是,反汇编程序已被导入反汇编数据。比如说

clear condition flag
branch if condition flag clear
data
反汇编程序不会知道数据是数据,如果没有额外的智能,反汇编程序也不会意识到条件分支实际上是无条件分支(条件清除和条件清除分支之间的不同分支路径上可能有许多指令)因此,它假定条件分支后的字节是一条指令

最后,我对你的努力表示赞赏,我经常鼓吹编写简单的反汇编程序(假定代码非常短,有意编写代码的反汇编程序)来很好地学习指令集。如果您不将反汇编程序置于必须按照执行顺序执行的情况下,而是可以按照内存顺序执行(基本上不在指令之间嵌入数据,将其放在末尾或其他地方,只留下要反汇编的指令字符串)。理解指令集的操作码解码可以使您更好地为该平台编程,无论是低级语言还是高级语言


简而言之,英特尔曾经出版,也许现在仍然出版,处理器的技术参考手册,我还有8088/86手册,一本硬件手册,一本电子手册,一本软件手册,介绍指令集及其工作原理。我有一辆486,可能还有一辆386。Igor回答中的快照与intel手册非常相似。因为指令集随着时间的推移已经进化了很多,所以x86充其量只能算是一个困难的野兽。同时,如果处理器本身能够遍历这些字节并执行它们,那么您可以编写一个程序来执行相同的操作,但对它们进行解码。不同之处在于,您可能不会制作模拟器,并且任何由代码计算且在代码中不明确的分支您将无法看到,并且该分支的目标可能不会显示在要反汇编的字节列表中。

这不是机器代码指令(由一个操作码和零个或多个操作数组成)

这是文本字符串的一部分,翻译为:

$ echo -e "\x69\x62\x2f\x6c\x64\x2d\x6c"
ib/ld-l

这显然是字符串
“/lib/ld linux.so.2”

的一部分,尽管x86指令集非常复杂(反正是CISC)我看到很多人都在阻止你尝试去理解它,我要说的是相反的:它仍然可以被理解,你可以在途中了解为什么它如此复杂,以及英特尔是如何将它从8086扩展到现代处理器的

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

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

Th
       second
      +---+---+
f     | 0 | 1 |          00 = A
i +---+---+---+          01 = C
r | 0 | A : C |          10 = D
s +---+ - + - +          11 = B
t | 1 | D : B |
  +---+---+---+
       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
       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
0 00 = EAX      1 00 = ESP
0 01 = ECX      1 01 = EBP
0 10 = EDX      1 10 = ESI
0 11 = EBX      1 11 = EDI
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