(Raw/High) P-Code Ghidra使用的中间表示语言

简介

P-code是一种专为逆向工程而设计的寄存器传输语言。该语言通用性强,可以对不同处理器的行为进行建模,将对不同处理器的分析放入一个通用框架中,促进retargetable的分析算法和应用开发。

P-code的工作原理是将单个处理器指令转换为一组p-code操作,这些操作将处理器状态的一部分作为输入和输出变量(varnodes),这组唯一的p-code操作(和opcode区分)包含一组相当紧凑的由通用处理器执行的算术和逻辑操作。将指令直接转换为这些操作称为raw p-code。raw p-code可用于直接模拟指令执行,并且通常遵循相同的控制流,尽管它可能会添加一些自己的内部控制流。

P-code专门设计用于方便地构建数据流图,以便对反汇编后的指令进行后续分析。Varnodes和p-code操作符可以显式地视为这些图中的节点。生成raw p-code是图构造中必要的第一步,但还需要执行其他步骤,这会引入一些新的操作符。特别地其中两个新操作符MULTIEQUALINDIRECT用于图的构建过程,其他操作符可以在图的后续分析和转换过程中引入,并帮助保持恢复数据类型关系。最后,一些p-code操作CALL、CALLIND、RETURN可能会在分析期间更改其输入和输出varnode,导致它们不再和raw p-code形式匹配。

Ghidra的初始程序分析生成的p-code和varnode是原始(raw)的,因为仅用于表示指令的语义,很少或没有从高级语言中分析收集到的高级语义信息。Ghidra在反编译期间,p-code和varnodes被refined和关联到抽象的局部变量和源码级别的数据结构,称之为high p-code,因为它与 Ghidra中包含反编译信息的数据结构绑定,例如HighVariables和HighFunctions

Ghidra默认情况下显示的p-code可读性较好:
在这里插入图片描述
然而在script中获取和处理的时候是未经翻译的原始形式,显示raw p-code的方法:Edit - Tool Options - Listing Fields - Pcode Field - Display Raw Pcode,例子:
在这里插入图片描述
high p-code举例(script时应该叫PcodeOpAST):

(register, 0x20, 4) CALL (ram, 0x13438, 8) , (unique, 0x10000051, 4) , (const, 0x0, 4)
(ram, 0x1d3330, 4) INDIRECT (ram, 0x1d3330, 4) , (const, 0x24, 4)
(stack, 0xffffffffffffffdc, 4) INDIRECT (stack, 0xffffffffffffffdc, 4) , (const, 0x24, 4)
(unique, 0x10000051, 4) COPY (const, 0xfa5da8, 4)
(register, 0x64, 1) INT_SLESS (register, 0x20, 4) , (const, 0x0, 4)
 ---  CBRANCH (ram, 0x1d3318, 1) , (register, 0x64, 1)
 ---  CALL (ram, 0x136c0, 8) , (register, 0x20, 4) , (const, 0x0, 4) , (const, 0x0, 4)
(ram, 0x1d3330, 4) INDIRECT (ram, 0x1d3330, 4) , (const, 0x3a, 4)
(stack, 0xffffffffffffffdc, 4) INDIRECT (stack, 0xffffffffffffffdc, 4) , (const, 0x3a, 4)
(unique, 0x10000059, 4) INT_ADD (register, 0x20, 4) , (const, 0xd0, 4)
(register, 0x24, 4) CAST (unique, 0x10000059, 4)
(register, 0x20, 4) CALL (ram, 0x13d54, 8) , (register, 0x20, 4) , (register, 0x24, 4) , (const, 0x400, 4)
(ram, 0x1d3330, 4) INDIRECT (ram, 0x1d3330, 4) , (const, 0x4c, 4)
(stack, 0xffffffffffffffdc, 4) INDIRECT (stack, 0xffffffffffffffdc, 4) , (const, 0x4c, 4)
 ---  CALL (ram, 0x1216c, 8) , (register, 0x20, 4)

p-code的核心概念包括

地址空间(Address Space)

p-code的地址空间是RAM的泛化。简单的定义成可以被p-code操作读和写的可索引的字节序列。对于特定的字节,标记它的唯一索引是字节的地址。地址空间有一个名称用于识别它,一个大小表示空间中不同索引的数量,以及与之相关的endianess字节序表示整数或其他多字节的值如何编码到空间中。一个典型的处理器有一个RAM空间用于对可通过其主数据总线访问的内存进行建模,以及一个用于对处理器通用寄存器进行建模的寄存器空间(register space)。处理器操作的任何数据都必须在某个地址空间中。处理器的规范可以根据需要自由定义任意数量的地址空间。总是有一个特殊的地址空间,称为常量地址空间(const space),用于对p-code操作所需的任何常量值进行编码生成p-code的系统通常也使用专用的临时空间(temporary space),可以将其视为临时寄存器的无尽bottomless的源,这些地址空间用于在对指令行为建模时保存中间值

p-code的代码规范允许地址空间的可寻址单元大于一个字节。每个地址空间都有一个wordsize属性,可以设置该属性以指示一个单元中的字节数。大于1的wordsize对p-code的表示几乎没有影响。地址空间的所有偏移量仍在内部表示为字节偏移量。唯一的例外是 LOAD 和 STORE 操作,这些操作读取一个指针偏移量,当解引用指针时,该偏移量必须正确缩放以获得正确的字节偏移量。wordsize属性对任何其他 p-code操作都没有影响。

常见的地址空间包括CONST、RAM、UNIQUE、REGISTER、STACK,其定义在ghidra/program/model/address/AddressSpace.java

Varnode

varnode是寄存器或内存位置的概括,由三元组表示**<地址空间、偏移量、大小>**,直观地说,varnode是某个地址空间中的连续字节序列,可以被视为单个值。p-code的所有操作都发生在varnode上

Varnodes 本身只是一个连续的字节块,由地址和大小标识,没有类型。然而,p-code操作可以强制对 varnode 进行三种类型解释之一:整数、布尔值和浮点数。

  • 操作整数的操作总是使用与包含 varnode 的地址空间相关的字节序将 varnode 解释为二进制补码编码。
  • 用作布尔值的 varnode 被假定为单个字节,它只能取值 0,表示 false,1 表示 true。
  • 浮点运算使用被建模的处理器所期望的编码,这取决于 varnode 的大小。对于大多数处理器,这些编码由 IEEE 754 标准描述,但原则上其他编码也是可能的。

如果将 varnode 指定为常量地址空间的偏移量,则在使用该 varnode 的任何 p 代码操作中,该偏移量将被解释为常量或立即值。在这种情况下,varnode 的大小可以被视为可用于常量编码的大小或精度。与其他 varnode 一样,常量只有一种类型,由使用它们的 p 代码操作强制。

P-code操作(P-code Operation)

p 码操作类似于机器指令。所有 p 码操作在内部具有相同的基本格式。它们都将一个或多个 varnode 作为输入,并可选择生成单个输出 varnode。操作的动作由其操作码决定。对于几乎所有 p 代码操作,只有输出 varnode 可以修改其值;操作没有间接影响。唯一可能的例外是伪操作,参见“伪 P-CODE 操作”一节,当对指令行为的了解不完整时,有时需要使用伪操作。

所有 p 代码操作都与它们被翻译的原始处理器指令的地址相关联。对于单个指令,使用从零开始的加一计数器来枚举其翻译中涉及的多个 p 码操作。地址和计数器作为一对被称为 p 码操作的唯一序列号。 p 码操作的控制流通常遵循序列号顺序。当一条指令的所有p-code执行完成时,如果该指令具有fall-through语义,则p-code控制流从fall-through地址处的指令对应的顺序的第一个p-code操作开始。类似地,如果 p 代码操作导致控制流分支,则顺序中的第一个 p 代码操作在目标地址处执行。

可能的操作码列表类似于许多基于 RISC 的指令集。每个操作码的作用在后面的章节中有详细的描述,在“语法参考”一节中给出了一个参考表。通常,特定 p 码操作的大小或精度取决于 varnode 输入或输出的大小,而不是操作码。

HighFunction

HighFunction是反编译器生成的函数的特定信息的集合。HighFunction由下列对象组成:

  • 控制流表示基本块的基本功能
  • 数据流表示varnodes和p-code操作
  • 符号表函数访问的变量的符号表

HighSymbol

HighSymbol是反编译器恢复的显式符号之一,由名称和数据类型组成,可以描述为

  • 函数的参数
  • 函数的局部变量
  • 函数访问的全局变量

Varnodes

反编译器中的Varnode和p-code操作中的varnode不是一个东西。Varnode是反编译器的核心变量概念。Varnode表示反编译器生成的函数数据流表示中的各个结点。在分析的初始阶段,varnode仅表示特定的存储位置,这些位置由各个p-code操作按顺序访问。反编译器立即将p-code转换为基于图的数据流表示,称为静态单赋值形式SSA。在SSA中,varnode具有一些附加属性。

关于SSA可以参考南大《软件分析》课程IR章节
SSA,每个变量都有自己的唯一定义(def-use),有phi结点。
优点:唯一的变量名可以间接体现程序流信息,简化分析过程;def和use都是显式的
缺点:拆解出太多的变量和phi结点;转换回字节码存在性能问题,引入很多拷贝操作。

在SSA形式中,对存储未知的每次写入操作都会定义一个新的varnode。将代码中不同位置的操作写入同一个存储位置,仍然会产生不同的varnode。在这种情况下,每个varnode在函数内部都有一个生命周期或范围,开始于:

  • 定义了变量的(断句)p-code操作的(断句)输出varnode。
  • 如果varnode是函数的输入,则开始于函数起始位置

varnode的范围通过控制流扩展到读取了特定的varnode作为操作数的每个p-code操作定义p-code操作读取操作之间varnode的值不会改变。varnode的范围可以被认为是函数体中通过控制流连接的一组地址。定义了变量的p-code操作的地址称为varnode的first use point或first use offset

在高级语言比如C和java的反编译器输出中,一个varnode也具有范围,并且仅在代码的这个连通区域中表示高级语言中的变量。一组不相交的范围的varnode提供了对可以在函数多个位置写入的高级变量的完整描述。

HighVariable

HighVariable是一组varnodes,合在一起表示在反编译器输出的高级语言中整个变量的存储。每个varnode都描述了变量值在某些代码段中的存储位置。

HighVariables 和 HighSymbols 之间一般是一一对应的。 HighVariables 可以被认为是高级变量的详细存储描述,而 HighSymbol 提供了它的名称和数据类型。

HighVariable 总是描述函数中的指令对数据的显式操作。在某些情况下,HighVariable 可能只描述 HighSymbol 的部分存储。特别是对于结构化或复合数据类型,函数可能在代码的不同点对变量的不同部分进行操作,因此 HighVariable 可能只包含结构的一个字段。

一个符号可以在一个函数中被引用,但是这个符号的值可能没有被显式地操作。常量指针可以引用堆栈或主存储器中的变量,但变量的值在函数内既不读取也不写入。在这种情况下,HighSymbol 存在,但没有对应的 HighVariable。

merging

合并(merging)是分析过程的一部分,反编译器决定将哪些varnode组合在一起以在输出中创建最终的HighVariable。每个 varnode 的作用域(参见反编译器中 Varnodes 中的讨论)提供了对此过程的基本限制。如果两个 varnode 的作用域相交,则不能合并它们。但这在哪些 varnode 可以合并方面留下了很大的余地。

某些 varnode 必须合并;例如,如果它们使用相同的存储但在不同的控制流路径中,或者如果明确知道 varnodes 必须表示相同的变量。这称为强制合并。

反编译器还可以合并可以作为单独变量轻松存在的 varnode。这称为投机合并。除了 varnode 作用域上的交集条件外,反编译器仅推测性地合并共享相同数据类型的变量。除此之外,反编译器会优先考虑在同一指令中读取和写入的变量对,然后是函数控制流中彼此靠近的对。在有限的范围内,用户能够控制这种合并(请参阅拆分为新变量)。

Prototype Model

略。

参考

文档目录$ghidra_home/docs/languages/html/pcoderef.html
opcodes的子集可以出现在raw p-code中的被描述在P-Code Operation Reference(docs/languages/html/pcodedescription.html)和Pseudo P-CODE Operations(docs/languages/html/pseudo-ops.html)构成了本文档的大部分内容。
所有新的操作码都在Additional P-CODE Operations(docs/languages/html/additionalpcode.html)一节中进行了描述,这些操作码都不能在原始原始 p 代码转换中发生。

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

苏打呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值