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"},
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);
}

View File

@ -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 {

View File

@ -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
*/

View File

@ -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;

View File

@ -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);

View File

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

View File

@ -58,23 +58,18 @@ import java.util.zip.ZipFile;
public class ZipDexContainer implements MultiDexContainer<ZipDexFile> {
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<ZipDexFile> {
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;
}

View File

@ -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<T extends DexFile> {
*/
@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
*/

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
*
* @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;
}
/**

View File

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