最近想用ghidra分析一些东西,学下ghidra的PCode
PCode
ghidra为了分析多个平台的二进制文件,需要一种架构无关的IR来执行分析,于是ghidra官方提出了PCode针对寄存器内容转移的IR来使得不同的架构可以使用同一架构来分析。
ghidra PCode基本原来为将每条指令翻译为PCode指令序列,将上下文状态的一部分作为输入然后产生输出(有些指令并没有输出),将指令直接翻译的PCode序列被称作raw PCode,raw PCode可以用于直接模拟指令执行,在模拟指令执行的过程中会按照原来的控制流并加上PCode内部的控制流。
PCode的设计初衷为简化在之后反汇编指令中用到的数据流图的构建。varnodes和pcode运算符可以当作是这个图上的节点。在构建数据流图的过程中,第一步一定是raw pcode的翻译,之后会有一些特殊的优化过程,在这个过程中ghidra加入了其他指令,如MULTIEQUAL、INDIRECT等指令,这些指令我将在本文的后半部分讲解。
Address Space
在ghidra的抽象中地址空间就是一个可索引的字节序列,包括Register、ram、constant、temporary等
constant负责编码固定的数值
temporary可以被看作一个很大的临时寄存器,负责记录转换指令到pcode时的立即数
Varnode
一个varnode代表一块寄存器位置或者内存位置,他由三元组形式来表示1
Varnode: (ram/const/register/other, val, len)
简单来说一个varnode就是把一个连续的字节序列转化为单个值,pcode中所有的数据操作都发生在varnodes上
如果一个varnode为constant地址空间中的值,则它的offset将被视为立即数,即我们通过getOffset方法获得的数字即为constant varnode的值1
(unique, 0x10000381, 4) COPY (const, 0x134b4, 4)
COPY的input0 即为const varnode,可以使用isConstant方法来判断,getOffset方法来获得constant varnode的值
PCode operation
COPY
Parameters | Description |
---|---|
input0 | Source varnode. |
output | Destination varnode. |
将input0的内容拷贝任意字节到output,input0和output的size必须相同
LOAD
Parameters | Description |
---|---|
input0 (special) | Constant ID of space to load from. |
input1 | Varnode containing pointer offset to data. |
output | Destination varnode. |
1 | output = *input1; |
input0的ID是address space的ID由PCode的编译器生成,input1 是对应address space的地址偏移,从对应地址空间的地址偏移处取出值放到output的位置,output的size可以大于input的size,但是output的size必须是inputsize的整数倍
input0的size必须和input1的size相等
STORE
和LOAD相似
BRANCH
raw PCode 提取
1 | def dump_raw_pcode(func): |
refined PCode 提取
1 | from ghidra.util.task import ConsoleTaskMonitor |