diff --git a/baksmali/src/main/java/org/jf/baksmali/DexInputCommand.java b/baksmali/src/main/java/org/jf/baksmali/DexInputCommand.java index c7660cbe..13502634 100644 --- a/baksmali/src/main/java/org/jf/baksmali/DexInputCommand.java +++ b/baksmali/src/main/java/org/jf/baksmali/DexInputCommand.java @@ -55,7 +55,7 @@ public abstract class DexInputCommand extends Command { @Parameter(names = {"-a", "--api"}, description = "The numeric api level of the file being disassembled.") @ExtendedParameter(argumentNames = "api") - public int apiLevel = 15; + public int apiLevel = -1; @Parameter(description = "A dex/apk/oat/odex file. For apk or oat files that contain multiple dex " + "files, you can specify the specific entry to use as if the apk/oat file was a directory. " + @@ -125,6 +125,11 @@ public abstract class DexInputCommand extends Command { dexEntry = input.substring(file.getPath().length() + 1); } + Opcodes opcodes = null; + if (apiLevel != -1) { + opcodes = Opcodes.forApi(apiLevel); + } + if (!Strings.isNullOrEmpty(dexEntry)) { boolean exactMatch = false; if (dexEntry.length() > 2 && dexEntry.charAt(0) == '"' && dexEntry.charAt(dexEntry.length() - 1) == '"') { @@ -135,13 +140,13 @@ public abstract class DexInputCommand extends Command { inputEntry = dexEntry; try { - dexFile = DexFileFactory.loadDexEntry(file, dexEntry, exactMatch, Opcodes.forApi(apiLevel)); + dexFile = DexFileFactory.loadDexEntry(file, dexEntry, exactMatch, opcodes); } catch (IOException ex) { throw new RuntimeException(ex); } } else { try { - dexFile = DexFileFactory.loadDexFile(file, Opcodes.forApi(apiLevel)); + dexFile = DexFileFactory.loadDexFile(file, opcodes); } catch (IOException ex) { throw new RuntimeException(ex); } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/DexFileFactory.java b/dexlib2/src/main/java/org/jf/dexlib2/DexFileFactory.java index 1caaf9f8..eb33315f 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/DexFileFactory.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/DexFileFactory.java @@ -58,7 +58,7 @@ import java.util.List; public final class DexFileFactory { @Nonnull - public static DexBackedDexFile loadDexFile(@Nonnull String path, @Nonnull Opcodes opcodes) throws IOException { + public static DexBackedDexFile loadDexFile(@Nonnull String path, @Nullable Opcodes opcodes) throws IOException { return loadDexFile(new File(path), opcodes); } @@ -79,7 +79,7 @@ public final class DexFileFactory { * in a zip file is not a valid dex file */ @Nonnull - public static DexBackedDexFile loadDexFile(@Nonnull File file, @Nonnull Opcodes opcodes) throws IOException { + public static DexBackedDexFile loadDexFile(@Nonnull File file, @Nullable Opcodes opcodes) throws IOException { if (!file.exists()) { throw new DexFileNotFoundException("%s does not exist", file.getName()); } @@ -176,7 +176,7 @@ public final class DexFileFactory { * @throws MultipleMatchingDexEntriesException If multiple entries match the given dexEntry */ public static DexBackedDexFile loadDexEntry(@Nonnull File file, @Nonnull String dexEntry, - boolean exactMatch, @Nonnull Opcodes opcodes) throws IOException { + boolean exactMatch, @Nullable Opcodes opcodes) throws IOException { if (!file.exists()) { throw new DexFileNotFoundException("Container file %s does not exist", file.getName()); } @@ -456,10 +456,6 @@ public final class DexFileFactory { } return null; } - - @Nonnull @Override public Opcodes getOpcodes() { - return dexFile.getOpcodes(); - } } public static class FilenameVdexProvider implements VdexProvider { diff --git a/dexlib2/src/main/java/org/jf/dexlib2/Opcodes.java b/dexlib2/src/main/java/org/jf/dexlib2/Opcodes.java index d6d4fdbf..53bd899c 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/Opcodes.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/Opcodes.java @@ -64,6 +64,11 @@ public class Opcodes { return new Opcodes(NO_VERSION, artVersion); } + @Nonnull + public static Opcodes forDexVersion(int dexVersion) { + return new Opcodes(VersionMap.mapDexVersionToApi(dexVersion) , NO_VERSION); + } + /** * @return a default Opcodes instance for when the exact Opcodes to use doesn't matter or isn't known */ diff --git a/dexlib2/src/main/java/org/jf/dexlib2/VersionMap.java b/dexlib2/src/main/java/org/jf/dexlib2/VersionMap.java index 550f7547..a101ccd9 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/VersionMap.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/VersionMap.java @@ -34,6 +34,19 @@ package org.jf.dexlib2; public class VersionMap { public static final int NO_VERSION = -1; + public static int mapDexVersionToApi(int dexVersion) { + if (dexVersion == 35) { + return 23; + } + if (dexVersion == 37) { + return 25; + } + if (dexVersion == 38) { + return 27; + } + throw new RuntimeException("Unsupported dex version " + dexVersion); + } + public static int mapArtVersionToApi(int artVersion) { if (artVersion >= 124) { return 26; diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedDexFile.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedDexFile.java index 82a83e7b..664428b9 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedDexFile.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedDexFile.java @@ -71,13 +71,20 @@ public class DexBackedDexFile extends BaseDexBuffer implements DexFile { private final int classStartOffset; private final int mapOffset; - protected DexBackedDexFile(@Nonnull Opcodes opcodes, @Nonnull byte[] buf, int offset, boolean verifyMagic) { + protected DexBackedDexFile(@Nullable Opcodes opcodes, @Nonnull byte[] buf, int offset, boolean verifyMagic) { super(buf, offset); - this.opcodes = opcodes; - + int dexVersion; if (verifyMagic) { - DexUtil.verifyDexHeader(buf, offset); + dexVersion = DexUtil.verifyDexHeader(buf, offset); + } else { + dexVersion = HeaderItem.getVersion(buf, offset); + } + + if (opcodes == null) { + this.opcodes = Opcodes.forDexVersion(dexVersion); + } else { + this.opcodes = opcodes; } stringCount = readSmallUint(HeaderItem.STRING_COUNT_OFFSET); @@ -95,20 +102,20 @@ public class DexBackedDexFile extends BaseDexBuffer implements DexFile { mapOffset = readSmallUint(HeaderItem.MAP_OFFSET); } - public DexBackedDexFile(@Nonnull Opcodes opcodes, @Nonnull BaseDexBuffer buf) { + public DexBackedDexFile(@Nullable Opcodes opcodes, @Nonnull BaseDexBuffer buf) { this(opcodes, buf.buf, buf.baseOffset); } - public DexBackedDexFile(@Nonnull Opcodes opcodes, @Nonnull byte[] buf, int offset) { + public DexBackedDexFile(@Nullable Opcodes opcodes, @Nonnull byte[] buf, int offset) { this(opcodes, buf, offset, false); } - public DexBackedDexFile(@Nonnull Opcodes opcodes, @Nonnull byte[] buf) { + public DexBackedDexFile(@Nullable Opcodes opcodes, @Nonnull byte[] buf) { this(opcodes, buf, 0, true); } @Nonnull - public static DexBackedDexFile fromInputStream(@Nonnull Opcodes opcodes, @Nonnull InputStream is) + public static DexBackedDexFile fromInputStream(@Nullable Opcodes opcodes, @Nonnull InputStream is) throws IOException { DexUtil.verifyDexHeader(is); diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/OatFile.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/OatFile.java index aaf942ea..eeb7de70 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/OatFile.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/OatFile.java @@ -177,10 +177,6 @@ public class OatFile extends BaseDexBuffer implements MultiDexContainer getDexFiles() { return new AbstractForwardSequentialList() { diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/ZipDexContainer.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/ZipDexContainer.java index 50052c20..5bd30dd8 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/ZipDexContainer.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/ZipDexContainer.java @@ -58,23 +58,18 @@ import java.util.zip.ZipFile; public class ZipDexContainer implements MultiDexContainer { private final File zipFilePath; - private final Opcodes opcodes; + @Nullable private final Opcodes opcodes; /** * Constructs a new ZipDexContainer for the given zip file * * @param zipFilePath The path to the zip file - * @param opcodes The Opcodes instance to use when loading dex files from this container */ - public ZipDexContainer(@Nonnull File zipFilePath, @Nonnull Opcodes opcodes) { + public ZipDexContainer(@Nonnull File zipFilePath, @Nullable Opcodes opcodes) { this.zipFilePath = zipFilePath; this.opcodes = opcodes; } - @Nonnull @Override public Opcodes getOpcodes() { - return opcodes; - } - /** * Gets a list of the names of dex files in this zip file. * @@ -147,7 +142,7 @@ public class ZipDexContainer implements MultiDexContainer { private final String entryName; - protected ZipDexFile(@Nonnull Opcodes opcodes, @Nonnull byte[] buf, @Nonnull String entryName) { + protected ZipDexFile(@Nullable Opcodes opcodes, @Nonnull byte[] buf, @Nonnull String entryName) { super(opcodes, buf, 0); this.entryName = entryName; } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/iface/MultiDexContainer.java b/dexlib2/src/main/java/org/jf/dexlib2/iface/MultiDexContainer.java index 6c4e769a..251ecdef 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/iface/MultiDexContainer.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/iface/MultiDexContainer.java @@ -31,8 +31,6 @@ package org.jf.dexlib2.iface; -import org.jf.dexlib2.Opcodes; - import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.io.IOException; @@ -55,11 +53,6 @@ public interface MultiDexContainer { */ @Nullable T getEntry(@Nonnull String entryName) throws IOException; - /** - * @return the Opcodes instance associated with this MultiDexContainer - */ - @Nonnull Opcodes getOpcodes(); - /** * This class represents a dex file that is contained in a MultiDexContainer */ diff --git a/dexlib2/src/main/java/org/jf/dexlib2/util/DexUtil.java b/dexlib2/src/main/java/org/jf/dexlib2/util/DexUtil.java index 389edfd7..aaa455ca 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/util/DexUtil.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/util/DexUtil.java @@ -50,11 +50,12 @@ public class DexUtil { * The inputStream must support mark(), and will be reset to initial position upon exiting the method * * @param inputStream An input stream that is positioned at a dex header + * @return The dex version * @throws NotADexFile If the file is not a dex file * @throws InvalidFile If the header appears to be a dex file, but is not valid for some reason * @throws UnsupportedFile If the dex header is valid, but uses unsupported functionality */ - public static void verifyDexHeader(@Nonnull InputStream inputStream) throws IOException { + public static int verifyDexHeader(@Nonnull InputStream inputStream) throws IOException { if (!inputStream.markSupported()) { throw new IllegalArgumentException("InputStream must support mark"); } @@ -68,7 +69,7 @@ public class DexUtil { inputStream.reset(); } - verifyDexHeader(partialHeader, 0); + return verifyDexHeader(partialHeader, 0); } /** @@ -76,11 +77,12 @@ public class DexUtil { * * @param buf A byte array containing at least the first 44 bytes of a dex file * @param offset The offset within the array to the dex header + * @return The dex version * @throws NotADexFile If the file is not a dex file * @throws InvalidFile If the header appears to be a dex file, but is not valid for some reason * @throws UnsupportedFile If the dex header is valid, but uses unsupported functionality */ - public static void verifyDexHeader(@Nonnull byte[] buf, int offset) { + public static int verifyDexHeader(@Nonnull byte[] buf, int offset) { int dexVersion = HeaderItem.getVersion(buf, offset); if (dexVersion == -1) { StringBuilder sb = new StringBuilder("Not a valid dex magic value:"); @@ -102,6 +104,8 @@ public class DexUtil { if (endian != HeaderItem.LITTLE_ENDIAN_TAG) { throw new InvalidFile(String.format("Invalid endian tag: 0x%x", endian)); } + + return dexVersion; } /** diff --git a/dexlib2/src/test/java/org/jf/dexlib2/DexEntryFinderTest.java b/dexlib2/src/test/java/org/jf/dexlib2/DexEntryFinderTest.java index 610d3c76..de5b05fa 100644 --- a/dexlib2/src/test/java/org/jf/dexlib2/DexEntryFinderTest.java +++ b/dexlib2/src/test/java/org/jf/dexlib2/DexEntryFinderTest.java @@ -223,9 +223,5 @@ public class DexEntryFinderTest { } return null; } - - @Nonnull @Override public Opcodes getOpcodes() { - return Opcodes.getDefault(); - } } }