菜单

处理器规范语言

相关源文件

处理器规范语言(也称为 SLEIGH)是 Ghidra 用来定义处理器架构和指令集的领域特定语言。这种语言使 Ghidra 能够理解如何反汇编和分析各种硬件平台的二进制代码。

有关 ARM 或 x86 等特定处理器模块的信息,请参阅处理器架构支持及其子页面。

概述

SLEIGH 规范定义了机器码二进制模式如何映射到人类可读的汇编语言以及 Ghidra 内部称为 P-code 的中间表示。这些规范使 Ghidra 能够解码、显示和分析不同处理器架构的指令。

来源

语言规范结构

SLEIGH 中的处理器规范包含几个关键组件:

  1. 空间定义 - 定义地址空间(内存、寄存器)
  2. 寄存器定义 - 声明处理器寄存器及其组织方式
  3. 令牌定义 - 指定指令编码位模式
  4. 上下文定义 - 定义影响指令解码的处理器状态
  5. 构造器模式 - 将位模式映射到汇编语言语法和 P-code 语义

来源

空间定义

空间定义声明了处理器的地址空间,包括内存(RAM)和寄存器空间。每个空间都有特定的尺寸和属性。

以下是 x86 的空间定义示例:

define space ram type=ram_space size=$(SIZE) default;
define space register type=register_space size=4;

这创建了两个空间:一个 RAM 空间,其大小由$(SIZE)变量决定,以及一个 4 字节大小的寄存器空间。

来源

寄存器定义

寄存器定义指定了处理器的寄存器、它们的大小以及它们在寄存器空间中的组织方式。寄存器定义可以包含重叠的寄存器(例如,32 位 EAX 包含 16 位 AX)。

x86 示例:

define register offset=0 size=4 [ EAX ECX EDX EBX ESP EBP ESI EDI ];
define register offset=0 size=2 [ AX CX DX BX SP BP SI DI ];
define register offset=0 size=1 [ AL CL DL BL AH CH DH BH ];

这定义了三组寄存器,它们位于相同的偏移量但大小不同,代表了 x86 中重叠的寄存器结构。

来源

令牌定义

令牌定义描述了如何将指令字节解析为逻辑组件。令牌指定了指令字节中的位字段,这些位字段对应于指令的不同部分,例如操作码、寄存器说明符和立即值。

ARM 示例:

define token instrArm (32)
    cond=(28,31)
    I25=(25,25)
    P24=(24,24)
    op=(21,24)
    Rn=(16,19)
    Rd=(12,15)
    immed12=(8,19)
    Rm=(0,3)
    ...
;

这定义了 32 位 ARM 指令中的各种位字段,例如条件字段(位 28-31)和寄存器字段。

来源

上下文定义

上下文定义指定了影响指令解码的处理器状态。这可以包括模式位、指令集选择或可以改变指令字节解释的其他状态。

ARM 示例:

define context contextreg
    TMode = (0,0)    # 1 if in Thumb instruction decode mode
    T = (0,0)        # exact copy (alias!) of TMode
    ...
;

此上下文定义了一个位,指示处理器是处于 Thumb 模式还是 ARM 模式,这会影响指令的解码方式。

来源

构造器模式

构造器模式是 SLEIGH 规范的核心。它们将位模式(在令牌中定义)映射到汇编语言语法和 P-code 操作。每个构造器都包含:

  1. 一个指定要匹配的指令位模式的模式匹配部分
  2. 一个定义指令如何显示的汇编语法显示部分
  3. 一个(用大括号括起来的)语义部分,定义 P-code 操作

ARM 示例:

:add^COND Rd,Rn,#imm12 is $(AMODE) & cond=COND & op=0b0100 & S=0 & Rn & Rd & imm12
{
    Rd = Rn + imm12;
}

此模式定义了一个带有立即值的 ADD 指令,展示了它应如何显示以及其语义效果(将立即值添加到 Rn 并存储在 Rd 中)。

来源

表格

SLEIGH 支持使用表来处理复杂的指令编码模式。表允许将相似的指令分组并共享共同的属性。

示例

@define ARITH1 "ADD SUB AND OR XOR"
@define ARITH2 "MOV CMP TEST"

:$(ARITH1) rm8, r8  is op1=4 & op2=0 & mod=3 & reg=r8 & rm=rm8 & repne=0 & opcode=0 & ARITH1[ opcode=0 ]  { rm8 = r8 + rm8; }
:$(ARITH1) rm16, r16 is op1=4 & op2=1 & mod=3 & reg=r16 & rm=rm16 & repne=0 & opcode=0 & ARITH1[ opcode=0 ] { rm16 = r16 + rm16; }

来源

P-code 操作

P-code 是 Ghidra 用于指令语义的中间表示。SLEIGH 允许使用 P-code 操作定义语义,这些操作能够精确捕获每条指令对处理器状态的影响。

常见的 P-code 操作包括:

操作描述示例
=作业r0 = r1 + r2;
+, -, *, /算术操作r0 = r1 + 5;
&, |, ^逻辑操作r0 = r1 & 0xFF;
<<, >>移位操作r0 = r1 << 2;
*[...]内存访问r0 = *[ram]:4 0x1000;
zext, sext零/符号扩展r0 = zext(r1);
f+, f-, f*浮点操作d0 = d1 f+ d2;

来源

自定义 P-code 操作

SLEIGH 允许为无法轻易用内置操作表达的复杂处理器行为定义自定义 P-code 操作。这些操作使用pcodeop关键字声明。

示例

define pcodeop AESSubBytes;
define pcodeop AESShiftRows;
define pcodeop AESMixColumns;

这些操作随后可以在指令语义中使用,以表示由 Ghidra 处理其实现细节的复杂操作。

来源

SLEIGH 支持使用宏进行代码复用。宏使用macro关键字定义,并可用于抽象常见的代码模式。

示例

macro addflags(op1,op2) {
 tmpCY = carry(op1,op2);
 tmpOV = scarry(op1,op2);
}

此宏封装了在加法操作后设置条件标志的常见任务。

来源

条件编译

SLEIGH 支持使用@ifdef@ifndef 和相关指令进行条件编译。这允许根据定义的变量包含或排除规范的某些部分。

示例

@ifdef IA64
define register offset=0 size=8 [ RAX RCX RDX RBX RSP RBP RSI RDI ];
@else
define register offset=0 size=4 [ EAX ECX EDX EBX ESP EBP ESI EDI ];
@endif

这允许同一个规范文件处理架构的多个变体,例如 32 位和 64 位 x86。

来源

处理器变体处理

SLEIGH 允许在单个规范或多个文件中定义处理器架构的多个变体。这通过以下方式实现:

  1. 条件编译 - 使用@ifdef 根据定义的变量包含/排除部分
  2. 上下文变量 - 使用上下文位在指令集或模式之间切换
  3. 包含文件 - 将规范分解为模块化组件

这使得 Ghidra 能够支持 ARM(ARM、Thumb、ARM64)和 x86(16 位、32 位、64 位)等架构的多个变体。

来源

文件组织

处理器规范通常组织成以下几种文件类型:

文件扩展名目的示例
.slaspec主处理器规范x86.slaspec
.sinc规范模块的包含文件ia.sinc, avx.sinc
.ldefs语言定义元数据x86.ldefs, ARM.ldefs
.pspec处理器特定行为x86.pspec
.cspec编译器特定行为x86gcc.cspec
.sla已编译规范(二进制)x86.sla

.slaspec文件通常包含各种.sinc文件,它们包含规范的模块化组件。

来源

创建新处理器规范的工作流程

创建新的处理器规范通常涉及:

  1. 研究处理器的指令集架构文档
  2. 识别寄存器集和内存模型
  3. 定义基本的指令编码模式
  4. 使用 P-code 实现指令语义
  5. 使用示例二进制文件进行测试
  6. 迭代地完善规范

来源

结论

处理器规范语言(SLEIGH)是一种强大的领域特定语言,它使 Ghidra 能够支持广泛的处理器架构。通过其模式匹配能力和 P-code 语义,它提供了一个灵活的系统来定义指令如何被解码、显示和进行语义解释。

理解 SLEIGH 对于扩展 Ghidra 以支持新的处理器架构或变体至关重要,也是理解 Ghidra 的反汇编和反编译过程如何在基本层面工作的基础。