private AndroidMethod parseMethod(Matcher m) { assert(m.group(1) != null && m.group(2) != null && m.group(3) != null && m.group(4) != null); int groupIdx = 1; //class name String className = m.group(groupIdx++).trim(); //return type String returnType = m.group(groupIdx++).trim(); //method name String methodName = m.group(groupIdx++).trim(); //method parameter List<String> methodParameters = new ArrayList<String>(); String params = m.group(groupIdx++).trim(); if (!params.isEmpty()) for (String parameter : params.split(",")) methodParameters.add(parameter.trim()); //create method signature return new AndroidMethod(methodName, methodParameters, returnType, className); }
/** * Testing AndroidMethod.createfromSignature for signatures without * surrounding <> */ @Test public void signatureTest2() { String methodName = "sourceTest"; String returnType = "void"; String className = "com.example.androidtest.Sources"; List<String> methodParameters = new ArrayList<String>(); methodParameters.add("com.example.androidtest.MyTestObject"); methodParameters.add("int"); AndroidMethod am1 = new AndroidMethod(methodName, methodParameters, returnType, className); String sig = am1.getSignature(); sig = sig.substring(1, sig.length() - 1); AndroidMethod am2 = AndroidMethod.createFromSignature(sig); Assert.assertEquals(am1, am2); }
/** * This method iterates over all sources from the FlowDroid-results and extracts the * category of the specific source. If there is no category found, it will return an empty set, * otherwise the correct categories will be added. * @param sourcesInfo: all possible sources from which we try to identify the category * @return: set of categories for specific sink */ private Set<String> getDataIdList(Set<ResultSourceInfo> sourcesInfo){ Set<String> dataIdList = new HashSet<String>(); for(ResultSourceInfo sInfo : sourcesInfo){ if(sInfo.getSource().containsInvokeExpr()){ InvokeExpr invExpr = sInfo.getSource().getInvokeExpr(); for(SourceSinkDefinition meth : sources) { AndroidMethod am = (AndroidMethod) meth.getMethod(); if(am.getSignature().equals(invExpr.getMethod().getSignature())) { dataIdList.add(am.getCategory().toString()); } } } else if (isSourceInfoParameter(sInfo)){ dataIdList.add(unknownCategory); } else throw new RuntimeException("Currently not supported"); } return dataIdList; }
private String getSourceCategory(ResultSourceInfo sourceInfo){ if(sourceInfo.getSource().containsInvokeExpr()){ InvokeExpr invExpr = sourceInfo.getSource().getInvokeExpr(); for(SourceSinkDefinition meth : sources) { AndroidMethod am = (AndroidMethod) meth.getMethod(); if(am.getSignature().equals(invExpr.getMethod().getSignature())){ return am.getCategory().toString(); } } } else if(isSourceInfoParameter(sourceInfo)){ return unknownCategory; } else throw new RuntimeException("Currently not supported"); return null; }
/** * Return true if the method corresponding to the source 'si' is an * Inter Component Communication source method such as "Intent.getExtras()". * @param si * @param cfg * @return */ private boolean isInterComponentSourceNoCallback(ResultSourceInfo si, BiDiInterproceduralCFG<Unit, SootMethod> cfg){ if(!si.getSource().containsInvokeExpr()) return false; InvokeExpr invExpr = si.getSource().getInvokeExpr(); SootMethod sm = invExpr.getMethod(); for(SourceSinkDefinition meth : sources){ AndroidMethod am = (AndroidMethod) meth.getMethod(); if(am.getCategory() == CATEGORY.INTER_APP_COMMUNICATION){ if(am.getSubSignature().equals(sm.getSubSignature())) { log.info("source is: "+ am); return true; } } } return false; }
public Set<AndroidMethod> getAndroidSourcesMethods(String sourceFile){ Set<AndroidMethod> sources = new HashSet<AndroidMethod>(); Set<CATEGORY> categories = new HashSet<CATEGORY>(); if(Settings.instance.sourceCategories.equals(CATEGORY.ALL)) categories.add(CATEGORY.ALL); else{ for(String category : Settings.instance.sourceCategories.split("\\|")) categories.add(CATEGORY.valueOf(category)); } try{ CategorizedAndroidSourceSinkParser parser = new CategorizedAndroidSourceSinkParser(categories, sourceFile, true, false); for (AndroidMethod am : parser.parse()){ if (am.isSource()) sources.add(am); } }catch(Exception ex){ ex.printStackTrace(); System.exit(1); } return sources; }
public Set<AndroidMethod> getAndroidSinkMethods(String sinkFile){ Set<AndroidMethod> sinks = new HashSet<AndroidMethod>(); Set<CATEGORY> categories = new HashSet<CATEGORY>(); for(String category : Settings.instance.sinkCategories.split("\\|")) categories.add(CATEGORY.valueOf(category)); try{ CategorizedAndroidSourceSinkParser parser = new CategorizedAndroidSourceSinkParser(categories, sinkFile, false, true); for (AndroidMethod am : parser.parse()){ if (am.isSink()) sinks.add(am); } }catch(Exception ex){ ex.printStackTrace(); System.exit(1); } return sinks; }
public Set<AndroidMethod> parse() throws IOException { Set<AndroidMethod> methods = new HashSet<AndroidMethod>(); BufferedReader rdr = readFile(); if (rdr == null) throw new RuntimeException("Could not read source/sink file"); String line = null; Pattern p = Pattern.compile(regex); while ((line = rdr.readLine()) != null) { Matcher m = p.matcher(line); if(m.find()) { CATEGORY cat = CATEGORY.valueOf(m.group(5)); if(categories.contains(CATEGORY.ALL) || categories.contains(cat)){ AndroidMethod method = parseMethod(m); method.setCategory(cat); if(isSources) method.setSource(true); else if(isSinks) method.setSink(true); else throw new RuntimeException("Oops, something went all wonky!"); methods.add(method); } } } try { if (rdr != null) rdr.close(); } catch (IOException e) { e.printStackTrace(); } return methods; }
private void parse() { sourceList = new HashSet<SourceSinkDefinition>(INITIAL_SET_SIZE); sinkList = new HashSet<SourceSinkDefinition>(INITIAL_SET_SIZE); neitherList = new HashSet<SourceSinkDefinition>(INITIAL_SET_SIZE); BufferedReader rdr = readFile(); String line = null; Pattern p = Pattern.compile(regex); String currentPermission = null; try { while ((line = rdr.readLine()) != null) { if(line.startsWith("Permission:")) currentPermission = line.substring(11); else{ Matcher m = p.matcher(line); if(m.find()) { AndroidMethod singleMethod = parseMethod(m, currentPermission); if (singleMethod != null) { if (singleMethod.isSource()) addToList(sourceList, singleMethod, currentPermission); else if (singleMethod.isSink()) addToList(sinkList, singleMethod, currentPermission); else if (singleMethod.isNeitherNor()) addToList(neitherList, singleMethod, currentPermission); } } } } if (rdr != null) rdr.close(); } catch (IOException e) { e.printStackTrace(); } }
private void addToList(Set<SourceSinkDefinition> sourceList, AndroidMethod singleMethod, String currentPermission) { SourceSinkDefinition def = new SourceSinkDefinition(singleMethod); if (!sourceList.add(def)) { for (SourceSinkDefinition ssdef : sourceList) if (ssdef.getMethod().equals(singleMethod)) { singleMethod.addPermission(currentPermission); break; } } }
/** * 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)); }
@Test public void signaturTest() { String methodName = "sourceTest"; String returnType = "void"; String className = "com.example.androidtest.Sources"; List<String> methodParameters = new ArrayList<String>(); methodParameters.add("com.example.androidtest.MyTestObject"); methodParameters.add("int"); AndroidMethod am1 = new AndroidMethod(methodName, methodParameters, returnType, className); String sig = am1.getSignature(); AndroidMethod am2 = AndroidMethod.createFromSignature(sig); Assert.assertEquals(am1, am2); }
/** * Testing AndroidMethod.createfromSignature if parameters are switched */ @Test public void switchedParameterTest() { String methodName = "poll"; String returnType = "java.lang.Object"; String className = "java.util.concurrent.LinkedBlockingQueue"; List<String> methodParameters = new ArrayList<String>(); methodParameters.add("java.util.concurrent.TimeUnit"); methodParameters.add("long"); AndroidMethod am1 = new AndroidMethod(methodName, methodParameters, returnType, className); String sig = "<java.util.concurrent.LinkedBlockingQueue: java.lang.Object poll(long,java.util.concurrent.TimeUnit)>"; AndroidMethod am2 = AndroidMethod.createFromSignature(sig); Assert.assertNotEquals(am1, am2); }
/** * 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 Set<AndroidMethod> parse() throws IOException{ Set<AndroidMethod> methodList = new HashSet<AndroidMethod>(INITIAL_SET_SIZE); Pattern p = Pattern.compile(regex); //Pattern pNoRet = Pattern.compile(regexNoRet); for(String line : this.data){ if (line.isEmpty() || line.startsWith("%")) continue; Matcher m = p.matcher(line); if(m.find()) { AndroidMethod singleMethod = parseMethod(m, true); methodList.add(singleMethod); } else { // Matcher mNoRet = pNoRet.matcher(line); // if(mNoRet.find()) { // AndroidMethod singleMethod = parseMethod(mNoRet, false); // methodList.add(singleMethod); // } // else System.err.println("Line does not match: " + line); } } return methodList; }
public boolean isICCMethod(SootMethod sm) { Set<AndroidMethod> amSet = AndroidIPCManager.ipcAMethods; String rightSm = sm.toString().split(":")[1]; for (AndroidMethod am: amSet) { String amRight = am.getSignature().split(":")[1]; if (amRight.equals(rightSm)) { return true; } } return false; }
public AndroidIPCManager(Set<AndroidMethod> ipcAMethods, String appPackageName) { AndroidIPCManager.ipcAMethods = ipcAMethods; this.appPackageNames = new String[1]; this.appPackageNames[0] = appPackageName; System.out.println("Created a AndroidIPCManager with " + AndroidIPCManager.ipcAMethods.size() + " IPC methods for " + " app. package name '" + appPackageNames[0] + "'"); }
public void setIPCMethods(String ipcFile) throws IOException { IPCMethodParser ipc_parser = IPCMethodParser.fromFile(ipcFile); System.out.println("add ipc methods!"); for (AndroidMethod am: ipc_parser.parse()) { System.out.println("add "+ am.getSignature()); ipcAMethods.add(am); } }
public static boolean isIccMethod(SootMethod sm) { if (isAndroidComponent(sm)) { for (AndroidMethod am : AndroidIPCManager.ipcAMethods) { //same method and same parameters if (! am.getMethodName().equals(sm.getName())) { continue; } List<String> params = am.getParameters(); List<Type> types = sm.getParameterTypes(); if (params.size() != types.size()) { continue; } for (int i = 0; i < params.size(); i++) { String p1 = params.get(i); String p2 = types.get(i).toString(); if (! p1.equals(p2)) { continue; } } return true; } } return false; }
private boolean isMethodInterComponentSink(SootMethod sm) { for (SourceSinkDefinition meth : sinks) { AndroidMethod am = (AndroidMethod) meth.getMethod(); if(am.getCategory() == CATEGORY.INTER_APP_COMMUNICATION){ if(am.getSubSignature().equals(sm.getSubSignature())) return true; } } return false; }
/** * manual verification of the parser result * * @throws IOException * @throws XmlPullParserException */ @Test public void verifyParserResultTest() throws IOException, XmlPullParserException { // parsing data from xml file String xmlFile = "testXmlParser/complete.xml"; XMLSourceSinkParser newParser = XMLSourceSinkParser.fromFile(xmlFile); Set<SourceSinkDefinition> sourceListParser = newParser.getSources(); Set<SourceSinkDefinition> sinkListParser = newParser.getSinks(); // create two methods with reference data String methodName = "sourceTest"; String returnType = "java.lang.String"; String className = "com.example.androidtest.Sources"; List<String> methodParameters = new ArrayList<String>(); methodParameters.add("com.example.androidtest.MyTestObject"); methodParameters.add("int"); AndroidMethod am1 = new AndroidMethod(methodName, methodParameters, returnType, className); methodParameters = new ArrayList<String>(); methodParameters.add("double"); methodParameters.add("double"); AndroidMethod am2 = new AndroidMethod("sinkTest", methodParameters, "void", "com.example.androidtest.Sinks"); // Check the loaded access paths (sources) Assert.assertEquals(1, sourceListParser.size()); SourceSinkDefinition loadedSource = sourceListParser.iterator().next(); Assert.assertEquals(am1, loadedSource.getMethod()); Assert.assertEquals(0, loadedSource.getBaseObjectCount()); Assert.assertEquals(2, loadedSource.getParameterCount()); Assert.assertEquals(1, loadedSource.getReturnValueCount()); // Check the loaded access paths (sinks) Assert.assertEquals(2, sinkListParser.size()); for (SourceSinkDefinition def : sinkListParser) { Assert.assertTrue(def.getMethod().equals(am1) || def.getMethod().equals(am2)); if (def.getMethod().equals(am1)) { Assert.assertEquals(1, def.getBaseObjectCount()); Assert.assertEquals(1, def.getParameterCount()); } else if (def.getMethod().equals(am2)) { Assert.assertEquals(1, def.getParameterCount()); } else Assert.fail("should never happen"); } }
/** * 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"); } }
public Set<AndroidMethod> getIPCMethods() { return ipcAMethods; }
public static void main(String[] args) { startTime = System.currentTimeMillis(); long d = 0; Set<AndroidMethod> sources, sinks; log.info("Starting Intrumentation-PEP"); //arguments will be set Settings.instance.parseCommandLineArgs(args); log.info("Initialize Soot and FlowDroid."); //Soot is initialized Settings.instance.initialiseSoot(); //clean the sootOutput dir before start Util.clearSootOutputJimpleDir(); //parse the eventInformation.xml file in order to extract all information about the //events we will cover EventInformationParser eventInfoParser = new EventInformationParser(); Map<String, EventInformation> eventInformation = eventInfoParser.parseEventInformation(); if (log.isDebugEnabled()) { log.debug("All Event Information:"); for (String k: eventInformation.keySet()) { log.debug("event information for "+ k); log.debug(""+ eventInformation.get(k)); } log.debug(""); } SourcesSinks sourcesSinks = new SourcesSinks(); //get Android sources sources = sourcesSinks.getAndroidSourcesMethods(Settings.instance.sourceFile); //get Android sinks sinks = sourcesSinks.getAndroidSinkMethods(Settings.instance.sinkFile); //get SetupApplication SetupApplication setupApp = new SetupApplication(Settings.instance.androidJar == null ? Settings.instance.androidPlatforms : Settings.instance.androidJar, Settings.instance.apkFile); try{ //initialize SetupApplication setupApp.calculateSourcesSinksEntrypoints(sources, sinks); }catch(Exception ex){ ex.printStackTrace(); System.exit(0); } d = (System.currentTimeMillis() - startTime); log.info("Initialization done. Duration: "+ d +" ms."); log.info("Starting taint analysis and bytecode instrumentation."); startTime = System.currentTimeMillis(); runFlowDroid(setupApp, eventInformation); d = (System.currentTimeMillis() - startTime); log.info("Taint analysis and bytecode instrumentation have finished. Duration: " + d +" ms"); }
Set<AndroidMethod> parse() throws IOException;