mirror of
https://github.com/revanced/smali.git
synced 2025-05-25 02:42:06 +02:00
Let baksmali infer the api level when possible
This commit is contained in:
parent
e6f4475c87
commit
077c5c1b91
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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>() {
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -223,9 +223,5 @@ public class DexEntryFinderTest {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull @Override public Opcodes getOpcodes() {
|
|
||||||
return Opcodes.getDefault();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user