Let baksmali infer the api level when possible

This commit is contained in:
Ben Gruver 2018-05-22 11:29:45 -07:00
parent e6f4475c87
commit 077c5c1b91
10 changed files with 54 additions and 44 deletions

View File

@ -55,7 +55,7 @@ public abstract class DexInputCommand extends Command {
@Parameter(names = {"-a", "--api"}, @Parameter(names = {"-a", "--api"},
description = "The numeric api level of the file being disassembled.") description = "The numeric api level of the file being disassembled.")
@ExtendedParameter(argumentNames = "api") @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 " + @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. " + "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); dexEntry = input.substring(file.getPath().length() + 1);
} }
Opcodes opcodes = null;
if (apiLevel != -1) {
opcodes = Opcodes.forApi(apiLevel);
}
if (!Strings.isNullOrEmpty(dexEntry)) { if (!Strings.isNullOrEmpty(dexEntry)) {
boolean exactMatch = false; boolean exactMatch = false;
if (dexEntry.length() > 2 && dexEntry.charAt(0) == '"' && dexEntry.charAt(dexEntry.length() - 1) == '"') { if (dexEntry.length() > 2 && dexEntry.charAt(0) == '"' && dexEntry.charAt(dexEntry.length() - 1) == '"') {
@ -135,13 +140,13 @@ public abstract class DexInputCommand extends Command {
inputEntry = dexEntry; inputEntry = dexEntry;
try { try {
dexFile = DexFileFactory.loadDexEntry(file, dexEntry, exactMatch, Opcodes.forApi(apiLevel)); dexFile = DexFileFactory.loadDexEntry(file, dexEntry, exactMatch, opcodes);
} catch (IOException ex) { } catch (IOException ex) {
throw new RuntimeException(ex); throw new RuntimeException(ex);
} }
} else { } else {
try { try {
dexFile = DexFileFactory.loadDexFile(file, Opcodes.forApi(apiLevel)); dexFile = DexFileFactory.loadDexFile(file, opcodes);
} catch (IOException ex) { } catch (IOException ex) {
throw new RuntimeException(ex); throw new RuntimeException(ex);
} }

View File

@ -58,7 +58,7 @@ import java.util.List;
public final class DexFileFactory { public final class DexFileFactory {
@Nonnull @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); return loadDexFile(new File(path), opcodes);
} }
@ -79,7 +79,7 @@ public final class DexFileFactory {
* in a zip file is not a valid dex file * in a zip file is not a valid dex file
*/ */
@Nonnull @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()) { if (!file.exists()) {
throw new DexFileNotFoundException("%s does not exist", file.getName()); 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 * @throws MultipleMatchingDexEntriesException If multiple entries match the given dexEntry
*/ */
public static DexBackedDexFile loadDexEntry(@Nonnull File file, @Nonnull String 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()) { if (!file.exists()) {
throw new DexFileNotFoundException("Container file %s does not exist", file.getName()); throw new DexFileNotFoundException("Container file %s does not exist", file.getName());
} }
@ -456,10 +456,6 @@ public final class DexFileFactory {
} }
return null; return null;
} }
@Nonnull @Override public Opcodes getOpcodes() {
return dexFile.getOpcodes();
}
} }
public static class FilenameVdexProvider implements VdexProvider { public static class FilenameVdexProvider implements VdexProvider {

View File

@ -64,6 +64,11 @@ public class Opcodes {
return new Opcodes(NO_VERSION, artVersion); 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 * @return a default Opcodes instance for when the exact Opcodes to use doesn't matter or isn't known
*/ */

View File

@ -34,6 +34,19 @@ package org.jf.dexlib2;
public class VersionMap { public class VersionMap {
public static final int NO_VERSION = -1; 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) { public static int mapArtVersionToApi(int artVersion) {
if (artVersion >= 124) { if (artVersion >= 124) {
return 26; return 26;

View File

@ -71,13 +71,20 @@ public class DexBackedDexFile extends BaseDexBuffer implements DexFile {
private final int classStartOffset; private final int classStartOffset;
private final int mapOffset; 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); super(buf, offset);
this.opcodes = opcodes; int dexVersion;
if (verifyMagic) { 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); stringCount = readSmallUint(HeaderItem.STRING_COUNT_OFFSET);
@ -95,20 +102,20 @@ public class DexBackedDexFile extends BaseDexBuffer implements DexFile {
mapOffset = readSmallUint(HeaderItem.MAP_OFFSET); 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); 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); 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); this(opcodes, buf, 0, true);
} }
@Nonnull @Nonnull
public static DexBackedDexFile fromInputStream(@Nonnull Opcodes opcodes, @Nonnull InputStream is) public static DexBackedDexFile fromInputStream(@Nullable Opcodes opcodes, @Nonnull InputStream is)
throws IOException { throws IOException {
DexUtil.verifyDexHeader(is); DexUtil.verifyDexHeader(is);

View File

@ -177,10 +177,6 @@ public class OatFile extends BaseDexBuffer implements MultiDexContainer<OatDexFi
return Arrays.asList(bcp.split(":")); return Arrays.asList(bcp.split(":"));
} }
@Nonnull @Override public Opcodes getOpcodes() {
return opcodes;
}
@Nonnull @Nonnull
public List<OatDexFile> getDexFiles() { public List<OatDexFile> getDexFiles() {
return new AbstractForwardSequentialList<OatDexFile>() { return new AbstractForwardSequentialList<OatDexFile>() {

View File

@ -58,23 +58,18 @@ import java.util.zip.ZipFile;
public class ZipDexContainer implements MultiDexContainer<ZipDexFile> { public class ZipDexContainer implements MultiDexContainer<ZipDexFile> {
private final File zipFilePath; private final File zipFilePath;
private final Opcodes opcodes; @Nullable private final Opcodes opcodes;
/** /**
* Constructs a new ZipDexContainer for the given zip file * Constructs a new ZipDexContainer for the given zip file
* *
* @param zipFilePath The path to the 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.zipFilePath = zipFilePath;
this.opcodes = opcodes; this.opcodes = opcodes;
} }
@Nonnull @Override public Opcodes getOpcodes() {
return opcodes;
}
/** /**
* Gets a list of the names of dex files in this zip file. * Gets a list of the names of dex files in this zip file.
* *
@ -147,7 +142,7 @@ public class ZipDexContainer implements MultiDexContainer<ZipDexFile> {
private final String entryName; 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); super(opcodes, buf, 0);
this.entryName = entryName; this.entryName = entryName;
} }

View File

@ -31,8 +31,6 @@
package org.jf.dexlib2.iface; package org.jf.dexlib2.iface;
import org.jf.dexlib2.Opcodes;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.io.IOException; import java.io.IOException;
@ -55,11 +53,6 @@ public interface MultiDexContainer<T extends DexFile> {
*/ */
@Nullable T getEntry(@Nonnull String entryName) throws IOException; @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 * This class represents a dex file that is contained in a MultiDexContainer
*/ */

View File

@ -50,11 +50,12 @@ public class DexUtil {
* The inputStream must support mark(), and will be reset to initial position upon exiting the method * 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 * @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 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 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 * @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()) { if (!inputStream.markSupported()) {
throw new IllegalArgumentException("InputStream must support mark"); throw new IllegalArgumentException("InputStream must support mark");
} }
@ -68,7 +69,7 @@ public class DexUtil {
inputStream.reset(); 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 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 * @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 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 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 * @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); int dexVersion = HeaderItem.getVersion(buf, offset);
if (dexVersion == -1) { if (dexVersion == -1) {
StringBuilder sb = new StringBuilder("Not a valid dex magic value:"); StringBuilder sb = new StringBuilder("Not a valid dex magic value:");
@ -102,6 +104,8 @@ public class DexUtil {
if (endian != HeaderItem.LITTLE_ENDIAN_TAG) { if (endian != HeaderItem.LITTLE_ENDIAN_TAG) {
throw new InvalidFile(String.format("Invalid endian tag: 0x%x", endian)); throw new InvalidFile(String.format("Invalid endian tag: 0x%x", endian));
} }
return dexVersion;
} }
/** /**

View File

@ -223,9 +223,5 @@ public class DexEntryFinderTest {
} }
return null; return null;
} }
@Nonnull @Override public Opcodes getOpcodes() {
return Opcodes.getDefault();
}
} }
} }