#arm64 #json-file #reverse-engineering #disassembler #disassembly #arm-v8

bin+lib disarm64

disarm64 提供解码 ARM64 指令的工具和库(在开发用的笔记本电脑上,解码速度至少为 250 MiB/sec)。在这里,您还可以找到一个从 JSON 文件生成反汇编器/指令解码器表的实用程序。除此之外,还可以将指令解码以树形结构进行可视化。

21 个版本

0.1.20 2024 年 7 月 14 日
0.1.19 2024 年 6 月 3 日
0.1.17 2024 年 5 月 28 日
0.1.15 2024 年 4 月 28 日
0.1.6 2024 年 2 月 26 日

#118 in 开发工具

Download history 162/week @ 2024-04-25 150/week @ 2024-05-02 17/week @ 2024-05-09 1/week @ 2024-05-16 141/week @ 2024-05-23 245/week @ 2024-05-30 64/week @ 2024-06-06 11/week @ 2024-06-13 3/week @ 2024-06-20 2/week @ 2024-06-27 31/week @ 2024-07-04 126/week @ 2024-07-11 9/week @ 2024-07-18 108/week @ 2024-07-25 26/week @ 2024-08-01

144 每月下载量
用于 wechat-multi

Unlicense OR MIT

11MB
309K SLoC

指令解码器生成器

关于此项目

本项目包含一个库和工具,可以从 ISA 的某些标准描述中生成基于决策树的指令解码器。对于反汇编器、调试器、模拟器、VMM 和其他类似工具,高效且精确的指令解码是一个相当常见的目标。

要解码的指令越多,任务就越繁琐,错误的可能性就越大。因此,人们可能会考虑从可机器读取的描述中 生成 解码器。

ISA 描述从 JSON 文件中读取(仓库中有一个示例:aarch64.json,有超过 3,000 条指令可以操作),算法假设固定长度的 32 位编码。该文件由 opcodes-lab 仓库中的工具生成。生成的解码器可以通过条件语句(默认)、DFS 表或 BFS 表驱动。在大小和速度之间有一个权衡,可以找到最适合他们的方案。

默认选项在开发用的笔记本电脑上至少提供 250 MiB/sec 的解码速度。

在指令被识别(解码)后,可能需要将其格式化为某种字符串。目前正在添加类似于反汇编器使用的指令格式,预计将在版本 0.2.0 中实现。详细的进展情况总结如下 这里。高级状态是,与 LLVM 和 binutils 匹配的格式化工作适用于以下组中的指令

  • 算术
  • 数据处理(一些别名可能尚未使用,使用基本指令形式)
  • 逻辑
  • 位操作(一些别名可能尚未使用,使用基本指令形式)
  • 分支(无条件、条件、测试位和分支)
  • 条件
  • 异常
  • 加载/存储
  • 移动
  • PC 相对
  • 系统指令(提示、系统寄存器)
  • 浮点
  • SIMD, SVE
  • SME

当前的工作重点是提供对指令操作数进行结构匹配的能力。

安装工具

cargo install disarm64

作为库使用

cargo add disarm64
cargo add disarm64_defn

在你的酷代码中,可能可以从以下代码片段开始

use disarm64::decoder;
use disarm64_defn::defn::InsnOpcode;

fn neato() {
    let insn = decoder::decode(0x11000000).unwrap();

    println!("Instruction: {insn:?}");
    println!("Formatted: {insn}");
    println!("Definition: {:?}", insn.definition());
}

默认情况下,库构建为支持完整的已知指令集。还有一些子集可用,作为 crate 功能以节省编译时间和代码大小

# Other features are `exception` and `system` 
disarm64 = { version = "0.1", default_features = false, features = ["load_store"] }

使用工具

解码器

解码器可以从命令行、平面二进制文件、ELF 文件、PE 文件或 Mach-O 文件解码 ARM64 指令

disarm64 --help
Usage: disarm64 [OPTIONS] <COMMAND>

Commands:
  insn    Instructions to decode (hex 32-bit), can specify multiple instructions separated by commas
  bin     Flat binary file with instructions to decode, can specify offset and count
  elf     ELF file with instructions to decode
  mach-o  Mach-O file with instructions to decode
  pe      PE file with instructions to decode
  help    Print this message or the help of the given subcommand(s)

Options:
  -v...       Log level/verbosity; repeat (-v, -vv, ...) to increase the verbosity
  --benchmark <BENCHMARK>  Benchmark mode: measure the time to decode the instructions.
                           This is not a synthetic benchmark, it provides an estimate
                           of the real-world performance
                           [possible values: decode, format]
  -h, --help  Print help

生成器

此工具可以从 ISA 的 JSON 描述生成指令解码器。

disarm64_gen --help
Usage: disarm64_gen [OPTIONS] <DESCRIPTION_JSON>

Arguments:
  <DESCRIPTION_JSON>
          A JSON file with the description of the instruction set architecture

Options:
  -a, --algo <ALGO>
          Decoder algorithm style, defaults to conditionals

          Possible values:
          - cond: Conditionals
          - dfs:  DFS table-driven
          - bfs:  BFS table-driven

  -f, --feature-sets <FEATURE_SETS>...
          Include filter for feature sets, e.g. "v8,simd". Case-insensitive, ignored if not provided

  -c, --insn-class <INSN_CLASS>...
          Include filter for instruction classes, e.g. "addsub_imm,ldst_pos,exception". Case-insensitive, ignored if not provided

  -m, --mnemonic <MNEMONIC>...
          Include filter for mnemonics, e.g. "adc,ldp". Case-insensitive, ignored if not provided

  -g, --graphviz <GRAPHVIZ>
          Output the decision tree to a Graphviz DOT file

  -r, --rs-file <RS_FILE>
          Generate the decoder implemented in Rust

  -t, --test-bin <TEST_BIN>
          Generate a test binary

      --test-bin-size-limit <TEST_BIN_SIZE_LIMIT>
          The size limit of the generated test binary, the default is 64MB
          
          [default: 67108864]

      --test-encodings-limit <TEST_ENCODINGS_LIMIT>
          The number of test encodings to generate for each instruction, the default is 0x10_000
          
          [default: 65536]

  -v...
          Log level/verbosity; repeat (-v, -vv, ...) to increase the verbosity

  -h, --help
          Print help (see a summary with '-h')

指令类和功能集

要了解 ISA 描述中可用的类和功能集,请运行

disarm64_gen ./aarch64.json -c -

disarm64_gen ./aarch64.json -f -

示例

生成 Rust 实现的解码器

对于整个已知指令集

disarm64_gen ./aarch64.json -r decoder.rs

如果只需要解码整个指令集的子集,请适当使用过滤器。例如,要生成 V8 加载/存储指令的解码器,请执行

disarm64_gen ./aarch64.json -c ldst_imm10,ldst_imm9,ldst_pos,ldst_regoff,ldst_unpriv,ldst_unscaled,ldstexcl,ldstnapair_offs,ldstpair_indexed,ldstpair_off,loadlit -f v8 -r decoder.rs

使用解码器

解码命令行上的指令

disarm64 -v insn 0x1a000001,0xa,0xa,0xa,0xa
[DEBUG] Decoded instruction: ADDSUB_CARRY(ADC_Rd_Rn_Rm(ADC_Rd_Rn_Rm { rd: 00000001, rn: 00000000, rm: 00000000 }))
[DEBUG] 0x1a000001: Insn { mnemonic: "adc", aliases: [], opcode: 1a000000, mask: 7fe0fc00, class: ADDSUB_CARRY, feature_set: V8, operands: [InsnOperand { kind: Rd, class: INT_REG, qualifiers: [W, X], bit_fields: [BitfieldSpec { bitfield: Rd, lsb: 00000000, width: 00000005 }] }, InsnOperand { kind: Rn, class: INT_REG, qualifiers: [W, X], bit_fields: [BitfieldSpec { bitfield: Rn, lsb: 00000005, width: 00000005 }] }, InsnOperand { kind: Rm, class: INT_REG, qualifiers: [W, X], bit_fields: [BitfieldSpec { bitfield: Rm, lsb: 00000010, width: 00000005 }] }], flags: InsnFlags(HAS_SF_FIELD) }
[INFO ] 0x000000: 1a000001      adc     w1, w0, w0
[DEBUG] Decoded instruction: EXCEPTION(UDF_UNDEFINED(UDF_UNDEFINED { imm16_0: 0000000a }))
[DEBUG] 0x00000a: Insn { mnemonic: "udf", aliases: [], opcode: 00000000, mask: ffff0000, class: EXCEPTION, feature_set: V8, operands: [InsnOperand { kind: UNDEFINED, class: IMMEDIATE, qualifiers: [], bit_fields: [BitfieldSpec { bitfield: imm16_0, lsb: 00000000, width: 00000010 }] }], flags: InsnFlags(0x0) }
[INFO ] 0x000004: 0000000a      udf     #0xa
[DEBUG] Decoded instruction: EXCEPTION(UDF_UNDEFINED(UDF_UNDEFINED { imm16_0: 0000000a }))
[DEBUG] 0x00000a: Insn { mnemonic: "udf", aliases: [], opcode: 00000000, mask: ffff0000, class: EXCEPTION, feature_set: V8, operands: [InsnOperand { kind: UNDEFINED, class: IMMEDIATE, qualifiers: [], bit_fields: [BitfieldSpec { bitfield: imm16_0, lsb: 00000000, width: 00000010 }] }], flags: InsnFlags(0x0) }
[INFO ] 0x000008: 0000000a      udf     #0xa
[DEBUG] Decoded instruction: EXCEPTION(UDF_UNDEFINED(UDF_UNDEFINED { imm16_0: 0000000a }))
[DEBUG] 0x00000a: Insn { mnemonic: "udf", aliases: [], opcode: 00000000, mask: ffff0000, class: EXCEPTION, feature_set: V8, operands: [InsnOperand { kind: UNDEFINED, class: IMMEDIATE, qualifiers: [], bit_fields: [BitfieldSpec { bitfield: imm16_0, lsb: 00000000, width: 00000010 }] }], flags: InsnFlags(0x0) }
[INFO ] 0x00000c: 0000000a      udf     #0xa
[DEBUG] Decoded instruction: EXCEPTION(UDF_UNDEFINED(UDF_UNDEFINED { imm16_0: 0000000a }))
[DEBUG] 0x00000a: Insn { mnemonic: "udf", aliases: [], opcode: 00000000, mask: ffff0000, class: EXCEPTION, feature_set: V8, operands: [InsnOperand { kind: UNDEFINED, class: IMMEDIATE, qualifiers: [], bit_fields: [BitfieldSpec { bitfield: imm16_0, lsb: 00000000, width: 00000010 }] }], flags: InsnFlags(0x0) }
[INFO ] 0x000010: 0000000a      udf     #0xa

可视化决策树

disarm64_gen ./aarch64.json -c exception -g dt-exception.dot
disarm64_gen ./aarch64.json -f v8 -g dt-v8.dot
disarm64_gen ./aarch64.json -c ic_system -g dt-system.dot
disarm64_gen ./aarch64.json -c ldst_imm10,ldst_imm9,ldst_pos,ldst_regoff,ldst_unpriv,ldst_unscaled,ldstexcl,ldstnapair_offs,ldstpair_indexed,ldstpair_off,loadlit -f v8 -g dt-ldst.dot

然后(假设已安装 Graphviz 工具)

dot -Tpng dt-exception.dot -o dt-exception.png
dot -Tpng dt-v8.dot -o dt-v8.png
dot -Tpng dt-system.dot -o dt-system.png
dot -Tpng dt-ldst.dot -o dt-ldst.png

dot 文件渲染为 png 图像。圆圈中的数字表示要检查的位;在矩形中,有要检查的指令和操作码。

示例(Aarch64)

调试输出

disarm64_gen ./aarch64.json -c ldst_pos -m ldr -v
[INFO ] Loading "./aarch64.json"
[INFO ] Including instructions from all feature sets
[INFO ] Including instructions from classes {LDST_POS}
[INFO ] Including instructions with mnemonics {"ldr"}
[DEBUG] instruction Insn { mnemonic: "ldr", opcode: 3d400000, mask: 3f400000, class: LDST_POS, feature_set: V8, operands: {ADDR_UIMM12: InsnOperand { class: ADDRESS, qualifiers: [S_B], bit_fields: [BitfieldSpec { bitfield: Rn, lsb: 5, width: 5 }, BitfieldSpec { bitfield: imm12, lsb: a, width: c }] }, Ft: InsnOperand { class: FP_REG, qualifiers: [S_B], bit_fields: [BitfieldSpec { bitfield: Rt, lsb: 0, width: 5 }] }}, flags: InsnFlags(0x0), index: 37d }
[DEBUG] instruction Insn { mnemonic: "ldr", opcode: b9400000, mask: bfc00000, class: LDST_POS, feature_set: V8, operands: {Rt: InsnOperand { class: INT_REG, qualifiers: [W], bit_fields: [BitfieldSpec { bitfield: Rt, lsb: 0, width: 5 }] }, ADDR_UIMM12: InsnOperand { class: ADDRESS, qualifiers: [S_S], bit_fields: [BitfieldSpec { bitfield: Rn, lsb: 5, width: 5 }, BitfieldSpec { bitfield: imm12, lsb: a, width: c }] }}, flags: InsnFlags(HAS_ADVSIMV_GPRSIZE_IN_Q), index: 382 }
[DEBUG] Classes {LDST_POS}
[DEBUG] Feature sets {V8}
[INFO ] Processed 3323 instructions, skipped 200 aliases, 1 classes, 1 feature sets filtered out 3321 instructions
[INFO ] Loaded 2 instructions
[DEBUG] Building decision tree at depth 1
[DEBUG] mask: 3f400000
[DEBUG] decision bit: 22
[DEBUG] decision mask: 400000
[DEBUG] zero: 0, one: 2
[DEBUG] mask: 3f000000
[DEBUG] decision bit: 24
[DEBUG] decision mask: 1000000
[DEBUG] zero: 0, one: 2
[DEBUG] mask: 3e000000
[DEBUG] decision bit: 25
[DEBUG] decision mask: 2000000
[DEBUG] zero: 2, one: 0
[DEBUG] mask: 3c000000
[DEBUG] decision bit: 26
[DEBUG] decision mask: 4000000
[DEBUG] zero: 1, one: 1
[DEBUG] Building decision tree at depth 2
[DEBUG] One instruction at depth 1
[DEBUG] Building decision tree at depth 2
[DEBUG] One instruction at depth 1
[DEBUG] Decision tree built at depth 0

测试

有关 disarm64 与 LLVM 以及 diasrm64 与 binutils 之间的测试夹具和反汇编差异,请参阅 disarm64_test_data

ARM A64 ISA 参考材料

此项目没有任何声名鹊起之处。它使用众所周知的算法和方法来生成指令解码器和反汇编器,似乎只有一些微小的变化:从 JSON 文件读取 ISA 描述,并生成无指针、无不安全块和无内存分配的强类型 Rust 解码器。也许,您会喜欢生成指令集的一部分解码器的功能,这可以使代码大小更小。

以下是其他涉及机器指令解码主题的项目

上述内容具有更广泛的范围,其中一些提供各种语言的绑定。

不是以库/API为中心,却是唯一的

尽管只针对x86_64,但仍然非常出色

关于此主题及其密切相关主题的一些论文

  • C.S. Collberg. "逆向解释 + 变异分析 = 自动重定位",ACM SIG-PLAN 1997年编程语言设计与实现会议论文集", 1997年,第57-70页。
  • C.S. Collberg. "自动推导编译器机器描述",ACM Trans. Program. Lang. Syst.,2002年,第24卷,第4期,第369-408页。
  • W.C. Hsieh,D.R. Engler,G. Back. "逆向工程指令编码",通用轨道会议记录:2002年USENIX年度技术会议",2001年,第133-145页。
  • R. Krishna,T. Austin. "高效的软件解码器设计",IEEE计算机学会计算机体系结构技术委员会通讯,2001年。
  • H. Theiling. "为解码二进制生成决策树",ACM SIGPLAN嵌入式系统语言、编译器和工具研讨会论文集",2001年,第112-120页。
  • W. Qin,S. Malik. "为可重定位软件工具包自动合成高效的二进制解码器",第40届年度设计自动化会议论文集",2003年,第764-769页。

所有商标均为各自所有者的财产。本项目的内内容可在MIT许可证的条款下使用。MIT许可证

依赖项

~3.5–4.5MB
~85K SLoC