前言
本文主要介绍Soot工具的概念及使用。soot官方文档: 官方文档
soot介绍
soot是是java优化框架,提供4种中间代码来分析和转换字节码:
- Baf:精简的字节码表示,操作简单
- Jimple:适用于优化的3-address中间表示
- Shimple:Jimple的SSA变体
- Grimple:适用于反编译和代码检查的Jimple汇总版本。
1.soot提供的输入输出格式
输入格式:
- java(bytecode and source code up to Java 7)
- android字节码
- Jimple中间表示
- Jasmin,低级中间表示
- soot提供的分析功能
输出格式:
- java 字节码
- android字节码
- Jimple
- Jasmin
2.soot提供的分析功能
- 调用图构造
- 指针分析
- Def/use chains
- 模板驱动的程序内数据流分析
- 模板驱动的程序间数据流分析,与heros结合
- 结合FlowDroid的污染分析
安装
使用
soot的使用方法一般有两种:一 1.在命令行使用soot.
2.在java程序中使用soot。
命令行使用
1. 处理单个文件
soot可以处理三种类型的文件:java源代码(.java文件),java字节码(.class文件),Jimple源代码(.jimple文件)
Jimple是soot中最主要的中间表示,三地址代码基本上是一种Java的简化版本,只需要大约15种不同的指令。
java -jar soot.jar A B
其中,A B叫做应用类。如果只运行该命令,可能报错,因为soot有自己的classpath,它只会从该路径的jar文件或目录加载文件。所有要用-cp指出路径。
但运行之后还可能会报错,因为soot需要知道类型信息,它需要为局部变量重构类型,那么它要知道你要处理的文件的完整类层次。有三种方法可以解决。
1.添加相关的jar文件到classpath,比如说JDK的rt.jar。
2.使用-pp选项。-pp表示prepend path,它表示Soot自动将以下内容添加到类路径中(按此顺序):当前环境的CLASSPATH变量值;{JAVA_HOME}/lib/rt.jar;如果使用-w(whole-program mode),添加{JAVA_HOME}/lib/jce.jar。
- 使用-allow-phantom-refs。如果soot无解析类型,它会创建一个phantom类。这种方法非常有限,在很多情况下不会得到您需要的结果。
2.处理整个文件夹
java -jar soot.jar -process-dir 文件名
如果要处理jar文件,也是使用-process-dir 选项,后跟jar文件路径。同时可以使用-d选项,对soot的输出重定向。
3.处理确定类型的文件
假设你有个文件夹下放了A.java和A.class文件,并且你之前运行过soot,soot会自行加载A.class中A的定义。可能这并不是你想要的,那么可以使用-src-prec选项指定你想要处理的文件类型。-src-prec有4个值可供使用:
- c或class(默认)
- only-class:只使用class文件作为soot的源。
- J或jimple
- java
比如: -src-prec java 只加载A.java
4.应用类和库类
soot处理的类就叫做应用类,但库类恰恰相反,soot并不处理,只是用来类型分析。
5.指定输出类型
使用-f选项,指定你想要soot为你生成的文件类型,比如.jimple文件,.class文件,甚至.java文件。
6.阶段选项
soot支持不同细粒度的选项,允许您直接从命令行调整所有满足您的要求的分析和优化。这些命令选项的一般格式是-p PHASE OPT:VAL。
所有阶段选项的完整文档 例如,我们想要在soot执行过程中保留局部变量名。那么就在命令行里添加选项-p jb ues-original-names:true
编写java程序使用
soot的java api文档
如果我们想要编写一个java程序来访问一个Class中的所有Field和Methods,那么用java语言编写使用soot API,应该怎么做呢?
首先要指定classpath,然后像在命令行里添加选项一样对选项进行设置(比如对-process-dir设置)。其次将你想要的应用类加载到soot环境中Scene。然后对soot环境中的每个class进行遍历,再对每个class中的每个method方法访问。
假如我要对Employee.java文件中的字段和方法进行访问。
Employee.java:
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Objects;
public class Employee {
private String name;
private double salary;
private Date hireDay;
public Employee(String n, double s, int year, int month, int day)
{
name = n;
salary = s;
GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day);
hireDay = calendar.getTime();
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public Date getHireDay()
{
return hireDay;
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
public boolean equals(Object otherObject )
{
// a quick test to see if the objects are identical
if (this == otherObject) return true;
// must return false if the explicit parameter is null
if (otherObject == null) return false;
// if the classes don't match, they can't be equal
if (getClass() != otherObject.getClass()) return false;
// now we know otherObject is a non-null Employee
Employee other = (Employee) otherObject;
// test whether the fields have identical values
return Objects.equals(name, other.name) && salary == other.salary && Objects.equals(hireDay, other.hireDay);
}
public int hashCode()
{
return Objects.hash(name, salary, hireDay);
}
public String toString()
{
return getClass().getName() + "[name=" + name + ",salary=" + salary + ",hireDay=" + hireDay
+ "]";
}
}
helloSoot.java:
import java.io.File;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.*;
import soot.Scene;
import soot.SootClass;
import soot.SootMethod;
import soot.SourceLocator;
import soot.options.Options;
public class helloSoot {
private String apiPath = "";//应用类的路径
private Set apiClasses;
public void getClassUnderDir() {
apiClasses = new LinkedHashSet<String>();
for (String clzName: SourceLocator.v().getClassesUnder(apiPath)) {
System.out.printf("api class: %s\n", clzName);
//加载要处理的类设置为应用类,并加载到soot环境Scene中
Scene.v().loadClass(clzName, SootClass.BODIES).setApplicationClass();
}
}
public void getMethods() {
for (SootClass clz : Scene.v().getApplicationClasses()) {
System.out.println(clz.getName());
if (clz.getMethods().size() == 0){System.out.println("do not have methods!!!!!");}
else{
System.out.println("method num:"+clz.getMethods().size());
for(SootMethod me : clz.getMethods()) {
System.out.println(me.toString());
if (me.hasActiveBody()){
System.out.println(me.getActiveBody().toString());
}
}
}
}
}
private static void setOptions() {
soot.options.Options.v().set_keep_line_number(true);
soot.options.Options.v().set_whole_program(true);
// LWG
soot.options.Options.v().setPhaseOption("jb", "use-original-names:true");
soot.options.Options.v().setPhaseOption("cg", "verbose:false");
soot.options.Options.v().setPhaseOption("cg", "trim-clinit:true");
//soot.options.Options.v().setPhaseOption("jb.tr", "ignore-wrong-staticness:true");
soot.options.Options.v().set_src_prec(Options.src_prec_java);
soot.options.Options.v().set_prepend_classpath(true);
// don't optimize the program
soot.options.Options.v().setPhaseOption("wjop", "enabled:false");
// allow for the absence of some classes
//soot.options.Options.v().set_allow_phantom_refs(true);
}
private static void setSootClassPath() {
StringBuffer cp = new StringBuffer();
cp.append(".");
cp.append(File.pathSeparator + "/home/myw/name");
cp.append(File.pathSeparator+"/usr/lib/jvm/jdk1.7.0_79/jre/lib/rt.jar"+File.pathSeparator+"/usr/lib/jvm/jdk1.7.0_79/jre/lib/jce.jar");
System.setProperty("soot.class.path", cp.toString());
}
public static void main(String[] args) {
setSootClassPath();//设置classpath
setOptions();//设置soot的选项
helloSoot s = new helloSoot();
s.getClassUnderDir();
s.getMethods();
}
}