/** * Finds the given resource in the given package * * @param resName * The name of the resource to retrieve * @param resID * @param packageName * The name of the package in which to look for the resource * @return The specified resource if available, otherwise null */ private AbstractResource findResource(String resName, String resID, String packageName) { // Find the correct package for (ARSCFileParser.ResPackage pkg : this.resourcePackages) { // If we don't have any package specification, we pick the app's // default package boolean matches = (packageName == null || packageName.isEmpty()) && pkg.getPackageName().equals(this.appPackageName); matches |= pkg.getPackageName().equals(packageName); if (!matches) continue; // We have found a suitable package, now look for the resource for (ARSCFileParser.ResType type : pkg.getDeclaredTypes()) if (type.getTypeName().equals(resID)) { AbstractResource res = type.getFirstResource(resName); return res; } } return null; }
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); } }
public LayoutFileParser(String packageName, ARSCFileParser resParser) { this.packageName = packageName; this.resParser = resParser; }
/** * Finds the last assignment to the given local representing a resource ID * by searching upwards from the given statement * * @param stmt * The statement from which to look backwards * @param local * The variable for which to look for assignments * @return The last value assigned to the given variable */ private Integer findLastResIDAssignment(Stmt stmt, Local local, BiDiInterproceduralCFG<Unit, SootMethod> cfg, Set<Stmt> doneSet) { if (!doneSet.add(stmt)) return null; // If this is an assign statement, we need to check whether it changes // the variable we're looking for if (stmt instanceof AssignStmt) { AssignStmt assign = (AssignStmt) stmt; if (assign.getLeftOp() == local) { // ok, now find the new value from the right side if (assign.getRightOp() instanceof IntConstant) return ((IntConstant) assign.getRightOp()).value; else if (assign.getRightOp() instanceof FieldRef) { SootField field = ((FieldRef) assign.getRightOp()).getField(); for (Tag tag : field.getTags()) if (tag instanceof IntegerConstantValueTag) return ((IntegerConstantValueTag) tag).getIntValue(); else System.err.println("Constant " + field + " was of unexpected type"); } else if (assign.getRightOp() instanceof InvokeExpr) { InvokeExpr inv = (InvokeExpr) assign.getRightOp(); if (inv.getMethod().getName().equals("getIdentifier") && inv.getMethod().getDeclaringClass().getName().equals("android.content.res.Resources") && this.resourcePackages != null) { // The right side of the assignment is a call into the // well-known // Android API method for resource handling if (inv.getArgCount() != 3) { System.err.println("Invalid parameter count for call to getIdentifier"); return null; } // Find the parameter values String resName = ""; String resID = ""; String packageName = ""; // In the trivial case, these values are constants if (inv.getArg(0) instanceof StringConstant) resName = ((StringConstant) inv.getArg(0)).value; if (inv.getArg(1) instanceof StringConstant) resID = ((StringConstant) inv.getArg(1)).value; if (inv.getArg(2) instanceof StringConstant) packageName = ((StringConstant) inv.getArg(2)).value; else if (inv.getArg(2) instanceof Local) packageName = findLastStringAssignment(stmt, (Local) inv.getArg(2), cfg); else { System.err.println("Unknown parameter type in call to getIdentifier"); return null; } // Find the resource ARSCFileParser.AbstractResource res = findResource(resName, resID, packageName); if (res != null) return res.getResourceID(); } } } } // Continue the search upwards for (Unit pred : cfg.getPredsOf(stmt)) { if (!(pred instanceof Stmt)) continue; Integer lastAssignment = findLastResIDAssignment((Stmt) pred, local, cfg, doneSet); if (lastAssignment != null) return lastAssignment; } return null; }
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"); } }