diff --git a/baksmali/src/main/java/org/jf/baksmali/Baksmali.java b/baksmali/src/main/java/org/jf/baksmali/Baksmali.java index 24d7416d..a2fc4112 100644 --- a/baksmali/src/main/java/org/jf/baksmali/Baksmali.java +++ b/baksmali/src/main/java/org/jf/baksmali/Baksmali.java @@ -36,7 +36,6 @@ import org.jf.dexlib2.iface.DexFile; import org.jf.util.ClassFileNameHandler; import org.jf.util.IndentingWriter; -import javax.annotation.Nonnull; import java.io.*; import java.util.List; import java.util.concurrent.*; @@ -155,75 +154,4 @@ public class Baksmali { } return true; } - - @Nonnull - public static List getDefaultBootClassPath(int apiLevel) { - if (apiLevel < 9) { - return Lists.newArrayList( - "/system/framework/core.jar", - "/system/framework/ext.jar", - "/system/framework/framework.jar", - "/system/framework/android.policy.jar", - "/system/framework/services.jar"); - } else if (apiLevel < 12) { - return Lists.newArrayList( - "/system/framework/core.jar", - "/system/framework/bouncycastle.jar", - "/system/framework/ext.jar", - "/system/framework/framework.jar", - "/system/framework/android.policy.jar", - "/system/framework/services.jar", - "/system/framework/core-junit.jar"); - } else if (apiLevel < 14) { - return Lists.newArrayList( - "/system/framework/core.jar", - "/system/framework/apache-xml.jar", - "/system/framework/bouncycastle.jar", - "/system/framework/ext.jar", - "/system/framework/framework.jar", - "/system/framework/android.policy.jar", - "/system/framework/services.jar", - "/system/framework/core-junit.jar"); - } else if (apiLevel < 16) { - return Lists.newArrayList( - "/system/framework/core.jar", - "/system/framework/core-junit.jar", - "/system/framework/bouncycastle.jar", - "/system/framework/ext.jar", - "/system/framework/framework.jar", - "/system/framework/android.policy.jar", - "/system/framework/services.jar", - "/system/framework/apache-xml.jar", - "/system/framework/filterfw.jar"); - } else if (apiLevel < 21) { - // this is correct as of api 17/4.2.2 - return Lists.newArrayList( - "/system/framework/core.jar", - "/system/framework/core-junit.jar", - "/system/framework/bouncycastle.jar", - "/system/framework/ext.jar", - "/system/framework/framework.jar", - "/system/framework/telephony-common.jar", - "/system/framework/mms-common.jar", - "/system/framework/android.policy.jar", - "/system/framework/services.jar", - "/system/framework/apache-xml.jar"); - } else { // api >= 21 - // TODO: verify, add new ones? - return Lists.newArrayList( - "/system/framework/core-libart.jar", - "/system/framework/conscrypt.jar", - "/system/framework/okhttp.jar", - "/system/framework/core-junit.jar", - "/system/framework/bouncycastle.jar", - "/system/framework/ext.jar", - "/system/framework/framework.jar", - "/system/framework/telephony-common.jar", - "/system/framework/voip-common.jar", - "/system/framework/ims-common.jar", - "/system/framework/mms-common.jar", - "/system/framework/android.policy.jar", - "/system/framework/apache-xml.jar"); - } - } } diff --git a/baksmali/src/main/java/org/jf/baksmali/DisassembleCommand.java b/baksmali/src/main/java/org/jf/baksmali/DisassembleCommand.java index 90e8d44f..8995887a 100644 --- a/baksmali/src/main/java/org/jf/baksmali/DisassembleCommand.java +++ b/baksmali/src/main/java/org/jf/baksmali/DisassembleCommand.java @@ -35,12 +35,12 @@ import com.beust.jcommander.JCommander; import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; import com.beust.jcommander.validators.PositiveInteger; -import com.google.common.collect.Iterables; +import org.jf.dexlib2.DexFileFactory; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import org.jf.dexlib2.analysis.ClassPath; import org.jf.dexlib2.dexbacked.DexBackedDexFile; -import org.jf.dexlib2.dexbacked.DexBackedOdexFile; +import org.jf.dexlib2.dexbacked.OatFile; import org.jf.dexlib2.iface.DexFile; import org.jf.dexlib2.util.SyntheticAccessorResolver; import org.jf.util.StringWrapper; @@ -48,6 +48,7 @@ import org.jf.util.jcommander.CommaColonParameterSplitter; import javax.annotation.Nonnull; import java.io.File; +import java.io.IOException; import java.util.List; import java.util.Map; @@ -75,7 +76,7 @@ public class DisassembleCommand extends DexInputCommand { "bootclasspath files that baksmali would otherwise perform. This is analogous to Android's " + "BOOTCLASSPATH environment variable.", splitter = CommaColonParameterSplitter.class) - private List bootClassPath = new ArrayList(); + private List bootClassPath = null; @Parameter(names = {"-c", "--classpath"}, description = "A comma/colon separated list of additional jar/oat files to include in the classpath " + @@ -85,7 +86,8 @@ public class DisassembleCommand extends DexInputCommand { private List classPath = Lists.newArrayList(); @Parameter(names = {"-d", "--classpath-dir"}, - description = "baksmali will search these directories in order for any classpath entries.") + description = "A directory to search for classpath files. This option can be used multiple times to " + + "specify multiple directories to search. They will be searched in the order they are provided.") private List classPathDirectories = Lists.newArrayList("."); @Parameter(names = {"--code-offsets"}, @@ -170,16 +172,39 @@ public class DisassembleCommand extends DexInputCommand { return; } - if (inputList.size() > 1) { - System.err.println("Too many files specified"); - jc.usage(jc.getParsedCommand()); - return; + String input = inputList.get(0); + File dexFileFile = new File(input); + String dexFileEntry = null; + if (!dexFileFile.exists()) { + String filename = dexFileFile.getName(); + int colonIndex = filename.indexOf(':'); + + if (colonIndex >= 0) { + dexFileFile = new File(dexFileFile.getParent(), filename.substring(0, colonIndex)); + dexFileEntry = filename.substring(colonIndex + 1); + } + + if (!dexFileFile.exists()) { + System.err.println("Can't find the file " + input); + System.exit(1); + } } - String input = inputList.get(0); - DexBackedDexFile dexFile = loadDexFile(input, apiLevel, experimentalOpcodes); - if (dexFile == null) { - return; + //Read in and parse the dex file + DexBackedDexFile dexFile = null; + try { + dexFile = DexFileFactory.loadDexFile(dexFileFile, dexFileEntry, apiLevel, experimentalOpcodes); + } catch (DexFileFactory.MultipleDexFilesException ex) { + System.err.println(String.format("%s contains multiple dex files. You must specify which one to " + + "disassemble with the -e option", dexFileFile.getName())); + System.err.println("Valid entries include:"); + + for (OatFile.OatDexFile oatDexFile : ex.oatFile.getDexFiles()) { + System.err.println(oatDexFile.filename); + } + System.exit(1); + } catch (IOException ex) { + throw new RuntimeException(ex); } if (showDeodexWarning() && dexFile.hasOdexOpcodes()) { @@ -188,14 +213,6 @@ public class DisassembleCommand extends DexInputCommand { "re-assemble the results unless you deodex it. See \"baksmali help deodex\""); } - if (needsClassPath() && bootClassPath.isEmpty()) { - if (dexFile instanceof DexBackedOdexFile) { - bootClassPath = ((DexBackedOdexFile)dexFile).getDependencies(); - } else { - bootClassPath = Baksmali.getDefaultBootClassPath(apiLevel); - } - } - File outputDirectoryFile = new File(outputDir); if (!outputDirectoryFile.exists()) { if (!outputDirectoryFile.mkdirs()) { @@ -227,13 +244,13 @@ public class DisassembleCommand extends DexInputCommand { if (needsClassPath()) { try { - options.classPath = ClassPath.fromClassPath(classPathDirectories, - Iterables.concat(bootClassPath, classPath), dexFile, apiLevel, - shouldCheckPackagePrivateAccess(), experimentalOpcodes); + options.classPath = ClassPath.loadClassPath(classPathDirectories, + bootClassPath, classPath, dexFile, apiLevel, shouldCheckPackagePrivateAccess(), + experimentalOpcodes); } catch (Exception ex) { System.err.println("\n\nError occurred while loading class path files. Aborting."); ex.printStackTrace(System.err); - return null; + System.exit(-1); } } diff --git a/baksmali/src/main/java/org/jf/baksmali/ListFieldOffsetsCommand.java b/baksmali/src/main/java/org/jf/baksmali/ListFieldOffsetsCommand.java index 31a22179..8540ae19 100644 --- a/baksmali/src/main/java/org/jf/baksmali/ListFieldOffsetsCommand.java +++ b/baksmali/src/main/java/org/jf/baksmali/ListFieldOffsetsCommand.java @@ -34,7 +34,6 @@ package org.jf.baksmali; import com.beust.jcommander.JCommander; import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; -import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import org.jf.dexlib2.analysis.ClassPath; import org.jf.dexlib2.analysis.ClassProto; @@ -142,9 +141,8 @@ public class ListFieldOffsetsCommand extends DexInputCommand { options.apiLevel = apiLevel; try { - options.classPath = ClassPath.fromClassPath(classPathDirectories, - Iterables.concat(bootClassPath, classPath), dexFile, apiLevel, - checkPackagePrivateAccess, experimentalOpcodes); + options.classPath = ClassPath.loadClassPath(classPathDirectories, + bootClassPath, classPath, dexFile, apiLevel, checkPackagePrivateAccess, experimentalOpcodes); } catch (Exception ex) { System.err.println("Error occurred while loading class path files."); ex.printStackTrace(System.err); diff --git a/baksmali/src/main/java/org/jf/baksmali/ListVtablesCommand.java b/baksmali/src/main/java/org/jf/baksmali/ListVtablesCommand.java index ae74c811..80d41650 100644 --- a/baksmali/src/main/java/org/jf/baksmali/ListVtablesCommand.java +++ b/baksmali/src/main/java/org/jf/baksmali/ListVtablesCommand.java @@ -34,7 +34,6 @@ package org.jf.baksmali; import com.beust.jcommander.JCommander; import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; -import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import org.jf.dexlib2.analysis.ClassPath; import org.jf.dexlib2.analysis.ClassProto; @@ -164,9 +163,8 @@ public class ListVtablesCommand extends DexInputCommand { options.apiLevel = apiLevel; try { - options.classPath = ClassPath.fromClassPath(classPathDirectories, - Iterables.concat(bootClassPath, classPath), dexFile, apiLevel, checkPackagePrivateAccess, - experimentalOpcodes); + options.classPath = ClassPath.loadClassPath(classPathDirectories, bootClassPath, classPath, dexFile, + apiLevel, checkPackagePrivateAccess, experimentalOpcodes); } catch (Exception ex) { System.err.println("Error occurred while loading class path files."); ex.printStackTrace(System.err); diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassPath.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassPath.java index 9f9e396b..05cf4f8f 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassPath.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassPath.java @@ -36,28 +36,29 @@ import com.google.common.base.Suppliers; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Lists; +import com.google.common.collect.*; +import com.google.common.io.Files; +import com.google.common.primitives.Ints; import org.jf.dexlib2.DexFileFactory; -import org.jf.dexlib2.DexFileFactory.DexFileNotFound; import org.jf.dexlib2.DexFileFactory.MultipleDexFilesException; import org.jf.dexlib2.Opcodes; import org.jf.dexlib2.analysis.reflection.ReflectionClassDef; +import org.jf.dexlib2.dexbacked.DexBackedOdexFile; +import org.jf.dexlib2.dexbacked.OatFile; import org.jf.dexlib2.dexbacked.OatFile.OatDexFile; import org.jf.dexlib2.iface.ClassDef; import org.jf.dexlib2.iface.DexFile; import org.jf.dexlib2.immutable.ImmutableDexFile; import org.jf.util.ExceptionWithContext; +import org.jf.util.PathUtil; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.Serializable; -import java.util.Arrays; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import java.util.*; public class ClassPath { @Nonnull private final TypeProto unknownClass; @@ -164,117 +165,181 @@ public class ClassPath { return checkPackagePrivateAccess; } + + /** + * Creates a ClassPath given a set of user inputs + * + * This performs all the magic in finding the right defaults based on the values provided and what type of dex + * file we have. E.g. choosing the right default bootclasspath if needed, actually locating the files on the + * filesystem, etc. + * + * This is meant to be as forgiving as possible and to generally "do the right thing" based on the given inputs. + * + * @param classPathDirs A list of directories to search for class path entries in. Be sure to include "." to search + * the current working directory, if appropriate. + * @param bootClassPathEntries A list of boot class path entries to load. This can be just the bare filenames, + * relative paths, absolute paths based on the local directory structure, absolute paths + * based on the device directory structure, etc. It can contain paths to + * jar/dex/oat/odex files, or just bare filenames with no extension, etc. + * If non-null and blank, then no entries will be loaded other than dexFile + * If null, it will attempt to use the correct defaults based on the inputs. + * @param extraClassPathEntries Additional class path entries. The same sorts of naming mechanisms as for + * bootClassPathEntries are allowed + * @param checkPackagePrivateAccess Whether checkPackagePrivateAccess is needed, enabled for ONLY early API 17 by + * default + * @param dexFile The dex file that will be analyzed. It can be a dex, odex or oat file. + * @param api The api level of the device that these dex files come from. + * @param experimental Whether to allow experimental opcodes + * + * @return A ClassPath object + */ @Nonnull - public static ClassPath fromClassPath(Iterable classPathDirs, Iterable classPath, DexFile dexFile, - int api, boolean experimental) { - return fromClassPath(classPathDirs, classPath, dexFile, api, api == 17, experimental); - } + public static ClassPath loadClassPath(@Nonnull Iterable classPathDirs, + @Nullable Iterable bootClassPathEntries, + @Nonnull Iterable extraClassPathEntries, @Nonnull DexFile dexFile, + int api, boolean experimental, boolean checkPackagePrivateAccess) + throws IOException { + List classProviders = Lists.newArrayList(); + if (bootClassPathEntries == null) { + bootClassPathEntries = getDefaultDeviceBootClassPath(dexFile, api); + } + if (extraClassPathEntries == null) { + extraClassPathEntries = ImmutableList.of(); + } + for (String entry: Iterables.concat(bootClassPathEntries, extraClassPathEntries)) { + List files = Lists.newArrayList(); - @Nonnull - public static ClassPath fromClassPath(Iterable classPathDirs, Iterable classPath, DexFile dexFile, - int api, boolean checkPackagePrivateAccess, boolean experimental) { - List providers = Lists.newArrayList(); + for (String extension: new String[] { null, ".apk", ".jar", ".odex", ".oat", ".dex" }) { + String searchEntry = entry; + if (Files.getFileExtension(entry).equals(extension)) { + continue; + } + if (extension != null) { + searchEntry = Files.getNameWithoutExtension(entry) + extension; + } - int oatVersion = NOT_ART; - - for (String classPathEntry: classPath) { - List classPathDexFiles = - loadClassPathEntry(classPathDirs, classPathEntry, api, experimental); - if (oatVersion == NOT_ART) { - for (DexFile classPathDexFile: classPathDexFiles) { - if (classPathDexFile instanceof OatDexFile) { - oatVersion = ((OatDexFile)classPathDexFile).getOatVersion(); - break; - } + for (String dir: classPathDirs) { + files.addAll(findFiles(new File(dir), new File(searchEntry).getName(), 100)); + } + if (files.size() > 0) { + break; } } - for (DexFile classPathDexFile: classPathDexFiles) { - providers.add(new DexClassProvider(classPathDexFile)); + + if (files.size() == 0) { + throw new FileNotFoundException(String.format("Classpath entry %s could not be found", entry)); + } + + File bestMatch = Collections.max(files, new ClassPathEntryComparator(entry)); + try { + DexFile entryDexFile = DexFileFactory.loadDexFile(bestMatch, api, experimental); + classProviders.add(new DexClassProvider(entryDexFile)); + } catch (MultipleDexFilesException ex) { + for (DexFile entryDexFile: ex.oatFile.getDexFiles()) { + classProviders.add(new DexClassProvider(entryDexFile)); + } } } - providers.add(new DexClassProvider(dexFile)); - return new ClassPath(providers, checkPackagePrivateAccess, oatVersion); + + int oatVersion = -1; + if (dexFile instanceof OatDexFile) { + oatVersion = ((OatDexFile)dexFile).getOatVersion(); + } + classProviders.add(new DexClassProvider(dexFile)); + + return new ClassPath(classProviders, checkPackagePrivateAccess, oatVersion); } - @Nonnull - public static ClassPath fromClassPath(Iterable classPathDirs, Iterable classPath, DexFile dexFile, - int api, boolean checkPackagePrivateAccess, boolean experimental, - int oatVersion) { - List providers = Lists.newArrayList(); + private static class ClassPathEntryComparator implements Comparator { + @Nonnull private List reversePathComponents; - for (String classPathEntry: classPath) { - List classPathDexFiles = - loadClassPathEntry(classPathDirs, classPathEntry, api, experimental); - for (DexFile classPathDexFile: classPathDexFiles) { - providers.add(new DexClassProvider(classPathDexFile)); - } + public ClassPathEntryComparator(@Nonnull String entry) { + // TODO: will PathUtil.getPathComponents work for unix-style paths while on windows? + this.reversePathComponents = Lists.reverse(PathUtil.getPathComponents(new File(entry))); + } + + @Override public int compare(File file1, File file2) { + int comparison = Ints.compare(countMatchingComponents(file1), countMatchingComponents(file2)); + if (comparison != 0) { + // the path that matches the entry being searched for wins + return comparison; + } + + comparison = Ints.compare(PathUtil.getPathComponents(file1).size(), + PathUtil.getPathComponents(file2).size()); + if (comparison != 0) { + // the path "higher up" (with fewer directories) wins + return comparison * -1; + } + + // otherwise.. just return the first one alphabetically. + return file1.compareTo(file2); + } + + private int countMatchingComponents(File file) { + for (int i=0; i loadClassPathEntry(@Nonnull Iterable classPathDirs, - @Nonnull String bootClassPathEntry, int api, - boolean experimental) { - File rawEntry = new File(bootClassPathEntry); - // strip off the path - we only care about the filename - String entryName = rawEntry.getName(); + private static List findFiles(@Nonnull File dir, @Nonnull String name, int maxDepth) throws IOException { + List files = Lists.newArrayList(); + Set visitedPaths = Sets.newHashSet(); - // if it's a dalvik-cache entry, grab the name of the jar/apk - if (entryName.endsWith("@classes.dex")) { - Matcher m = dalvikCacheOdexPattern.matcher(entryName); - - if (!m.find()) { - throw new ExceptionWithContext(String.format("Cannot parse dependency value %s", bootClassPathEntry)); - } - - entryName = m.group(1); + if (!dir.exists()) { + throw new IllegalArgumentException(String.format("Directory %s does not exist", dir.getPath())); + } + if (!dir.isDirectory()) { + throw new IllegalArgumentException(String.format("%s is not a directory", dir.getPath())); } - int extIndex = entryName.lastIndexOf("."); + findFiles(files, visitedPaths, dir, name, maxDepth); + return files; + } - String baseEntryName; - if (extIndex == -1) { - baseEntryName = entryName; - } else { - baseEntryName = entryName.substring(0, extIndex); + private static void findFiles(@Nonnull List result, @Nonnull Set visitedPaths, @Nonnull File dir, + @Nonnull String name, int maxDepth) throws IOException { + if (maxDepth < 0 || !visitedPaths.add(dir.getCanonicalPath())) { + return; } - for (String classPathDir: classPathDirs) { - String[] extensions; - - if (entryName.endsWith(".oat")) { - extensions = new String[] { ".oat" }; + File[] children = dir.listFiles(); + if (children == null) { + return; + } + for (File child: children) { + if (child.isDirectory()) { + findFiles(result, visitedPaths, child, name, maxDepth-1); } else { - extensions = new String[] { "", ".odex", ".jar", ".apk", ".zip" }; - } - - for (String ext: extensions) { - File file = new File(classPathDir, baseEntryName + ext); - - if (file.exists() && file.isFile()) { - if (!file.canRead()) { - System.err.println(String.format( - "warning: cannot open %s for reading. Will continue looking.", file.getPath())); - } else { - try { - return ImmutableList.of(DexFileFactory.loadDexFile(file, api, experimental)); - } catch (DexFileNotFound ex) { - // ignore and continue - } catch (MultipleDexFilesException ex) { - return ex.oatFile.getDexFiles(); - } catch (Exception ex) { - throw ExceptionWithContext.withContext(ex, - "Error while reading boot class path entry \"%s\"", bootClassPathEntry); + if (name.equals(child.getName())) { + try { + DexFileFactory.loadDexFile(child, 15); + } catch (ExceptionWithContext ex) { + if (!(ex instanceof MultipleDexFilesException)) { + // Don't add it to the results if it can't be loaded + continue; } } + result.add(child); } } } - throw new ExceptionWithContext("Cannot locate boot class path file %s", bootClassPathEntry); } private final Supplier fieldInstructionMapperSupplier = Suppliers.memoize( @@ -288,4 +353,133 @@ public class ClassPath { public OdexedFieldInstructionMapper getFieldInstructionMapper() { return fieldInstructionMapperSupplier.get(); } + + /** + * Returns the default boot class path for the given api. This is boot class path that is used for "stock" + * (i.e nexus) images for the given api level, but may not be correct for devices with heavily modified firmware. + */ + @Nonnull + private static List getDefaultDeviceBootClassPath(DexFile dexFile, int apiLevel) { + if (dexFile instanceof OatFile.OatDexFile) { + if (((OatFile.OatDexFile) dexFile).getOatVersion() >= 74) { + return ((OatFile.OatDexFile) dexFile).getOatFile().getBootClassPath(); + } else { + return Lists.newArrayList("boot.oat"); + } + } + + if (dexFile instanceof DexBackedOdexFile) { + return ((DexBackedOdexFile)dexFile).getDependencies(); + } + + if (apiLevel <= 8) { + return Lists.newArrayList( + "/system/framework/core.jar", + "/system/framework/ext.jar", + "/system/framework/framework.jar", + "/system/framework/android.policy.jar", + "/system/framework/services.jar"); + } else if (apiLevel <= 11) { + return Lists.newArrayList( + "/system/framework/core.jar", + "/system/framework/bouncycastle.jar", + "/system/framework/ext.jar", + "/system/framework/framework.jar", + "/system/framework/android.policy.jar", + "/system/framework/services.jar", + "/system/framework/core-junit.jar"); + } else if (apiLevel <= 13) { + return Lists.newArrayList( + "/system/framework/core.jar", + "/system/framework/apache-xml.jar", + "/system/framework/bouncycastle.jar", + "/system/framework/ext.jar", + "/system/framework/framework.jar", + "/system/framework/android.policy.jar", + "/system/framework/services.jar", + "/system/framework/core-junit.jar"); + } else if (apiLevel <= 15) { + return Lists.newArrayList( + "/system/framework/core.jar", + "/system/framework/core-junit.jar", + "/system/framework/bouncycastle.jar", + "/system/framework/ext.jar", + "/system/framework/framework.jar", + "/system/framework/android.policy.jar", + "/system/framework/services.jar", + "/system/framework/apache-xml.jar", + "/system/framework/filterfw.jar"); + } else if (apiLevel <= 17) { + // this is correct as of api 17/4.2.2 + return Lists.newArrayList( + "/system/framework/core.jar", + "/system/framework/core-junit.jar", + "/system/framework/bouncycastle.jar", + "/system/framework/ext.jar", + "/system/framework/framework.jar", + "/system/framework/telephony-common.jar", + "/system/framework/mms-common.jar", + "/system/framework/android.policy.jar", + "/system/framework/services.jar", + "/system/framework/apache-xml.jar"); + } else if (apiLevel <= 18) { + return Lists.newArrayList( + "/system/framework/core.jar", + "/system/framework/core-junit.jar", + "/system/framework/bouncycastle.jar", + "/system/framework/ext.jar", + "/system/framework/framework.jar", + "/system/framework/telephony-common.jar", + "/system/framework/voip-common.jar", + "/system/framework/mms-common.jar", + "/system/framework/android.policy.jar", + "/system/framework/services.jar", + "/system/framework/apache-xml.jar"); + } else if (apiLevel <= 19) { + return Lists.newArrayList( + "/system/framework/core.jar", + "/system/framework/conscrypt.jar", + "/system/framework/core-junit.jar", + "/system/framework/bouncycastle.jar", + "/system/framework/ext.jar", + "/system/framework/framework.jar", + "/system/framework/framework2.jar", + "/system/framework/telephony-common.jar", + "/system/framework/voip-common.jar", + "/system/framework/mms-common.jar", + "/system/framework/android.policy.jar", + "/system/framework/services.jar", + "/system/framework/apache-xml.jar", + "/system/framework/webviewchromium.jar"); + } else if (apiLevel <= 22) { + return Lists.newArrayList( + "/system/framework/core-libart.jar", + "/system/framework/conscrypt.jar", + "/system/framework/okhttp.jar", + "/system/framework/core-junit.jar", + "/system/framework/bouncycastle.jar", + "/system/framework/ext.jar", + "/system/framework/framework.jar", + "/system/framework/telephony-common.jar", + "/system/framework/voip-common.jar", + "/system/framework/ims-common.jar", + "/system/framework/mms-common.jar", + "/system/framework/android.policy.jar", + "/system/framework/apache-xml.jar"); + } else /*if (apiLevel <= 23)*/ { + return Lists.newArrayList( + "/system/framework/core-libart.jar", + "/system/framework/conscrypt.jar", + "/system/framework/okhttp.jar", + "/system/framework/core-junit.jar", + "/system/framework/bouncycastle.jar", + "/system/framework/ext.jar", + "/system/framework/framework.jar", + "/system/framework/telephony-common.jar", + "/system/framework/voip-common.jar", + "/system/framework/ims-common.jar", + "/system/framework/apache-xml.jar", + "/system/framework/org.apache.http.legacy.boot.jar"); + } + } } diff --git a/dexlib2/src/test/java/org/jf/dexlib2/analysis/CustomMethodInlineTableTest.java b/dexlib2/src/test/java/org/jf/dexlib2/analysis/CustomMethodInlineTableTest.java index 90a63590..3cadd08d 100644 --- a/dexlib2/src/test/java/org/jf/dexlib2/analysis/CustomMethodInlineTableTest.java +++ b/dexlib2/src/test/java/org/jf/dexlib2/analysis/CustomMethodInlineTableTest.java @@ -51,11 +51,12 @@ import org.jf.dexlib2.immutable.instruction.ImmutableInstruction35mi; import org.junit.Assert; import org.junit.Test; +import java.io.IOException; import java.util.List; public class CustomMethodInlineTableTest { @Test - public void testCustomMethodInlineTable_Virtual() { + public void testCustomMethodInlineTable_Virtual() throws IOException { List instructions = Lists.newArrayList( new ImmutableInstruction35mi(Opcode.EXECUTE_INLINE, 1, 0, 0, 0, 0, 0, 0), new ImmutableInstruction10x(Opcode.RETURN_VOID)); @@ -69,8 +70,9 @@ public class CustomMethodInlineTableTest { DexFile dexFile = new ImmutableDexFile(Opcodes.forApi(19), ImmutableList.of(classDef)); - ClassPath classPath = ClassPath.fromClassPath(ImmutableList.of(), ImmutableList.of(), dexFile, - 15, false); + ClassPath classPath = ClassPath.loadClassPath(ImmutableList.of(), + ImmutableList.of(), ImmutableList.of(), dexFile, 15, false, false); + InlineMethodResolver inlineMethodResolver = new CustomInlineMethodResolver(classPath, "Lblah;->blah()V"); MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, inlineMethodResolver, false); @@ -82,7 +84,7 @@ public class CustomMethodInlineTableTest { } @Test - public void testCustomMethodInlineTable_Static() { + public void testCustomMethodInlineTable_Static() throws IOException { List instructions = Lists.newArrayList( new ImmutableInstruction35mi(Opcode.EXECUTE_INLINE, 1, 0, 0, 0, 0, 0, 0), new ImmutableInstruction10x(Opcode.RETURN_VOID)); @@ -96,8 +98,8 @@ public class CustomMethodInlineTableTest { DexFile dexFile = new ImmutableDexFile(Opcodes.forApi(19), ImmutableList.of(classDef)); - ClassPath classPath = ClassPath.fromClassPath(ImmutableList.of(), ImmutableList.of(), dexFile, - 15, false); + ClassPath classPath = ClassPath.loadClassPath(ImmutableList.of(), + ImmutableList.of(), ImmutableList.of(), dexFile, 15, false, false); InlineMethodResolver inlineMethodResolver = new CustomInlineMethodResolver(classPath, "Lblah;->blah()V"); MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, inlineMethodResolver, false); @@ -109,7 +111,7 @@ public class CustomMethodInlineTableTest { } @Test - public void testCustomMethodInlineTable_Direct() { + public void testCustomMethodInlineTable_Direct() throws IOException { List instructions = Lists.newArrayList( new ImmutableInstruction35mi(Opcode.EXECUTE_INLINE, 1, 0, 0, 0, 0, 0, 0), new ImmutableInstruction10x(Opcode.RETURN_VOID)); @@ -123,8 +125,8 @@ public class CustomMethodInlineTableTest { DexFile dexFile = new ImmutableDexFile(Opcodes.forApi(19), ImmutableList.of(classDef)); - ClassPath classPath = ClassPath.fromClassPath(ImmutableList.of(), ImmutableList.of(), dexFile, - 15, false); + ClassPath classPath = ClassPath.loadClassPath(ImmutableList.of(), + ImmutableList.of(), ImmutableList.of(), dexFile, 15, false, false); InlineMethodResolver inlineMethodResolver = new CustomInlineMethodResolver(classPath, "Lblah;->blah()V"); MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, inlineMethodResolver, false); diff --git a/util/src/main/java/org/jf/util/PathUtil.java b/util/src/main/java/org/jf/util/PathUtil.java index 91eb7584..93cd01ba 100644 --- a/util/src/main/java/org/jf/util/PathUtil.java +++ b/util/src/main/java/org/jf/util/PathUtil.java @@ -28,9 +28,12 @@ package org.jf.util; +import com.google.common.collect.Lists; + import java.io.File; import java.io.IOException; import java.util.ArrayList; +import java.util.List; public class PathUtil { private PathUtil() { @@ -55,8 +58,8 @@ public class PathUtil { } static String getRelativeFileInternal(File canonicalBaseFile, File canonicalFileToRelativize) { - ArrayList basePath = getPathComponents(canonicalBaseFile); - ArrayList pathToRelativize = getPathComponents(canonicalFileToRelativize); + List basePath = getPathComponents(canonicalBaseFile); + List pathToRelativize = getPathComponents(canonicalFileToRelativize); //if the roots aren't the same (i.e. different drives on a windows machine), we can't construct a relative //path from one to the other, so just return the canonical file @@ -105,21 +108,21 @@ public class PathUtil { return sb.toString(); } - private static ArrayList getPathComponents(File file) { + public static List getPathComponents(File file) { ArrayList path = new ArrayList(); while (file != null) { File parentFile = file.getParentFile(); if (parentFile == null) { - path.add(0, file.getPath()); + path.add(file.getPath()); } else { - path.add(0, file.getName()); + path.add(file.getName()); } file = parentFile; } - return path; + return Lists.reverse(path); } }