Soot学习-Overview

记录一些关于Soot的最基础的知识。

Soot IRs

  • Baf: 一种基于栈的,以Bytecode形式呈现的中间表达

  • Jimple: Java的简单(Simple,不包含嵌套关系,且只有15种语句),带类型,无栈信息的,基于三位址码(Three Address Code)的中间表达,可放在控制流图中。每个三位址码都可被分解为一个四元组 \((operate, exp1, exp2, result)\) 由于其包含三个变量,故被称为三地址码

  • Shimple: SSA (Static Single Assignment)版本的Jimple。在SSA中,变量的每次赋值均对应一个新的变量(版本),而每次使用的变量均为该变量到达该程序点时的版本。例如,对于

    1
    2
    3
    y := 1 // 多余的赋值
    y := 2
    x := y
    对于非SSA形式的编译器,需要通过数据流分析(到达-定义分析)来确定\(x\)选取哪个\(y\)值。SSA将其转化为
    1
    2
    3
    y1 := 1
    y2 := 2
    x1 := y2 // x1来自于y2
    便省去了数据流分析的过程

  • 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
2
3
4
5
6
7
8
public JMethod(SootMethod rawMethod) {
Body body = rawMethod.getActiveBody();
PatchingChain<Unit> units = body.getUnits();
Iterator<Unit> iterator = units.iterator();
while (iterator.hasNext()) {
// do something
}
}

Soot Phases

下图展示了Soot中不同pack(处理阶段)之间的工作方式。

1
2
3
graph LR
jb --> cg --> wjtp --> wjop --> wjap --> jtp
jtp --> jop --> jap --> bb --> tag
1
2
3
4
5
graph TD
A[Hard] -->|Text| B(Round)
B --> C{Decision}
C -->|One| D[Result 1]
C -->|Two| E[Result 2]

jbJimple 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
2
3
4
5
6
7
8
9
10
public static void main(String[] args) {
PackManager.v().getPack("wjtp").add(
new Transform("wjtp.myTransform", new SceneTransformer() {
protected void internalTransform(String phaseName,
Map options) {
System.err.println(Scene.v().getApplicationClasses());
}
}));
soot.Main.main(args);
}

Jimple Packs

类似地,对于每个方法体,Soot还提供了 + jtp,Jimple transformation pack: 默认启用且为空。一般可将过程间分析逻辑(定义在一个继承自BodyTransformer的自定义类中)置于此处。

  • jop,Jimple optimization pack: 对Jimple进行优化,默认关闭。可通过参数-o-p jop enabled开启。

  • jap,Jimple annotation pack: 对Jimple进行属性注解。下面的代码片段中,插入的逻辑将为方法中的每条语句打印标签:

1
2
3
4
5
6
7
8
9
10
11
12
13
public static void main(String[] args) {
PackManager.v().getPack("jap").add(
new Transform("jap.myTransform", new BodyTransformer() {
protected void internalTransform(Body body, String phase, Map options) {
for (Unit u : body.getUnits()) {
System.out.println(u.getTags());
}
}
}));
Options.v().set_verbose(true);
PhaseOptions.v().setPhaseOption("jap.npc", "on");
soot.Main.main(args);
}

bb pack用于将(优化后和注解后的)Jimple体转换为Baf体,而tag pack用于聚合重复标签。

参考

  1. http://www.iro.umontreal.ca/~dufour/cours/ift6315/docs/soot-tutorial.pdf
  2. http://www.bodden.de/2008/11/26/soot-packs/