【问题标题】:How to write a disassembler? [closed]如何编写反汇编程序? [关闭]
【发布时间】:2010-10-29 18:52:33
【问题描述】:

我有兴趣编写一个 x86 反汇编程序作为一个教育项目。

我找到的唯一真正的资源是 Spiral Space 的“How to write a disassembler”。虽然这对反汇编程序的各种组件进行了很好的高级描述,但我对一些更详细的资源感兴趣。我还快速浏览了NASM's 源代码,但这有点值得学习。

我意识到这个项目的主要挑战之一是我必须处理相当大的 x86 指令集。我也对基本结构、基本反汇编链接等感兴趣。

谁能告诉我任何关于编写 x86 反汇编程序的详细资源?

【问题讨论】:

标签: x86 disassembly


【解决方案1】:

我建议查看一些开源反汇编程序,最好是distorm,尤其是“disOps(指令集数据库)”(ctrl+在页面上查找)。

文档本身充满了关于操作码和指令的丰富信息。

引用自 https://code.google.com/p/distorm/wiki/x86_x64_Machine_Code

80x86 指令:

一条 80x86 指令被划分为一个 元素数量:

  1. 指令前缀,影响指令的行为 操作。
  2. 强制前缀用作 SSE 指令的操作码字节。
  3. 操作码字节,可以是一个或多个字节(最多 3 个完整字节)。
  4. ModR/M 字节是可选的,有时可能包含 操作码本身。
  5. SIB 字节是可选的,表示复杂的内存间接寻址 表格。
  6. 位移是可选的,它是一个不同大小的值 字节(字节,字,长)并用作 偏移量。
  7. 立即数是可选的,它用作构建的通用数字值 从不同大小的字节(字节, 字,长)。

格式如下:

/-------------------------------------------------------------------------------------------------------------------------------------------\
|*Prefixes | *Mandatory Prefix | *REX Prefix | Opcode Bytes | *ModR/M | *SIB | *Displacement (1,2 or 4 bytes) | *Immediate (1,2 or 4 bytes) |
\-------------------------------------------------------------------------------------------------------------------------------------------/
* means the element is optional.

数据结构和解码阶段在https://code.google.com/p/distorm/wiki/diStorm_Internals中解释

引用:

解码阶段

  1. [前缀]
  2. [获取操作码]
  3. [过滤操作码]
  4. [提取操作数]
  5. [文本格式]
  6. [十六进制转储]
  7. [解码指令]

每个步骤都有说明。


出于历史原因保留原始链接:

http://code.google.com/p/distorm/wiki/x86_x64_Machine_Codehttp://code.google.com/p/distorm/wiki/diStorm_Internals

【讨论】:

  • +1 这些是一些优秀的指针
  • “可选的强制前缀”看起来很有趣。
  • 链接好像失效了
【解决方案2】:

从一些已经组装好的小程序开始,它会为您提供生成的代码和指令。使用instruction architecture 为自己获取参考,并使用体系结构参考手动处理一些生成的代码。您会发现指令具有非常典型的 inst op op op 结构,其中操作数的数量各不相同。您需要做的就是翻译代码的十六进制或八进制表示以匹配指令;稍微玩一下就会发现。

该过程是自动化的,是反汇编程序的核心。理想情况下,您可能希望在内部(或在外部,如果程序真的很大)构建一个 n 指令结构数组。然后,您可以将该数组转换为汇编格式的指令。

【讨论】:

    【解决方案3】:

    检查 objdump 源 - 这是一个很棒的工具,它包含许多操作码表,它的源可以为制作您自己的反汇编程序提供一个很好的基础。

    【讨论】:

      【解决方案4】:

      看看80386 Programmer's Reference Manual 中的section 17.2。反汇编器实际上只是一个美化的finite-state machine。拆解步骤如下:

      1. 检查当前字节是否为指令前缀字节(F3F2F0);如果是这样,那么你有一个 REP/REPE/REPNE/LOCK 前缀。前进到下一个字节。
      2. 检查当前字节是否为地址大小字节 (67)。如果是这样,如果当前处于 32 位模式,则以 16 位模式解码指令其余部分中的地址,如果当前处于 16 位模式,则以 32 位模式解码地址
      3. 检查当前字节是否为操作数大小字节 (66)。如果是这样,如果当前处于 32 位模式,则以 16 位模式解码立即操作数,如果当前处于 16 位模式,则以 32 位模式解码立即操作数
      4. 检查当前字节是否为段覆盖字节(2E363E266465)。如果是这样,请使用相应的段寄存器来解码地址,而不是使用默认的段寄存器。
      5. 下一个字节是操作码。如果操作码为0F,则为扩展操作码,读取下一个字节作为扩展操作码。
      6. 根据特定的操作码,读入并解码 Mod R/M 字节、比例索引基 (SIB) 字节、位移(0、1、2 或 4 字节)和/或立即值 ( 0、1、2 或 4 个字节)。这些字段的大小取决于之前解码的操作码、地址大小覆盖和操作数大小覆盖。

      操作码告诉您正在执行的操作。操作码的参数可以从 Mod R/M、SIB、位移和立即值的值中解码。由于 x86 的复杂性,存在很多可能性和很多特殊情况。有关更详尽的说明,请参阅上面的链接。

      【讨论】:

      • 英特尔手册上写着 Groups 1 through 4 may be placed in any order relative to each other,所以步骤 1-4 可能不是这个顺序
      • 这适用于 x86,但不适用于 x86_64 或现代指令集,例如 AVX/AVX2
      【解决方案5】:

      你需要一个操作码表来加载。

      基本的查找数据结构是 trie,但是如果您不太关心速度,那么表就足够了。

      要获取基本操作码类型,请从表上的匹配开始。

      有几种解码寄存器参数的常用方法;但是,有足够多的特殊情况需要单独实施其中的大部分。

      由于这是教育性的,请查看 ndisasm。

      【讨论】:

        猜你喜欢
        • 2010-10-08
        • 2011-03-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-12-15
        • 2011-09-08
        • 2013-07-02
        相关资源
        最近更新 更多