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); } }
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"); } }