Soot学习-Overview
记录一些关于Soot的最基础的知识。
Soot IRs
-
Baf: 一种基于栈的,以Bytecode形式呈现的中间表达
-
Jimple: Java的简单(Simple,不包含嵌套关系,且只有15种语句),带类型,无栈信息的,基于三位址码(Three Address Code)的中间表达,可放在控制流图中。每个三位址码都可被分解为一个四元组
由于其包含三个变量,故被称为三地址码 -
Shimple: SSA (Static Single Assignment)版本的Jimple。在SSA中,变量的每次赋值均对应一个新的变量(版本),而每次使用的变量均为该变量到达该程序点时的版本。例如,对于
1 | y := 1 // 多余的赋值 |
对于非SSA形式的编译器,需要通过数据流分析(到达-定义分析)来确定
1 | y1 := 1 |
便省去了数据流分析的过程
-
Grimp: 类似Jimple,不过表达式是聚合(aggregated)的
-
Dava: 用来反编译(Decompile)Java的结构化表达
Soot Structure
graph TD
s["Scene(singleton)"]
Root--> |"Scene.v()"|s --> |"getSootClass()"|SootClass
SootClass --> |"getField()"|SootField
SootField --> |"getSignature()"|SootField
SootClass --> |"getMethod()"|SootMethod
SootMethod --> |"getSignature()"|SootMethod
SootMethod --> |"getActiveBody()"|JimpleBody
在<IR>Body(如JimpleBody)中,代码语句(Statement)是用接口Unit表示的。该接口对每种IR均有对应的实现。Unit提供的API可用于向前或向后展开逐语句的分析(或插桩)。如
1 | public JMethod(SootMethod rawMethod) { |
Soot Phases
下图展示了Soot中不同pack(处理阶段)之间的工作方式。
graph LR
jb --> cg --> wjtp --> wjop --> wjap --> jtp
jtp --> jop --> jap --> bb --> tag
graph TD
A[Hard] -->|Text| B(Round)
B --> C{Decision}
C -->|One| D[Result 1]
C -->|Two| E[Result 2]
jb(Jimple Body Creation)
首先,Soot对每个方法体(Method Body)应用jb pack。诸如System.currentTimeMillis()等原生方法没有body。jb pack是固定的,与Jimple的创建有关,其无法被修改。
Whole-Program
接下来,Soot应用四个pack:
-
cg,即调用图(Call Graph)
-
wjtp,whole-jimple transformation pack
-
wjop,whole-jimple optimization pack
-
wjap,whole-jimple annotation pack
上述四个pack仅在指定-w(whole program)时才会加入到Soot中,且均可以被修改。特别地,每步后均可插入自定义的额外分析(插桩)步骤。例如,下面的代码在wjtp后追加一个额外的SceneTransformers:
1 | public static void main(String[] args) { |
Jimple Packs
类似地,对于每个方法体,Soot还提供了
-
jtp,Jimple transformation pack: 默认启用且为空。一般可将过程间分析逻辑(定义在一个继承自
BodyTransformer的自定义类中)置于此处。 -
jop,Jimple optimization pack: 对Jimple进行优化,默认关闭。可通过参数
-o或-p jop enabled开启。 -
jap,Jimple annotation pack: 对Jimple进行属性注解。下面的代码片段中,插入的逻辑将为方法中的每条语句打印标签:
1 | public static void main(String[] args) { |
bb pack用于将(优化后和注解后的)Jimple体转换为Baf体,而tag pack用于聚合重复标签。