package jqian.sootex.util; import java.lang.reflect.Method; import java.util.*; import jqian.Global; import jqian.sootex.util.callgraph.CallGraphEdgeFilter; import jqian.sootex.util.callgraph.CallGraphHelper; import jqian.sootex.util.callgraph.CallGraphNodeFilter; import jqian.sootex.util.callgraph.DirectedCallGraph; import jqian.util.SortedArraySet; import soot.jimple.*; import soot.jimple.spark.SparkTransformer; import soot.jimple.spark.pag.PAG; import soot.jimple.toolkits.callgraph.CallGraph; import soot.jimple.toolkits.callgraph.Edge; import soot.jimple.toolkits.callgraph.ReachableMethods; import soot.shimple.*; import soot.*; import soot.util.*; import soot.tagkit.*; import soot.toolkits.graph.DirectedGraph; import soot.toolkits.graph.StronglyConnectedComponents; import soot.toolkits.scalar.Pair; /** * A collection of basic soot utils */ public class SootUtils { /**Get the SootClass instance set of cls's father types*/ public static Set<SootClass> getAncestorTypes(SootClass cls){ Set<SootClass> classes = new HashSet<SootClass>(); Chain<SootClass> interfaces = cls.getInterfaces(); for(SootClass itInterface: interfaces){ classes.add(itInterface); classes.addAll(getAncestorTypes(itInterface)); } if(cls.hasSuperclass()){ SootClass superClass=cls.getSuperclass(); classes.add(superClass); classes.addAll(getAncestorTypes(superClass)); } return classes; } @SuppressWarnings("unchecked") public static void getAllSubClasses(SootClass cls,Collection<SootClass> out){ try{ FastHierarchy hierarchy = Scene.v().getFastHierarchy(); Collection<SootClass> subs = hierarchy.getSubclassesOf(cls); out.addAll(subs); for(SootClass c: subs){ getAllSubClasses(c,out); } } catch(Exception e){ } } public static Set<SootField> findAllInstanceFields(SootClass cls){ Set<SootField> fields = new TreeSet<SootField>(NumberableComparator.v()); Set<SootClass> set = getAncestorTypes(cls); set.add(cls); for(SootClass base: set){ try{ Chain<SootField> baseFields = base.getFields(); for(SootField f: baseFields){ if(!f.isStatic()) fields.add(f); } } catch(RuntimeException e){} //This operation requires resolving level SIGNATURES, but the analyzed //class may be resolving at level HIERARCHY. In the case, a RuntimeException //will be thrown out. The exception does not exist in whole-program analysis mode } return fields; } /**Check whether the statement is caught_exception_ref*/ public static boolean isExceptionIdentityStmt(Unit s){ if(s instanceof IdentityStmt){ IdentityStmt idStmt=(IdentityStmt)s; if(idStmt.getRightOp() instanceof CaughtExceptionRef) return true; } return false; } public boolean isLibMethod(SootMethod m){ SootClass cls = m.getDeclaringClass(); return isLibPackage(cls.getPackageName()); } static String[] JAVA_LIB_PACKAGES = { "sun.", "sunw.", "com.sun.", "java.", "javax.", "org.apache.", "org.ietf.jgss.", "org.omg.", "org.w3c.dom.", "org.xml.sax." }; public boolean isLibPackage(String name){ int size = JAVA_LIB_PACKAGES.length; for(int i=0;i<size;i++){ String pkg = JAVA_LIB_PACKAGES[i]; if(name.startsWith(pkg)){ return true; } } return false; } public static int getLine(Unit unit){ int line=0; LineNumberTag tag = (LineNumberTag)unit.getTag("LineNumberTag"); if (tag != null) { line=tag.getLineNumber(); } return line; } /**get the short description of a statement or access path by use the * class name instead of the one with package description.*/ public static String toShortString(String string){ StringBuffer str=new StringBuffer(string); //filter package name for(int i=str.length()-1;i>=0;i--){ char ch=str.charAt(i); if(ch=='.'){ char right=str.charAt(i+1); if(right=='<'){//the left must be a local }else{//the left must be a package name //find the first char of package name for(int j=i-1;j>=0;j--){ char cur=str.charAt(j); if(!( ('a'<=cur && cur<='z') || ('A'<=cur && cur <='Z') || ('0'<=cur && cur <='9') || cur=='.' || cur=='_') ){ str.delete(j+1,i+1); i=j; break; } } } } /* if(ch=='>'){ for(int j=i-1;j>=0;j--){ if(str.charAt(j)==' '){ int firstBlank=j; for(int k=firstBlank-1;k>=0;k--) if(str.charAt(k)==' '){ str.delete(k,firstBlank); break; } break; } } }*/ } //filter field description /* for(int i=str.length()-1;i>=0;i--){ char ch=str.charAt(i); if(ch=='>'){ int firstBlank=-1; for(int j=i-1;j>=0;j--){ char cur=str.charAt(j); if(cur==' ') firstBlank=j; if(cur=='<' && firstBlank>0){ str.delete(j,firstBlank+1); i=j; break; } } } }*/ return str.toString(); } public static String getValueString(Value v){ if(v instanceof Immediate){ return v.toString(); } else if(v instanceof ArrayRef){ return v.toString(); } else if(v instanceof StaticFieldRef){ StaticFieldRef ref = (StaticFieldRef)v; SootField field = ref.getField(); SootClass cls = field.getDeclaringClass(); return cls.getShortName()+"."+field.getName(); } else if(v instanceof InstanceFieldRef){ InstanceFieldRef ref = (InstanceFieldRef)v; SootField field = ref.getField(); Value base = ref.getBase(); return base.toString()+"."+field.getName(); } else if(v instanceof PhiExpr){ PhiExpr expr = (PhiExpr)v; String str = "Phi"+expr.getBlockId()+"("; int count = expr.getArgCount(); for (int i = 0; i < count; i++) { str += expr.getValue(i); if (i != count - 1){ str += ','; } } str += ")"; return str; } else if(v instanceof InvokeExpr){ String str = ""; InvokeExpr expr = (InvokeExpr)v; SootMethod tgt = expr.getMethod(); if(v instanceof InstanceInvokeExpr){ InstanceInvokeExpr instCall = (InstanceInvokeExpr)v; str += instCall.getBase().toString(); } else{ str += tgt.getDeclaringClass().getShortName(); } str += "." + tgt.getName() + "("; int count = expr.getArgCount(); for (int i = 0; i < count; i++) { str += expr.getArg(i); if (i != count - 1){ str += ','; } } str += ")"; return str; } else{ return v.toString(); } } public static String getShortFieldString(SootField f){ SootClass cls = f.getDeclaringClass(); String s = cls.getShortName(); s += "."+f.getName(); return s; } public static String getStmtString(Unit stmt) { if (stmt instanceof DefinitionStmt) { DefinitionStmt def = (DefinitionStmt) stmt; String str = getLine(stmt) + ":"; str += getValueString(def.getLeftOp()); str += "=" + getValueString(def.getRightOp()); return str; } else if (stmt instanceof InvokeStmt){ InvokeStmt invoke = (InvokeStmt)stmt; return SootUtils.getLine(stmt) + ":" + getValueString(invoke.getInvokeExpr()); } else if (stmt instanceof IfStmt) { IfStmt ifs = (IfStmt) stmt; return getLine(stmt) + ":if " + ifs.getCondition() + " goto " + getLine(ifs.getTarget()); } else if (stmt instanceof GotoStmt) { GotoStmt gotoStmt = (GotoStmt) stmt; return getLine(stmt) + ":goto " + getLine(gotoStmt.getTarget()); } else{ return getLine(stmt) + ":" + toShortString(stmt.toString()); } } public static int getMethodCount(){ return Scene.v().getMethodNumberer().size()+1; } /** Reset Soot analysis results, prepare for another time of analysis. */ public static void resetSoot(){ //clean soot Scene.v().releaseActiveHierarchy(); Scene.v().releaseCallGraph(); Scene.v().releaseFastHierarchy(); Scene.v().releasePointsToAnalysis(); Scene.v().releaseReachableMethods(); Scene.v().releaseSideEffectAnalysis(); Object[] classes = Scene.v().getClasses().toArray(); for(int i=0;i<classes.length;i++){ SootClass c = (SootClass)classes[i]; Scene.v().removeClass(c); } soot.G.reset(); //force garbage collection System.gc(); System.gc(); } @SuppressWarnings({ "unchecked", "rawtypes" }) public static ReachableMethods getReachableMethods(Collection entries){ Scene scene = Scene.v(); if(entries.equals(scene.getEntryPoints())){ return scene.getReachableMethods(); } else{ ReachableMethods rm = new ReachableMethods(scene.getCallGraph(),entries); rm.update(); return rm; } } public static SootClass loadClassesForEntry(String entryClass){ Scene scene = Scene.v(); SootClass mainClass = scene.loadClassAndSupport(entryClass); mainClass.setApplicationClass(); scene.setMainClass(mainClass); scene.loadNecessaryClasses(); scene.setEntryPoints(EntryPoints.v().application()); return mainClass; } /** Number classes, fields, methods, locals, and etc. * Soot does not automatically do numbering for these entities. */ public static void numberClassAndFields(){ Scene scene = Scene.v(); ArrayNumberer fieldNumberer = scene.getFieldNumberer(); int clsNum = 1; //Assure field numbers. SOOT only set number for fields of RefLikeType(s) //The later analysis may depend on the field numbers to distinguish them Collection<SootClass> classes = scene.getClasses(); for(SootClass cls: classes){ cls.setNumber(clsNum); clsNum++; Collection<SootField> fields = cls.getFields(); for(SootField f: fields){ if(f.getNumber()==0){ fieldNumberer.add(f); } } } } public static void numberLocals(){ //Assure local numbers ArrayNumberer localNumberer = Scene.v().getLocalNumberer(); for(SootClass cls: Scene.v().getClasses()){ for(SootMethod mthd: cls.getMethods()){ if(!mthd.isConcrete()) continue; if(!mthd.hasActiveBody()){ mthd.retrieveActiveBody(); } Body body = mthd.getActiveBody(); for(Local loc: body.getLocals()){ //check if not numbered yet if(loc.getNumber()<=0){ localNumberer.add(loc); } } } } } /** Clean unnecessary memories of PointsToAnalysis if a 'clean' method is add to class PAG. */ public static void cleanPAG() { PAG pag = (PAG) Scene.v().getPointsToAnalysis(); // in the extended version, PAG has a clean() method try { // call: pag.clean(); Method clean = PAG.class.getMethod("clean"); if (clean != null) clean.invoke(pag); } catch (Exception e) { } } public static Pair<Integer,Integer> getLineRange(SootClass cls){ int min = Integer.MAX_VALUE; int max = 0; for(SootMethod m: cls.getMethods()){ Body body = m.retrieveActiveBody(); for(Unit u: body.getUnits()){ int line = getLine(u); if(line<min){ min = line; } if(line>max){ max = line; } } } Pair<Integer,Integer> p = new Pair<Integer,Integer>(min,max); return p; } public static void doSparkPointsToAnalysis(Map<String,String> opt) { Global.v().out.println("[Spark] Starting analysis ..."); HashMap<String,String> defaultOptions = new HashMap<String,String>(); defaultOptions.put("enabled","true"); defaultOptions.put("verbose","true"); defaultOptions.put("ignore-types","false"); defaultOptions.put("force-gc","false"); defaultOptions.put("pre-jimplify","false"); defaultOptions.put("vta","false"); defaultOptions.put("rta","false"); defaultOptions.put("field-based","false"); defaultOptions.put("types-for-sites","false"); defaultOptions.put("merge-stringbuffer","true"); defaultOptions.put("string-constants","false"); defaultOptions.put("simple-edges-bidirectional","false"); defaultOptions.put("on-fly-cg","true"); defaultOptions.put("simplify-offline","false"); // true defaultOptions.put("simplify-sccs","false"); defaultOptions.put("ignore-types-for-sccs","false"); defaultOptions.put("propagator","worklist"); defaultOptions.put("set-impl","double"); defaultOptions.put("double-set-old","hybrid"); defaultOptions.put("double-set-new","hybrid"); defaultOptions.put("dump-html","false"); defaultOptions.put("dump-pag","false"); defaultOptions.put("dump-solution","false"); defaultOptions.put("topo-sort","false"); defaultOptions.put("dump-types","true"); defaultOptions.put("class-method-var","true"); defaultOptions.put("dump-answer","false"); defaultOptions.put("add-tags","false"); defaultOptions.put("set-mass","false"); defaultOptions.put("trim-clinit","true"); defaultOptions.put("all-reachable","false"); // Set the following configurations to false may reduce safety, // but dramatically improve performance defaultOptions.put("simulate-natives","true"); //false to increase speed defaultOptions.put("implicit-entry","true"); //false to ignore implicit entries for(Map.Entry<String, String> e: opt.entrySet()){ String name = e.getKey(); String value = e.getValue(); defaultOptions.put(name, value); } SparkTransformer.v().transform("",defaultOptions); Global.v().out.println("[Spark] Done!"); } public static void jimplify(){ Collection<SootClass> classes = Scene.v().getClasses(); for(SootClass c: classes){ for(SootMethod m: c.getMethods()){ if(m.isConcrete()) m.retrieveActiveBody(); } } } @SuppressWarnings({ "rawtypes", "unchecked" }) public static Set toCompactSet(Collection<?> s){ if(s==null) return null; if(s.isEmpty()){ return Collections.emptySet(); } else{ return new SortedArraySet(s,NumberableComparator.v()); } } /** * get graph of strong connected components. * XXX: Here we break thread start calls in the call graph when finding SCCs */ public static DirectedGraph<Collection> getSCCGraph(CallGraph cg, Collection entries) { ReachableMethods rm = CallGraphHelper.getReachableMethod(cg, entries); CallGraphNodeFilter nodeFilter = CallGraphHelper.getCallGraphNodeFilter(rm); CallGraphEdgeFilter edgeFilter = new CallGraphEdgeFilter() { public boolean isIgnored(Edge e) { return (e.kind() == Kind.THREAD); } }; DirectedCallGraph dcg = new DirectedCallGraph(cg, entries, nodeFilter, edgeFilter); StronglyConnectedComponents scc = new StronglyConnectedComponents(dcg); return scc.getSuperGraph(); } }