热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

(Raw/High)PCodeGhidra使用的中间表示语言

简介P-code是一种专为逆向工程而设计的寄存器传输语言。该语言通用性强,可以对不同处理器的行为进行建模,将对不同处理器的分析放入一个通用框架中&#x
简介

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

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

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

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

P-code操作&#xff08;P-code Operation&#xff09;

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

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

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

HighFunction

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

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

HighSymbol

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

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

Varnodes

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

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

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

  • 定义了变量的&#xff08;断句&#xff09;p-code操作的&#xff08;断句&#xff09;输出varnode。
  • 如果varnode是函数的输入&#xff0c;则开始于函数起始位置

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

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

HighVariable

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

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

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

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

merging

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

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

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

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&#xff08;docs/languages/html/additionalpcode.html&#xff09;一节中进行了描述&#xff0c;这些操作码都不能在原始原始 p 代码转换中发生。


推荐阅读
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • 本文探讨了C语言中指针的应用与价值,指针在C语言中具有灵活性和可变性,通过指针可以操作系统内存和控制外部I/O端口。文章介绍了指针变量和指针的指向变量的含义和用法,以及判断变量数据类型和指向变量或成员变量的类型的方法。还讨论了指针访问数组元素和下标法数组元素的等价关系,以及指针作为函数参数可以改变主调函数变量的值的特点。此外,文章还提到了指针在动态存储分配、链表创建和相关操作中的应用,以及类成员指针与外部变量的区分方法。通过本文的阐述,读者可以更好地理解和应用C语言中的指针。 ... [详细]
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • 本文介绍了RPC框架Thrift的安装环境变量配置与第一个实例,讲解了RPC的概念以及如何解决跨语言、c++客户端、web服务端、远程调用等需求。Thrift开发方便上手快,性能和稳定性也不错,适合初学者学习和使用。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • Android源码深入理解JNI技术的概述和应用
    本文介绍了Android源码中的JNI技术,包括概述和应用。JNI是Java Native Interface的缩写,是一种技术,可以实现Java程序调用Native语言写的函数,以及Native程序调用Java层的函数。在Android平台上,JNI充当了连接Java世界和Native世界的桥梁。本文通过分析Android源码中的相关文件和位置,深入探讨了JNI技术在Android开发中的重要性和应用场景。 ... [详细]
  • 3.223.28周学习总结中的贪心作业收获及困惑
    本文是对3.223.28周学习总结中的贪心作业进行总结,作者在解题过程中参考了他人的代码,但前提是要先理解题目并有解题思路。作者分享了自己在贪心作业中的收获,同时提到了一道让他困惑的题目,即input details部分引发的疑惑。 ... [详细]
  • 本文由编程笔记小编整理,主要介绍了使用Junit和黄瓜进行自动化测试中步骤缺失的问题。文章首先介绍了使用cucumber和Junit创建Runner类的代码,然后详细说明了黄瓜功能中的步骤和Steps类的实现。本文对于需要使用Junit和黄瓜进行自动化测试的开发者具有一定的参考价值。摘要长度:187字。 ... [详细]
author-avatar
金瑞期货秦臻
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有