@SuppressWarnings("unchecked") private static void addMaxPrioForSMSReceiver(ProcessManifest manifest) { for(AXmlNode receiver : manifest.getReceivers()) { for(AXmlNode receiverChild : receiver.getChildren()) { if(receiverChild.getTag().equals("intent-filter")) { //search for SMS receiver for(AXmlNode childChild : receiverChild.getChildren()) { if(childChild.getTag().equals("action")) { if(childChild.hasAttribute("name") && ((String)childChild.getAttribute("name").getValue()).equalsIgnoreCase("android.provider.Telephony.SMS_RECEIVED")){ //prepare the priority filter if(receiverChild.hasAttribute("priority")) ((AXmlAttribute<Integer>)receiverChild.getAttribute("priority")).setValue(Integer.MAX_VALUE); else { AXmlAttribute<Integer> attr = new AXmlAttribute<Integer>("priority", Integer.MAX_VALUE, ANDROID_NAMESPACE); receiverChild.addAttribute(attr); } } } } } } } }
private boolean isComponentExported(AXmlNode node) { boolean accessible = false; AXmlAttribute<?> attrEnabled = node.getAttribute("enabled"); if (attrEnabled == null || !attrEnabled.getValue().equals(Boolean.FALSE)) { AXmlAttribute<?> attrExported = node.getAttribute("exported"); if (attrExported != null){ if(attrExported.getValue().equals(Boolean.TRUE)) { accessible = true; } else if(attrExported.getValue().equals(Boolean.FALSE)) { accessible = false; } } else { if(node.getChildrenWithTag("intent-filter").size() != 0) { accessible = true; } } } return accessible; }
private void checkAndAddComponent(Set<String> entryPoints, AXmlNode node) { AXmlAttribute<?> attrEnabled = node.getAttribute("enabled"); if (attrEnabled == null || !attrEnabled.getValue().equals(Boolean.FALSE)) { AXmlAttribute<?> attr = node.getAttribute("name"); if (attr != null) entryPoints.add(expandClassName((String) attr.getValue())); else { // This component does not have a name, so this might be obfuscated // malware. We apply a heuristic. for (Entry<String, AXmlAttribute<?>> a : node.getAttributes().entrySet()) if (a.getValue().getName().isEmpty() && a.getValue().getType() == AxmlVisitor.TYPE_STRING) { String name = (String) a.getValue().getValue(); if (isValidComponentName(name)) entryPoints.add(expandClassName(name)); } } } }
/** * Gets the permissions this application requests * @return The permissions requested by this application * @return */ public Set<String> getPermissions() { List<AXmlNode> usesPerms = this.manifest.getChildrenWithTag("uses-permission"); Set<String> permissions = new HashSet<String>(); for (AXmlNode perm : usesPerms) { AXmlAttribute<?> attr = perm.getAttribute("name"); if (attr != null) permissions.add((String) attr.getValue()); else { // The required "name" attribute is missing, so we collect all empty // attributes as a best-effort solution for broken malware apps for (AXmlAttribute<?> a : perm.getAttributes().values()) if (a.getType() == AxmlVisitor.TYPE_STRING && a.getName().isEmpty()) permissions.add((String) a.getValue()); } } return permissions; }
private static void addComponentCallerService(ProcessManifest androidManifest) { AXmlNode componentCallerService = new AXmlNode("service", null, androidManifest.getApplication()); AXmlAttribute<String> nameAttribute = new AXmlAttribute<String>("name", UtilInstrumenter.COMPONENT_CALLER_SERVICE_HELPER, ANDROID_NAMESPACE); AXmlAttribute<String> exportedAttribute = new AXmlAttribute<String>("exported", "false", ANDROID_NAMESPACE); componentCallerService.addAttribute(nameAttribute); componentCallerService.addAttribute(exportedAttribute); androidManifest.addService(componentCallerService); }
private static void addHookinHelperAsApplicationIfNecessary(ProcessManifest androidManifest){ AXmlNode application = androidManifest.getApplication(); if(!application.hasAttribute("name")) { AXmlAttribute<String> nameAttribute = new AXmlAttribute<String>("name", UtilInstrumenter.HELPER_APPLICATION_FOR_HOOKING, ANDROID_NAMESPACE); application.addAttribute(nameAttribute); } }
private static void addTracingService(ProcessManifest androidManifest) { AXmlNode tracingService = new AXmlNode("service", null, androidManifest.getApplication()); AXmlAttribute<String> nameAttribute = new AXmlAttribute<String>("name", UtilInstrumenter.HELPER_SERVICE_FOR_PATH_TRACKING, ANDROID_NAMESPACE); AXmlAttribute<String> exportedAttribute = new AXmlAttribute<String>("exported", "false", ANDROID_NAMESPACE); tracingService.addAttribute(nameAttribute); tracingService.addAttribute(exportedAttribute); androidManifest.addService(tracingService); }
/** * Parses the attributes required for a layout file inclusion * @param layoutFile The full path and file name of the file being parsed * @param rootNode The AXml node containing the attributes */ private void parseIncludeAttributes(String layoutFile, AXmlNode rootNode) { for (Entry<String, AXmlAttribute<?>> entry : rootNode.getAttributes().entrySet()) { String attrName = entry.getKey().trim(); AXmlAttribute<?> attr = entry.getValue(); if (attrName.equals("layout")) { if ((attr.getType() == AxmlVisitor.TYPE_REFERENCE || attr.getType() == AxmlVisitor.TYPE_INT_HEX) && attr.getValue() instanceof Integer) { // We need to get the target XML file from the binary manifest AbstractResource targetRes = resParser.findResource((Integer) attr.getValue()); if (targetRes == null) { System.err.println("Target resource " + attr.getValue() + " for layout include not found"); return; } if (!(targetRes instanceof StringResource)) { System.err.println("Invalid target node for include tag in layout XML, was " + targetRes.getClass().getName()); return; } String targetFile = ((StringResource) targetRes).getValue(); // If we have already processed the target file, we can // simply copy the callbacks we have found there if (callbackMethods.containsKey(targetFile)) for (String callback : callbackMethods.get(targetFile)) addCallbackMethod(layoutFile, callback); else { // We need to record a dependency to resolve later addToMapSet(includeDependencies, targetFile, layoutFile); } } } } }
private void checkPublicAccessibleCompoenent(Set<String> entryPoints, AXmlNode node) { if (isComponentExported(node)) { AXmlAttribute<?> attr = node.getAttribute("name"); if (attr != null) entryPoints.add(expandClassName((String) attr.getValue())); } }
public String getPackageName() { if (cache_PackageName == null) { AXmlAttribute<?> attr = this.manifest.getAttribute("package"); if (attr != null) cache_PackageName = (String) attr.getValue(); } return cache_PackageName; }
/** * Gets the minimum SDK version on which this application is supposed to run * @return The minimum SDK version on which this application is supposed to run */ public int getMinSdkVersion() { List<AXmlNode> usesSdk = this.manifest.getChildrenWithTag("uses-sdk"); if (usesSdk == null || usesSdk.isEmpty()) return -1; AXmlAttribute<?> attr = usesSdk.get(0).getAttribute("minSdkVersion"); if (attr == null) return -1; if (attr.getValue() instanceof Integer) return (Integer) attr.getValue(); return Integer.getInteger((String) attr.getValue()); }
/** * Gets the target SDK version for which this application was developed * @return The target SDK version for which this application was developed */ public int targetSdkVersion() { List<AXmlNode> usesSdk = this.manifest.getChildrenWithTag("uses-sdk"); if (usesSdk == null || usesSdk.isEmpty()) return -1; AXmlAttribute<?> attr = usesSdk.get(0).getAttribute("targetSdkVersion"); if (attr == null) return -1; if (attr.getValue() instanceof Integer) return (Integer) attr.getValue(); return Integer.getInteger((String) attr.getValue()); }
/** * Gets the minimum SDK version on which this application is supposed to run * @return The minimum SDK version on which this application is supposed to run */ public int getMinSdkVersion() { List<AXmlNode> usesSdk = this.manifest.getChildrenWithTag("uses-sdk"); if (usesSdk == null || usesSdk.isEmpty()) return -1; AXmlAttribute<?> attr = usesSdk.get(0).getAttribute("minSdkVersion"); if (attr == null) return -1; if (attr.getValue() instanceof Integer) return (Integer) attr.getValue(); return Integer.parseInt("" + attr.getValue()); }
/** * Gets the target SDK version for which this application was developed * @return The target SDK version for which this application was developed */ public int targetSdkVersion() { List<AXmlNode> usesSdk = this.manifest.getChildrenWithTag("uses-sdk"); if (usesSdk == null || usesSdk.isEmpty()) return -1; AXmlAttribute<?> attr = usesSdk.get(0).getAttribute("targetSdkVersion"); if (attr == null) return -1; if (attr.getValue() instanceof Integer) return (Integer) attr.getValue(); return Integer.parseInt("" + attr.getValue()); }
/** * Get the package name of the application * @param apkFileLocation * @return */ public static String getApplicationPackageName(String apkFileLocation) { String packageName = null; try { ProcessManifest pm = new ProcessManifest(apkFileLocation); AXmlHandler axmlh = pm.getAXml(); // Find main activity and remove main intent-filter List<AXmlNode> anodes = axmlh.getNodesWithTag("manifest"); for (AXmlNode an: anodes) { boolean hasMain = false; boolean hasLauncher = false; AXmlNode filter = null; AXmlAttribute aname = an.getAttribute("package"); String aval = (String)aname.getValue(); packageName = aval; System.out.println("package: "+ packageName); break; } } catch (IOException | XmlPullParserException ex) { System.err.println("Could not read Android manifest file: " + ex.getMessage()); throw new RuntimeException(ex); } return packageName; }
/** * Parses the layout file with the given root node * @param layoutFile The full path and file name of the file being parsed * @param rootNode The root node from where to start parsing */ private void parseLayoutNode(String layoutFile, AXmlNode rootNode) { if (rootNode.getTag() == null || rootNode.getTag().isEmpty()) { System.err.println("Encountered a null or empty node name " + "in file " + layoutFile + ", skipping node..."); return; } String tname = rootNode.getTag().trim(); if (tname.equals("dummy")) { // dummy root node, ignore it } // Check for inclusions else if (tname.equals("include")) { parseIncludeAttributes(layoutFile, rootNode); } // The "merge" tag merges the next hierarchy level into the current // one for flattening hierarchies. else if (tname.equals("merge")) { // do not consider any attributes of this elements, just // continue with the children } else if (tname.equals("fragment")) { final AXmlAttribute<?> attr = rootNode.getAttribute("name"); if (attr == null) System.err.println("Fragment without class name detected"); else { if (attr.getType() != AxmlVisitor.TYPE_STRING) System.err.println("Invalid targer resource "+attr.getValue()+"for fragment class value"); getLayoutClass(attr.getValue().toString()); } } else { final SootClass childClass = getLayoutClass(tname); if (childClass != null && (isLayoutClass(childClass) || isViewClass(childClass))) parseLayoutAttributes(layoutFile, childClass, rootNode); } // Parse the child nodes for (AXmlNode childNode : rootNode.getChildren()) parseLayoutNode(layoutFile, childNode); }
/** * Parses the layout attributes in the given AXml node * @param layoutFile The full path and file name of the file being parsed * @param layoutClass The class for the attributes are parsed * @param rootNode The AXml node containing the attributes */ private void parseLayoutAttributes(String layoutFile, SootClass layoutClass, AXmlNode rootNode) { boolean isSensitive = false; int id = -1; for (Entry<String, AXmlAttribute<?>> entry : rootNode.getAttributes().entrySet()) { String attrName = entry.getKey().trim(); AXmlAttribute<?> attr = entry.getValue(); // On obfuscated Android malware, the attribute name may be empty if (attrName.isEmpty()) continue; // Check that we're actually working on an android attribute if (!isAndroidNamespace(attr.getNamespace())) continue; // Read out the field data if (attrName.equals("id") && (attr.getType() == AxmlVisitor.TYPE_REFERENCE || attr.getType() == AxmlVisitor.TYPE_INT_HEX)) id = (Integer) attr.getValue(); else if (attrName.equals("password")) { if (attr.getType() == AxmlVisitor.TYPE_INT_HEX) isSensitive = ((Integer) attr.getValue()) != 0; // -1 for true, 0 for false else if (attr.getType() == AxmlVisitor.TYPE_INT_BOOLEAN) isSensitive = (Boolean) attr.getValue(); else throw new RuntimeException("Unknown representation of boolean data type"); } else if (!isSensitive && attrName.equals("inputType") && attr.getType() == AxmlVisitor.TYPE_INT_HEX) { int tp = (Integer) attr.getValue(); isSensitive = ((tp & TYPE_NUMBER_VARIATION_PASSWORD) == TYPE_NUMBER_VARIATION_PASSWORD) || ((tp & TYPE_TEXT_VARIATION_PASSWORD) == TYPE_TEXT_VARIATION_PASSWORD) || ((tp & TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) == TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) || ((tp & TYPE_TEXT_VARIATION_WEB_PASSWORD) == TYPE_TEXT_VARIATION_WEB_PASSWORD); } else if (isActionListener(attrName) && attr.getType() == AxmlVisitor.TYPE_STRING && attr.getValue() instanceof String) { String strData = ((String) attr.getValue()).trim(); addCallbackMethod(layoutFile, strData); } else if (attr.getType() == AxmlVisitor.TYPE_STRING && attrName.equals("text")) { // To avoid unrecognized attribute for "text" field } else if (DEBUG && attr.getType() == AxmlVisitor.TYPE_STRING) { System.out.println("Found unrecognized XML attribute: " + attrName); } } // Register the new user control addToMapSet(this.userControls, layoutFile, new LayoutControl(id, layoutClass, isSensitive)); }
/** * Gets the application's version name as it is displayed to the user * @return The application#s version name as in pretty print */ public String getVersionName() { AXmlAttribute<?> attr = this.manifest.getAttribute("versionName"); return attr == null ? null : (String) attr.getValue(); }
/** * Gets the name of the Android application class * @return The name of the Android application class */ public String getApplicationName() { AXmlAttribute<?> attr = this.application.getAttribute("name"); return attr == null ? null : expandClassName((String) attr.getValue()); }
/** * Gets whether this Android application is enabled * @return True if this application is enabled, otherwise false */ public boolean isApplicationEnabled() { AXmlAttribute<?> attr = this.application.getAttribute("enabled"); return attr == null || !attr.getValue().equals(Boolean.FALSE); }
public boolean isApplicationDebuggable(){ AXmlAttribute<?> attr = this.application.getAttribute("debuggable"); return attr != null && attr.getValue().equals(Boolean.TRUE); }
public boolean isApplicationDisallowBackup(){ AXmlAttribute<?> attr = this.application.getAttribute("allowBackup"); return attr != null && attr.getValue().equals(Boolean.FALSE); }
/** * Adds a new permission to the manifest. * @param complete permission name e.g. "android.permission.INTERNET" */ public void addPermission(String permissionName) { AXmlNode permission = new AXmlNode("uses-permission", null, manifest); AXmlAttribute<String> permissionNameAttr = new AXmlAttribute<String>("name", permissionName, AXmlHandler.ANDROID_NAMESPACE); permission.addAttribute(permissionNameAttr); }
/** * Get the name of the main activity in the AndroidManifest.xml file * @param apkFileLocation * @return */ public static String getMainActivityName(String apkFileLocation) { String mainActivityName = null; try { ProcessManifest pm = new ProcessManifest(apkFileLocation); AXmlHandler axmlh = pm.getAXml(); // Find main activity and remove main intent-filter List<AXmlNode> anodes = axmlh.getNodesWithTag("activity"); for (AXmlNode an: anodes) { boolean hasMain = false; boolean hasLauncher = false; AXmlNode filter = null; AXmlAttribute aname = an.getAttribute("name"); String aval = (String)aname.getValue(); System.out.println("activity: "+ aval); for (AXmlNode ch : an.getChildren()) { System.out.println("children: "+ ch); } List<AXmlNode> fnodes = an.getChildrenWithTag("intent-filter"); for (AXmlNode fn: fnodes) { hasMain = false; hasLauncher = false; // check action List<AXmlNode> acnodes = fn.getChildrenWithTag("action"); for (AXmlNode acn: acnodes) { AXmlAttribute acname = acn.getAttribute("name"); String acval = (String)acname.getValue(); System.out.println("action: "+ acval); if (acval.equals("android.intent.action.MAIN")) { hasMain = true; } } // check category List<AXmlNode> catnodes = fn.getChildrenWithTag("category"); for (AXmlNode catn: catnodes) { AXmlAttribute catname = catn.getAttribute("name"); String catval = (String)catname.getValue(); System.out.println("category: "+ catval); if (catval.equals("android.intent.category.LAUNCHER")) { hasLauncher = true; filter = fn; } } if (hasLauncher && hasMain) { break; } } if (hasLauncher && hasMain) { // replace name with the activity waiting for the connection to the PDP System.out.println("main activity is: "+ aval); System.out.println("excluding filter: "+ filter); filter.exclude(); mainActivityName = aval; break; } } } catch (IOException | XmlPullParserException ex) { System.err.println("Could not read Android manifest file: " + ex.getMessage()); throw new RuntimeException(ex); } return mainActivityName; }
/** * Gets the version code of the application. This code is used to compare * versions for updates. * @return The version code of the application */ public int getVersionCode() { AXmlAttribute<?> attr = this.manifest.getAttribute("versionCode"); return attr == null ? -1 : Integer.getInteger((String) attr.getValue()); }
/** * Gets the version code of the application. This code is used to compare * versions for updates. * @return The version code of the application */ public int getVersionCode() { AXmlAttribute<?> attr = this.manifest.getAttribute("versionCode"); return attr == null ? -1 : Integer.parseInt("" + attr.getValue()); }