private void collectClassEntryPoints(String className) { AnalyzeJimpleClassJIT jimpleClass = new AnalyzeJimpleClassJIT(androidCallbacks); jimpleClass.collectCallbackMethodsForClass(className); // Run the soot-based operations PackManager.v().getPack("wjpp").apply(); PackManager.v().getPack("cg").apply(); PackManager.v().getPack("wjtp").apply(); // Collect the results of the soot-based phases for (Entry<String, Set<SootMethodAndClass>> entry : jimpleClass.getCallbackMethods().entrySet()) { if (this.callbackMethods.containsKey(entry.getKey())) { this.callbackMethods.get(entry.getKey()).addAll(entry.getValue()); } else { this.callbackMethods.put(entry.getKey(), new HashSet<>(entry.getValue())); } } entrypoints.addAll(jimpleClass.getDynamicManifestComponents()); Set<SootMethodAndClass> callbacks = new HashSet<>(); for (Set<SootMethodAndClass> methods : this.callbackMethods.values()) callbacks.addAll(methods); sourceSinkManager.setCallbacks(callbacks); }
/** * Incrementally collects the callback methods for all Android default * handlers implemented in the source code. This just processes the contents * of the worklist. * Note that this operation runs inside Soot, so this method only registers * a new phase that will be executed when Soot is next run */ public void collectCallbackMethodsIncremental() { Transform transform = new Transform("wjtp.ajc", new SceneTransformer() { protected void internalTransform(String phaseName, @SuppressWarnings("rawtypes") Map options) { // Process the worklist from last time System.out.println("Running incremental callback analysis for " + callbackWorklist.size() + " components..."); MultiMap<String, SootMethodAndClass> workListCopy = new HashMultiMap<String, SootMethodAndClass>(callbackWorklist); for (String className : workListCopy.keySet()) { List<MethodOrMethodContext> entryClasses = new LinkedList<MethodOrMethodContext>(); for (SootMethodAndClass am : workListCopy.get(className)) entryClasses.add(Scene.v().getMethod(am.getSignature())); analyzeRechableMethods(Scene.v().getSootClass(className), entryClasses); callbackWorklist.remove(className); } System.out.println("Incremental callback analysis done."); } }); PackManager.v().getPack("wjtp").add(transform); }
public SetupApplicationJIT(String apkFileLocation, String sootCP, ISourceSinkDefinitionProvider sourceSinkProvider) { this.apkFileLocation = apkFileLocation; this.sootCP = sootCP; try { // Load Android callbacks this.androidCallbacks = Activator.getDefault().getAndroidCallbacks(); // Process manifest ProcessManifest processMan = new ProcessManifest(apkFileLocation); this.appPackageName = processMan.getPackageName(); this.entrypoints = processMan.getEntryPointClasses(); // Parse the resource file ARSCFileParser resParser = new ARSCFileParser(); resParser.parse(apkFileLocation); this.resourcePackages = resParser.getPackages(); // LayoutFileParser LayoutFileParser lfp = new LayoutFileParser(this.appPackageName, resParser); lfp.parseLayoutFile(apkFileLocation, entrypoints); // Create the SourceSinkManager Set<SootMethodAndClass> callbacks = new HashSet<>(); for (Set<SootMethodAndClass> methods : this.callbackMethods.values()) callbacks.addAll(methods); sourceSinkManager = new AccessPathBasedSourceSinkManager(sourceSinkProvider.getSources(), sourceSinkProvider.getSinks(), callbacks, LayoutMatchingMode.MatchSensitiveOnly, lfp == null ? null : lfp.getUserControlsByID()); sourceSinkManager.setAppPackageName(this.appPackageName); sourceSinkManager.setResourcePackages(this.resourcePackages); sourceSinkManager.setEnableCallbackSources(true); } catch (IOException | XmlPullParserException e) { LOGGER.error("Error initializing " + apkFileLocation); } }
/***** Entry points calculation *****/ private AndroidEntryPointCreatorJIT createEntryPointCreator() { AndroidEntryPointCreatorJIT entryPointCreator = new AndroidEntryPointCreatorJIT( new ArrayList<String>(this.entrypoints)); Map<String, List<String>> callbackMethodSigs = new HashMap<String, List<String>>(); for (String className : this.callbackMethods.keySet()) { List<String> methodSigs = new ArrayList<String>(); callbackMethodSigs.put(className, methodSigs); for (SootMethodAndClass am : this.callbackMethods.get(className)) methodSigs.add(am.getSignature()); } entryPointCreator.setCallbackFunctions(callbackMethodSigs); return entryPointCreator; }
/*** * Static method to create AndroidMethod from Soot method signature * * @param signature The Soot method signature * @return The new AndroidMethod object */ public static AndroidMethod createFromSignature(String signature) { if (!signature.startsWith("<")) signature = "<" + signature; if (!signature.endsWith(">")) signature = signature + ">"; SootMethodAndClass smac = SootMethodRepresentationParser.v() .parseSootMethodString(signature); return new AndroidMethod(smac.getMethodName(), smac.getParameters(), smac.getReturnType(), smac.getClassName()); }
/** * Creates a new instance of the MethodSourceSinkDefinition class * @param am The method for which this object defines sources and sinks * @param baseObjects The source and sink definitions for the base object on * which a method of this class is invoked * @param parameters The source and sink definitions for parameters of * the current method * @param returnValues The source definitions for the return value of the * current method */ public SourceSinkDefinition(SootMethodAndClass am, Set<AccessPathTuple> baseObjects, Set<AccessPathTuple>[] parameters, Set<AccessPathTuple> returnValues) { this.method = am; this.baseObjects = baseObjects == null || baseObjects.isEmpty() ? null : baseObjects; this.parameters = parameters; this.returnValues = returnValues == null || returnValues.isEmpty() ? null : returnValues; }
/** * Adds a method to the set of callback method * * @param layoutClass * The layout class for which to register the callback * @param callbackMethod * The callback method to register */ private void addCallbackMethod(String layoutClass, AndroidMethod callbackMethod) { Set<SootMethodAndClass> methods = this.callbackMethods.get(layoutClass); if (methods == null) { methods = new HashSet<SootMethodAndClass>(); this.callbackMethods.put(layoutClass, methods); } methods.add(new AndroidMethod(callbackMethod)); }
private AndroidEntryPointCreator createEntryPointCreator() { AndroidEntryPointCreator entryPointCreator = new AndroidEntryPointCreator(new ArrayList<String>( this.entrypoints)); Map<String, List<String>> callbackMethodSigs = new HashMap<String, List<String>>(); for (String className : this.callbackMethods.keySet()) { List<String> methodSigs = new ArrayList<String>(); callbackMethodSigs.put(className, methodSigs); for (SootMethodAndClass am : this.callbackMethods.get(className)) methodSigs.add(am.getSignature()); } entryPointCreator.setCallbackFunctions(callbackMethodSigs); return entryPointCreator; }
/** * parses a string in soot representation, for example: * <soot.jimple.infoflow.test.TestNoMain: java.lang.String function1()> * <soot.jimple.infoflow.test.TestNoMain: void functionCallOnObject()> * <soot.jimple.infoflow.test.TestNoMain: java.lang.String function2(java.lang.String,java.lang.String)> * @param parseString The method signature to parse */ public SootMethodAndClass parseSootMethodString(String parseString){ if(!parseString.startsWith("<") || !parseString.endsWith(">")){ throw new IllegalArgumentException("Illegal format of " +parseString +" (should use soot method representation)"); } String name = ""; String className = ""; String returnType = ""; Pattern pattern = Pattern.compile("<(.*?):"); Matcher matcher = pattern.matcher(parseString); if(matcher.find()){ className = matcher.group(1); } pattern = Pattern.compile(": (.*?) "); matcher = pattern.matcher(parseString); if(matcher.find()){ returnType = matcher.group(1); //remove the string contents that are already found so easier regex is possible parseString = parseString.substring(matcher.end(1)); } pattern = Pattern.compile(" (.*?)\\("); matcher = pattern.matcher(parseString); if(matcher.find()){ name = matcher.group(1); } List<String> paramList = new ArrayList<String>(); pattern = Pattern.compile("\\((.*?)\\)"); matcher = pattern.matcher(parseString); if(matcher.find()){ String params = matcher.group(1); for (String param : params.split(",")) paramList.add(param.trim()); } return new SootMethodAndClass(name, className, returnType, paramList); }
@Test(timeout=300000) public void testParser(){ String s = "<soot.jimple.infoflow.test.TestNoMain: java.lang.String function1()>"; SootMethodRepresentationParser parser = SootMethodRepresentationParser.v(); SootMethodAndClass result = parser.parseSootMethodString(s); assertEquals("soot.jimple.infoflow.test.TestNoMain", result.getClassName()); assertEquals("function1", result.getMethodName()); assertEquals("java.lang.String", result.getReturnType()); }
/** * Adds a method to the set of callback method * * @param layoutClass The layout class for which to register the callback * @param callbackMethod The callback method to register */ private void addCallbackMethod(String layoutClass, AndroidMethod callbackMethod) { Set<SootMethodAndClass> methods = this.callbackMethods.get(layoutClass); if (methods == null) { methods = new HashSet<SootMethodAndClass>(); this.callbackMethods.put(layoutClass, methods); } methods.add(new AndroidMethod(callbackMethod)); }
public AndroidEntryPointCreator createEntryPointCreator() { AndroidEntryPointCreator entryPointCreator = new AndroidEntryPointCreator(new ArrayList<String>(this.entrypoints)); Map<String, List<String>> callbackMethodSigs = new HashMap<String, List<String>>(); for (String className : this.callbackMethods.keySet()) { List<String> methodSigs = new ArrayList<String>(); callbackMethodSigs.put(className, methodSigs); for (SootMethodAndClass am : this.callbackMethods.get(className)) { methodSigs.add(am.getSignature()); } } entryPointCreator.setCallbackFunctions(callbackMethodSigs); return entryPointCreator; }
public Map<String, Set<SootMethodAndClass>> getCallbackMethods() { return this.callbackMethods; }
private void addApplicationCallbackMethods(Body body) { if (!this.callbackFunctions.containsKey(applicationClass.getName())) return; // Do not try to generate calls to methods in non-concrete classes if (applicationClass.isAbstract()) return; if (applicationClass.isPhantom()) { System.err.println("Skipping possible application callbacks in " + "phantom class " + applicationClass); return; } for (String methodSig : this.callbackFunctions.get(applicationClass.getName())) { SootMethodAndClass methodAndClass = SootMethodRepresentationParser.v().parseSootMethodString(methodSig); // We do not consider lifecycle methods which are directly inserted // at their respective positions if (AndroidEntryPointConstants.getApplicationLifecycleMethods().contains(methodAndClass.getSubSignature())) continue; SootMethod method = findMethod(Scene.v().getSootClass(methodAndClass.getClassName()), methodAndClass.getSubSignature()); // If we found no implementation or if the implementation we found // is in a system class, we skip it. Note that null methods may // happen since all callback interfaces for application callbacks // are registered under the name of the application class. if (method == null) continue; if (method.getDeclaringClass().getName().startsWith("android.") || method.getDeclaringClass().getName().startsWith("java.")) continue; // Get the local instance of the target class Local local = this.localVarsForClasses.get(methodAndClass.getClassName()); if (local == null) { System.err.println( "Could not create call to application callback " + method.getSignature() + ". Local was null."); continue; } // Add a conditional call to the method JNopStmt thenStmt = new JNopStmt(); createIfStmt(thenStmt, body); buildMethodCall(method, body, local, generator); body.getUnits().add(thenStmt); } }
public AndroidMethod(SootMethodAndClass methodAndClass) { super(methodAndClass); this.permissions = Collections.emptySet(); }
/** * Creates a new instance of the MethodSourceSinkDefinition class */ public SourceSinkDefinition(SootMethodAndClass am) { this(am, null, null, null); }
/** * Adds calls to the callback methods defined in the application class * @param applicationClass The class in which the user-defined application * is implemented * @param applicationLocal The local containing the instance of the * user-defined application */ private void addApplicationCallbackMethods() { if (!this.callbackFunctions.containsKey(applicationClass.getName())) return; // Do not try to generate calls to methods in non-concrete classes if (applicationClass.isAbstract()) return; if (applicationClass.isPhantom()) { System.err.println("Skipping possible application callbacks in " + "phantom class " + applicationClass); return; } for (String methodSig : this.callbackFunctions.get(applicationClass.getName())) { SootMethodAndClass methodAndClass = SootMethodRepresentationParser.v().parseSootMethodString(methodSig); // We do not consider lifecycle methods which are directly inserted // at their respective positions if (AndroidEntryPointConstants.getApplicationLifecycleMethods().contains (methodAndClass.getSubSignature())) continue; SootMethod method = findMethod(Scene.v().getSootClass(methodAndClass.getClassName()), methodAndClass.getSubSignature()); // If we found no implementation or if the implementation we found // is in a system class, we skip it. Note that null methods may // happen since all callback interfaces for application callbacks // are registered under the name of the application class. if (method == null) continue; if (method.getDeclaringClass().getName().startsWith("android.") || method.getDeclaringClass().getName().startsWith("java.")) continue; // Get the local instance of the target class Local local = this.localVarsForClasses.get(methodAndClass.getClassName()); if (local == null) { System.err.println("Could not create call to application callback " + method.getSignature() + ". Local was null."); continue; } // Add a conditional call to the method JNopStmt thenStmt = new JNopStmt(); createIfStmt(thenStmt); buildMethodCall(method, body, local, generator); body.getUnits().add(thenStmt); } }
@Override protected SootMethod createDummyMainInternal(SootMethod mainMethod) { Map<String, Set<String>> classMap = SootMethodRepresentationParser.v().parseClassNames(methodsToCall, false); // create new class: Body body = mainMethod.getActiveBody(); LocalGenerator generator = new LocalGenerator(body); HashMap<String, Local> localVarsForClasses = new HashMap<String, Local>(); // create constructors: for(String className : classMap.keySet()){ SootClass createdClass = Scene.v().forceResolve(className, SootClass.BODIES); createdClass.setApplicationClass(); Local localVal = generateClassConstructor(createdClass, body); if (localVal == null) { logger.warn("Cannot generate constructor for class: {}", createdClass); continue; } localVarsForClasses.put(className, localVal); } // add entrypoint calls int conditionCounter = 0; JNopStmt startStmt = new JNopStmt(); JNopStmt endStmt = new JNopStmt(); Value intCounter = generator.generateLocal(IntType.v()); body.getUnits().add(startStmt); for (Entry<String, Set<String>> entry : classMap.entrySet()){ Local classLocal = localVarsForClasses.get(entry.getKey()); for (String method : entry.getValue()){ SootMethodAndClass methodAndClass = SootMethodRepresentationParser.v().parseSootMethodString(method); SootMethod currentMethod = findMethod(Scene.v().getSootClass(methodAndClass.getClassName()), methodAndClass.getSubSignature()); if (currentMethod == null) { logger.warn("Entry point not found: {}", method); continue; } JEqExpr cond = new JEqExpr(intCounter, IntConstant.v(conditionCounter)); conditionCounter++; JNopStmt thenStmt = new JNopStmt(); JIfStmt ifStmt = new JIfStmt(cond, thenStmt); body.getUnits().add(ifStmt); buildMethodCall(currentMethod, body, classLocal, generator); body.getUnits().add(thenStmt); } } body.getUnits().add(endStmt); JGotoStmt gotoStart = new JGotoStmt(startStmt); body.getUnits().add(gotoStart); body.getUnits().add(Jimple.v().newReturnVoidStmt()); NopEliminator.v().transform(body); eliminateSelfLoops(body); return mainMethod; }
@Override protected SootMethod createDummyMainInternal(SootMethod mainMethod) { Map<String, Set<String>> classMap = SootMethodRepresentationParser.v().parseClassNames(methodsToCall, false); // create new class: Body body = mainMethod.getActiveBody(); LocalGenerator generator = new LocalGenerator(body); // Create the classes for (String className : classMap.keySet()) { SootClass createdClass = Scene.v().forceResolve(className, SootClass.BODIES); createdClass.setApplicationClass(); Local localVal = generateClassConstructor(createdClass, body); if (localVal == null) { logger.warn("Cannot generate constructor for class: {}", createdClass); continue; } // Create the method calls for (String method : classMap.get(className)) { SootMethodAndClass methodAndClass = SootMethodRepresentationParser.v().parseSootMethodString(method); SootMethod methodToInvoke = findMethod(Scene.v().getSootClass( methodAndClass.getClassName()), methodAndClass.getSubSignature()); if (methodToInvoke == null) System.err.println("Method " + methodAndClass + " not found, skipping"); else if (methodToInvoke.isConcrete()) { // Load the method methodToInvoke.retrieveActiveBody(); buildMethodCall(methodToInvoke, body, localVal, generator); } } } // Jimple needs an explicit return statement body.getUnits().add(Jimple.v().newReturnVoidStmt()); return mainMethod; }
private void calculateCallbackMethods(ARSCFileParser resParser, LayoutFileParser lfp) throws IOException { AbstractCallbackAnalyzer jimpleClass = null; boolean hasChanged = true; while (hasChanged) { hasChanged = false; // Create the new iteration of the main method soot.G.reset(); initializeSoot(true); createMainMethod(); if (jimpleClass == null) { // Collect the callback interfaces implemented in the app's // source code jimpleClass = callbackClasses == null ? new DefaultCallbackAnalyzer(config, entrypoints, callbackFile) : new DefaultCallbackAnalyzer(config, entrypoints, callbackClasses); jimpleClass.collectCallbackMethods(); // Find the user-defined sources in the layout XML files. This // only needs to be done once, but is a Soot phase. lfp.parseLayoutFile(apkFileLocation); } else { jimpleClass.collectCallbackMethodsIncremental(); } // Run the soot-based operations PackManager.v().getPack("wjpp").apply(); PackManager.v().getPack("cg").apply(); PackManager.v().getPack("wjtp").apply(); // Collect the results of the soot-based phases for (Entry<String, Set<SootMethodAndClass>> entry : jimpleClass.getCallbackMethods() .entrySet()) { Set<SootMethodAndClass> curCallbacks = this.callbackMethods.get(entry.getKey()); if (curCallbacks != null) { if (curCallbacks.addAll(entry.getValue())) { hasChanged = true; } } else { this.callbackMethods.put(entry.getKey(), new HashSet<>(entry.getValue())); hasChanged = true; } } if (entrypoints.addAll(jimpleClass.getDynamicManifestComponents())) { hasChanged = true; } } // Collect the XML-based callback methods collectXmlBasedCallbackMethods(resParser, lfp, jimpleClass); }
/** * Collects the XML-based callback methods, e.g., Button.onClick() declared in layout XML files * * @param resParser The ARSC resource parser * @param lfp The layout file parser * @param jimpleClass The analysis class that gives us a mapping between layout IDs and components */ private void collectXmlBasedCallbackMethods(ARSCFileParser resParser, LayoutFileParser lfp, AbstractCallbackAnalyzer jimpleClass) { // Collect the XML-based callback methods for (Entry<String, Set<Integer>> lcentry : jimpleClass.getLayoutClasses().entrySet()) { final SootClass callbackClass = Scene.v().getSootClass(lcentry.getKey()); for (Integer classId : lcentry.getValue()) { AbstractResource resource = resParser.findResource(classId); if (resource instanceof StringResource) { final String layoutFileName = ((StringResource) resource).getValue(); // Add the callback methods for the given class Set<String> callbackMethods = lfp.getCallbackMethods().get(layoutFileName); if (callbackMethods != null) { for (String methodName : callbackMethods) { final String subSig = "void " + methodName + "(android.view.View)"; // The callback may be declared directly in the // class // or in one of the superclasses SootClass currentClass = callbackClass; while (true) { SootMethod callbackMethod = currentClass.getMethodUnsafe(subSig); if (callbackMethod != null) { addCallbackMethod(callbackClass.getName(), new AndroidMethod(callbackMethod)); break; } if (!currentClass.hasSuperclass()) { System.err.println("Callback method " + methodName + " not found in class " + callbackClass.getName()); break; } currentClass = currentClass.getSuperclass(); } } } // For user-defined views, we need to emulate their // callbacks Set<LayoutControl> controls = lfp.getUserControls().get(layoutFileName); if (controls != null) { for (LayoutControl lc : controls) { registerCallbackMethodsForView(callbackClass, lc); } } } else { System.err.println("Unexpected resource type for layout class"); } } } // Add the callback methods as sources and sinks { Set<SootMethodAndClass> callbacksPlain = new HashSet<SootMethodAndClass>(); for (Set<SootMethodAndClass> set : this.callbackMethods.values()) { callbacksPlain.addAll(set); } System.out.println("Found " + callbacksPlain.size() + " callback methods for " + this.callbackMethods.size() + " components"); } }
/** * Soot requires a main method, so we create a dummy method which calls all entry functions. * * @param classMap * the methods to call (signature as String) * @param createdClass * the class which contains the methods * @return list of entryPoints */ @Override protected SootMethod createDummyMainInternal(List<String> methods) { Map<String, List<String>> classMap = SootMethodRepresentationParser.v().parseClassNames(methods, false); // create new class: JimpleBody body = Jimple.v().newBody(); SootMethod mainMethod = createEmptyMainMethod(body); LocalGenerator generator = new LocalGenerator(body); HashMap<String, Local> localVarsForClasses = new HashMap<String, Local>(); // create constructors: for(String className : classMap.keySet()){ SootClass createdClass = Scene.v().forceResolve(className, SootClass.BODIES); createdClass.setApplicationClass(); Local localVal = generateClassConstructor(createdClass, body); if (localVal == null) { logger.warn("Cannot generate constructor for class: {}", createdClass); continue; } localVarsForClasses.put(className, localVal); } // add entrypoint calls int conditionCounter = 0; JNopStmt startStmt = new JNopStmt(); JNopStmt endStmt = new JNopStmt(); Value intCounter = generator.generateLocal(IntType.v()); body.getUnits().add(startStmt); for (Entry<String, List<String>> entry : classMap.entrySet()){ Local classLocal = localVarsForClasses.get(entry.getKey()); for (String method : entry.getValue()){ SootMethodAndClass methodAndClass = SootMethodRepresentationParser.v().parseSootMethodString(method); SootMethod currentMethod = findMethod(Scene.v().getSootClass(methodAndClass.getClassName()), methodAndClass.getSubSignature()); if (currentMethod == null) { logger.warn("Entry point not found: {}", method); continue; } JEqExpr cond = new JEqExpr(intCounter, IntConstant.v(conditionCounter)); conditionCounter++; JNopStmt thenStmt = new JNopStmt(); JIfStmt ifStmt = new JIfStmt(cond, thenStmt); body.getUnits().add(ifStmt); buildMethodCall(currentMethod, body, classLocal, generator); body.getUnits().add(thenStmt); } } body.getUnits().add(endStmt); JGotoStmt gotoStart = new JGotoStmt(startStmt); body.getUnits().add(gotoStart); body.getUnits().add(Jimple.v().newReturnVoidStmt()); return mainMethod; }
/** * Creates a new instance of the {@link AndroidSourceSinkManager} class with strong matching, i.e. the methods in * the code must exactly match those in the list. * * @param sources * The list of source methods * @param sinks * The list of sink methods * @param callbackMethods * The list of callback methods whose parameters are sources through which the application receives data * from the operating system * @param weakMatching * True for weak matching: If an entry in the list has no return type, it matches arbitrary return types * if the rest of the method signature is compatible. False for strong matching: The method signature in * the code exactly match the one in the list. * @param layoutMatching * Specifies whether and how to use Android layout components as sources for the information flow * analysis * @param layoutControls * A map from reference identifiers to the respective Android layout controls */ public AccessPathBasedSourceSinkManager(Set<SourceSinkDefinition> sources, Set<SourceSinkDefinition> sinks, Set<SootMethodAndClass> callbackMethods, LayoutMatchingMode layoutMatching, Map<Integer, LayoutControl> layoutControls) { super(sources, sinks, callbackMethods, layoutMatching, layoutControls); }
/** * Gets the method for which this object defines sources and sinks * @return The method for which this object defines sources and sinks */ public SootMethodAndClass getMethod() { return this.method; }
/** * Creates a new instance of the {@link AndroidSourceSinkManager} class with * either strong or weak matching. * * @param sources * The list of source methods * @param sinks * The list of sink methods */ public AndroidSourceSinkManager(Set<SourceSinkDefinition> sources, Set<SourceSinkDefinition> sinks) { this(sources, sinks, Collections.<SootMethodAndClass>emptySet(), LayoutMatchingMode.NoMatch, null); }