Static analysis指在不对program进行运行的情况下,对其行为进行分析。对于Java有两大开源的static analysis 框架,Soot和WALA。Soot支持对Andoird 代码分析。
soot是java优化框架,提供4种中间代码来分析和转换字节码。
Baf:精简的字节码表示,操作简单 Jimple:适用于优化的3-address中间表示 Shimple:Jimple的SSA变体 Grimple:适用于反编译和代码检查的Jimple汇总版本。
使用:
1.命令行使用 (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需要知道类型信息,它需要为局部变量重构类型,那么它要知道你 要处理的文件的完整类层次。以下方法可以解决。 a.添加相关的jar文件到classpath,比如说JDK的rt.jar。
b.使用-pp选项。-pp表示prepend path,它表示Soot自动将以下内容添加到类路径中(按此顺序):当前环境的CLASSPATH变量值;{JAVA_HOME}/lib/rt.jar;如果使用-w(whole-program mode),添加{JAVA_HOME}/lib/jce.jar。
(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或jimplejava比如: -src-prec java 只加载A.java
2.编写java程序使用 soot的java api文档:https://soot-build.cs.uni-paderborn.de/public/origin/develop/soot/soot-develop/jdoc/ 如果我们想要编写一个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;
publicEmployee(Stringn,doubles,intyear,intmonth,intday)
{
name=n;
salary=s;
GregorianCalendarcalendar=newGregorianCalendar(year,month-1,day);
hireDay=calendar.getTime();
}
publicStringgetName()
{
returnname;
}
publicdoublegetSalary()
{
returnsalary;
}
publicDategetHireDay()
{
returnhireDay;
}
publicvoidraiseSalary(doublebyPercent)
{
doubleraise=salary*byPercent/100;
salary+=raise;
}
publicbooleanequals(ObjectotherObject)
{
// a quick test to see if the objects are identical
if(this==otherObject)returntrue;
// must return false if the explicit parameter is null
if(otherObject==null)returnfalse;
// if the classes don't match, they can't be equal
if(getClass()!=otherObject.getClass())returnfalse;
// now we know otherObject is a non-null Employee
Employeeother=(Employee)otherObject;
// test whether the fields have identical values
returnObjects.equals(name,other.name)&&salary==other.salary&&Objects.equals(hireDay,other.hireDay);
}
publicinthashCode()
{
returnObjects.hash(name,salary,hireDay);
}
publicStringtoString()
{
returngetClass().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 = "/home/myw/name";
publicvoidgetClassUnderDir() {
apiClasses=newLinkedHashSet<String>();
for(StringclzName:SourceLocator.v().getClassesUnder(apiPath)) {
System.out.printf("api class: %s\n",clzName);
//加载要处理的类设置为应用类,并加载到soot环境Scene中
Scene.v().loadClass(clzName,SootClass.BODIES).setApplicationClass();
}
}
publicvoidgetMethods() {
for(SootClassclz: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(SootMethodme:clz.getMethods()) {
System.out.println(me.toString());
if(me.hasActiveBody()){
System.out.println(me.getActiveBody().toString());
}
}
}
}
}
privatestaticvoidsetOptions() {
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);
}
privatestaticvoidsetSootClassPath() {
StringBuffercp=newStringBuffer();
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());
}
publicstaticvoidmain(String[]args) {
setSootClassPath();//设置classpath
setOptions();//设置soot的选项
helloSoots=newhelloSoot();
s.getClassUnderDir();
s.getMethods();
}
Baf - 基于栈的bytecode
在bytecode中对不同保留类型,如int和float,的同一操作(如add),有不同的指令。这是因为在计算机中整形和浮点型的表达方式是不一样的,在底层实现时无法让两个操作符分属于这两种不同类型,也就是需要不同的指令对应不同的数据类型的操作。我们做分析时不用在意它到底调用的什么类型的指令,不对int还是float做细致区分,只要知道它是个数且知道是对这数的什么样的操作就行了。Baf因此用于在bytecode层面上的分析。
Jimple - typed, 3-addresses, statement based。
Jimple是Soot的核心,是四种IR中最重要的。Soot能直接创建Jimple码,也可由Java sourcecode或者bytecode转化翻译而来。bytecode会被翻译成untyped Jimple,再通过type inference 方法对局部变量加上类型。翻译的重要一步是对表达式作线性化使得每个statement只能最多refernce 3个局部变量或者常量(没懂。。)。相对于bytecode的200多种指令,Jimple只有15条,分别对应着核心指令的 NopStmt, IdentityStmt, AssignStmt;函数内控制流指令的IfStmt, GotoStt, TableSwitchStmt和LookUpSwitchStmt,函数间控制流的InvoeStmt, ReturnStmt, ReturnVoidStmt, 监视器指令EnterMonitorStmt和ExitMonitorStmt,最后处理异常ThrowStmt和退出的RetStmt。
用:
java -cp soot-trunk.jar soot.Main -f J Foo
读入sootOutput文件夹中的Foo.Jimple。其所对应的Java 源码为,
网友评论