处理器规范语言(也称为 SLEIGH)是 Ghidra 用来定义处理器架构和指令集的领域特定语言。这种语言使 Ghidra 能够理解如何反汇编和分析各种硬件平台的二进制代码。
有关 ARM 或 x86 等特定处理器模块的信息,请参阅处理器架构支持及其子页面。
SLEIGH 规范定义了机器码二进制模式如何映射到人类可读的汇编语言以及 Ghidra 内部称为 P-code 的中间表示。这些规范使 Ghidra 能够解码、显示和分析不同处理器架构的指令。
来源
SLEIGH 中的处理器规范包含几个关键组件:
来源
空间定义声明了处理器的地址空间,包括内存(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 操作。每个构造器都包含:
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 是 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; |
来源
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 允许在单个规范或多个文件中定义处理器架构的多个变体。这通过以下方式实现:
@ifdef 根据定义的变量包含/排除部分这使得 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文件,它们包含规范的模块化组件。
来源
创建新的处理器规范通常涉及:
来源
处理器规范语言(SLEIGH)是一种强大的领域特定语言,它使 Ghidra 能够支持广泛的处理器架构。通过其模式匹配能力和 P-code 语义,它提供了一个灵活的系统来定义指令如何被解码、显示和进行语义解释。
理解 SLEIGH 对于扩展 Ghidra 以支持新的处理器架构或变体至关重要,也是理解 Ghidra 的反汇编和反编译过程如何在基本层面工作的基础。