/** Expensive! */ private void dumpCallGraphReachablesCSV() { try { FileWriter fw = new FileWriter(Project.v().getOutputDir() + File.separator + "reachables-count.csv"); fw.write("Method,Reachables"); for (MethodOrMethodContext momc : getReachableMethodContexts()) { if ("<clinit>".equals(momc.method().getName())) continue; Set<MethodOrMethodContext> c = new HashSet<MethodOrMethodContext>(); c.add(momc); Filter filter = new Filter(noStaticInits); // filter on static initializers, hopefully they won't show in the stats, // or any calls that they make... ReachableMethods rm = new ReachableMethods(callGraph, c.iterator(), filter); rm.update(); QueueReader<MethodOrMethodContext> edges = rm.listener(); int reachables = 0; while (edges.hasNext()) { MethodOrMethodContext reachable = edges.next(); if ("<clinit>".equals(reachable.method().getName())) continue; reachables++; } fw.write(momc + "|" + reachables + "\n"); } fw.close(); } catch (IOException e) { } }
@Override protected void runInternal() { // don't print crap to screen! G.v().out = new PrintStream(NullOutputStream.NULL_OUTPUT_STREAM); Scene.v().loadDynamicClasses(); setSparkPointsToAnalysis(); // other passes can print crap now G.v().out = System.out; ptsProvider = (PAG) Scene.v().getPointsToAnalysis(); typeManager = ptsProvider.getTypeManager(); // cache the call graph callGraph = Scene.v().getCallGraph(); createNewToAllocMap(); /* for (SootMethod method : getReachableMethods()) { Set<MethodOrMethodContext> mcs = getMethodContexts(method); if (mcs.size() > 30) System.out.println(method + " " + mcs.size()); } */ // dumpReachablesAndAllocNodes(); // dumpCallGraphReachablesCSV(); // dumpOutdegreesCSV(); if (Config.v().dumpPta) { dumpPTA(Project.v().getOutputDir() + File.separator + "pta.txt"); } if (Config.v().dumpCallGraph) { // dumpCallGraph(Project.v().getOutputDir() + File.separator + "callgraph.dot"); String fileName = String.format("callgraph%d.txt", runCount++); dumpTextGraph(Project.v().getOutputDir() + File.separator + fileName); } // System.out.println(SparkEvaluator.v().toString()); }
public static void writeReport() { // make sure collapsed call graph has been run CollaspedCallGraph.v(); FileWriter fw; try { String name = ""; if ("".equals(Config.v().appName)) { name += "android-app"; } else { name += Config.v().appName; } String additionalInfo = Config.v().additionalInfo; if (!"".equals(additionalInfo)) { additionalInfo = "_" + additionalInfo.replaceAll(" ", "_"); } String fileName = name + "_" + getConfiguration().replaceAll(" ", "_") + additionalInfo + "_pta-report.txt"; fw = new FileWriter(Project.v().getOutputDir() + File.separator + fileName); // write configuration details fw.write("App Name: " + name + "\n"); fw.write("Config: " + getConfiguration() + "\n"); fw.write("Cmdline supplied extra info: " + Config.v().additionalInfo + "\n"); fw.write(refinementStats.toString()); // write final run of pta fw.write(SparkEvaluator.v().toString()); // write total lines of code fw.write("\nTotal Reachable LOC: " + getReachableLines() + "\n\n"); // write information flow fw.write(infoFlowResults()); // fw.write(finegrainedFlowResults()); fw.close(); } catch (IOException e) { } }
private String buildImportantAllocs() { StringBuffer sb = new StringBuffer(); for (SootClass clz : Scene.v().getClasses()) { if (Project.v().isSrcClass(clz) || /*isImportantJavaAlloc(clz) ||*/ clz.getName().startsWith(Project.DS_GENERATED_CLASSES_PREFIX) || clz.getName().startsWith("droidsafe.runtime")) { logger.info("Adding class to important alloc list of spark: {}", clz); sb.append(clz + ","); } } String ret = sb.toString(); ret = ret.substring(0, ret.length() - 1); return ret; }
private void dumpOutdegreesCSV() { try { FileWriter fw = new FileWriter(Project.v().getOutputDir() + File.separator + "reachables-outdegree.csv"); fw.write("Method,Outdegree"); for (MethodOrMethodContext momc : getReachableMethodContexts()) { int outdegree = 0; Iterator<Edge> edges = callGraph.edgesOutOf(momc); while (edges.hasNext()) { edges.next(); outdegree++; } fw.write(momc + "|" + outdegree + "\n"); } fw.close(); } catch (IOException e) { } }
private void dumpReachablesAndAllocNodes() { try { FileWriter fw = new FileWriter(Project.v().getOutputDir() + File.separator + "spark-dump.log"); fw.write("# Reachable Method Contexts:\n\n"); for (MethodOrMethodContext momc : getReachableMethodContexts()) { fw.write(momc + "\n\n"); } fw.write("\n\n# AllocNodes: \n\n"); Iterator<AllocNode> nodes = ptsProvider.getAllocNodeNumberer().iterator(); while (nodes.hasNext()) { AllocNode node = nodes.next(); fw.write(node + "\n\n"); } fw.close(); } catch (IOException e) { } }
private static String infoFlowResults() { Hierarchy hierarchy = Scene.v().getActiveHierarchy(); SootClass throwable = Scene.v().getSootClass("java.lang.Throwable"); StringBuffer buf = new StringBuffer(); // count number of flows // have to map it down to invoke statement because of context // key is invoke of sink -> sources Map<InvokeExpr, Set<Stmt>> invokeToSourcesMem = new HashMap<InvokeExpr, Set<Stmt>>(); // key is invoke of sink -> sources Map<InvokeExpr, Set<Stmt>> invokeToSourcesRec = new HashMap<InvokeExpr, Set<Stmt>>(); // key is invoke of sink -> sources Map<InvokeExpr, Set<Stmt>> invokeToSourcesArgs = new HashMap<InvokeExpr, Set<Stmt>>(); // key is invoke of sink -> sources Map<InvokeExpr, Set<Stmt>> invokeToSourcesArgsPrecise = new HashMap<InvokeExpr, Set<Stmt>>(); for (Map.Entry<Method, List<Method>> block : RCFGToSSL.v().getSpec().getEventBlocks().entrySet()) { // only count events in src classes, not in libraries boolean inSrc = false; for (IAllocNode recNode : block.getKey().getReceiverAllocNodes()) { if (recNode.getType() instanceof RefType) { SootClass clz = ((RefType) recNode.getType()).getSootClass(); if (Project.v().isSrcClass(clz)) { inSrc = true; break; } } } if (!inSrc) continue; for (Method oe : block.getValue()) { if (oe.getSinkInfoKinds().size() > 0 && oe.getSourcesInfoKinds().size() > 0) { // only count sensitive sinks Stmt sinkInvoke = JimpleRelationships.v().getEnclosingStmt(oe.getInvokeExpr()); if (!InfoKind.callsSensitiveSink(sinkInvoke)) continue; // we have a sink with connected sources InvokeExpr ie = oe.getInvokeExpr(); // get args for (int i = 0; i < oe.getNumArgs(); i++) { Type formalArgType = oe.getActualArgType(i); // ignore method arguments that have a declared type of throwable or a subclass of // throwable if (formalArgType instanceof RefType && !((RefType) formalArgType).getSootClass().isInterface() && hierarchy.isClassSubclassOfIncluding( ((RefType) formalArgType).getSootClass(), throwable)) continue; for (Map.Entry<InfoKind, Set<Stmt>> flows : oe.getArgSourceInfoUnitsConservative(i).entrySet()) { for (Stmt source : flows.getValue()) { if (InfoKind.callsSensitiveSource(source)) { if (!invokeToSourcesArgs.containsKey(ie)) { invokeToSourcesArgs.put(ie, new HashSet<Stmt>()); } invokeToSourcesArgs.get(ie).add(source); } } } for (Map.Entry<InfoKind, Set<Stmt>> flows : oe.getArgSourceInfoUnitsPrecise(i).entrySet()) { for (Stmt source : flows.getValue()) { if (InfoKind.callsSensitiveSource(source)) { if (!invokeToSourcesArgsPrecise.containsKey(ie)) { invokeToSourcesArgsPrecise.put(ie, new HashSet<Stmt>()); } invokeToSourcesArgsPrecise.get(ie).add(source); } } } } // get receiver for (Map.Entry<InfoKind, Set<Stmt>> flows : oe.getReceiverSourceInfoUnits().entrySet()) { // ignore all non-critical flows for (Stmt source : flows.getValue()) { if (InfoKind.callsSensitiveSource(source)) { if (!invokeToSourcesRec.containsKey(ie)) { invokeToSourcesRec.put(ie, new HashSet<Stmt>()); } invokeToSourcesRec.get(ie).add(source); } } } // get method accesses for (Map.Entry<InfoKind, Set<Stmt>> flows : oe.getMethodInfoUnits().entrySet()) { // ignore all non-critical flows for (Stmt source : flows.getValue()) { if (InfoKind.callsSensitiveSource(source)) { if (!invokeToSourcesMem.containsKey(ie)) { invokeToSourcesMem.put(ie, new HashSet<Stmt>()); } invokeToSourcesMem.get(ie).add(source); } } } } } } // count number of flows int flowsIntoSinksArgs = 0; int flowsIntoSinksArgsPrecise = 0; int flowsIntoSinksMem = 0; int flowsIntoSinksRec = 0; try { for (Map.Entry<InvokeExpr, Set<Stmt>> sink : invokeToSourcesArgs.entrySet()) { flowsIntoSinksArgs += sink.getValue().size(); } for (Map.Entry<InvokeExpr, Set<Stmt>> sink : invokeToSourcesArgsPrecise.entrySet()) { flowsIntoSinksArgsPrecise += sink.getValue().size(); } for (Map.Entry<InvokeExpr, Set<Stmt>> sink : invokeToSourcesMem.entrySet()) { flowsIntoSinksMem += sink.getValue().size(); } for (Map.Entry<InvokeExpr, Set<Stmt>> sink : invokeToSourcesRec.entrySet()) { flowsIntoSinksRec += sink.getValue().size(); } } catch (Exception e) { } buf.append("Info Flow Time Sec: " + infoFlowTimeSec + "\n"); buf.append("Flows into sinks (Args): " + flowsIntoSinksArgs + "\n"); buf.append("Flows into sinks (Args, Precise): " + flowsIntoSinksArgsPrecise + "\n"); buf.append("Flows into sinks (Mem): " + flowsIntoSinksMem + "\n"); buf.append("Flows into sinks (Rec): " + flowsIntoSinksRec + "\n"); buf.append(reachableSinksSources()); return buf.toString(); }
@Override public void tranformsInvoke( SootMethod containingMthd, SootMethod callee, InvokeExpr invokeExpr, Stmt stmt, Body body) { if (!Project.v().isSrcClass(containingMthd.getDeclaringClass())) { return; } if (modified.contains(stmt)) { return; } modified.add(stmt); IntentResolutionStats.v().contentProviderOps++; Value lvalue = null; if (stmt instanceof AssignStmt) { lvalue = ((AssignStmt) stmt).getLeftOp(); } Set<SootField> targetCPFields = new LinkedHashSet<SootField>(); boolean resolved = true; for (IAllocNode node : PTABridge.v().getPTSetIns(invokeExpr.getArg(0))) { resolved = addToTargets(node, targetCPFields, stmt); if (!resolved) { UnresolvedICC.v().addInfo(stmt, callee, "Unresolved URI for Content Provider"); // can break here because we added all possible content provider destinations IntentResolutionStats.v().contentProviderOpsUnresolvedUri++; break; } } // for each field of harness that is a content provider for (SootField cpField : targetCPFields) { SootClass cpClass = ((RefType) cpField.getType()).getSootClass(); SootMethod target = cpClass.getMethod(callee.getSubSignature()); // create local and add to body Local local = Jimple.v().newLocal("_$contentprovider_local_" + localID++, cpField.getType()); body.getLocals().add(local); // set field of cp to local [local = harness.contentproviderfield] // set local to field Stmt localAssign = Jimple.v().newAssignStmt(local, Jimple.v().newStaticFieldRef(cpField.makeRef())); // insert before original statement body.getUnits().insertBefore(localAssign, stmt); InvokeExpr newInvoke = Jimple.v().newVirtualInvokeExpr(local, target.makeRef(), invokeExpr.getArgs()); // create statement to invoke Stmt toInsert = null; if (lvalue == null) { // original call not in an assign; toInsert = Jimple.v().newInvokeStmt(newInvoke); } else { // original call in an assign toInsert = Jimple.v().newAssignStmt(lvalue, newInvoke); } // insert after original statement just to have all locals assigned in a block body.getUnits().insertAfter(toInsert, stmt); logger.info( "Adding {} call to ContentProvider {} in method {}", callee.getSubSignature(), cpClass, containingMthd); // ignore generated calls in rcfg RCFG.v().ignoreInvokeForOutputEvents(toInsert); } // if resolved and in app target, then don't report if (resolved) { IntentResolutionStats.v().contentProviderOpsResolvedUri++; if (targetCPFields.size() > 0) { RCFG.v().ignoreInvokeForOutputEvents(stmt); IntentResolutionStats.v().contentProviderOpsInAppTotalTargets += targetCPFields.size(); } else { IntentResolutionStats.v().contentProviderOpsInterAppTarget++; } } }