mirror of
https://github.com/revanced/smali.git
synced 2025-05-05 00:54:25 +02:00
Add the ability to read the dependency information from an odex file, and use those dependencies as the BOOTCLASSPATH by default for odex files
git-svn-id: https://smali.googlecode.com/svn/trunk@679 55b6fa8a-2a1e-11de-a435-ffa8d773f76a
This commit is contained in:
parent
dfb1b8c6c0
commit
78bde01ad4
@ -38,6 +38,8 @@ import org.jf.dexlib.ClassDefItem;
|
||||
import org.jf.dexlib.StringIdItem;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class baksmali {
|
||||
public static boolean noParameterRegisters = false;
|
||||
@ -51,10 +53,10 @@ public class baksmali {
|
||||
public static String bootClassPath;
|
||||
|
||||
public static void disassembleDexFile(String dexFilePath, DexFile dexFile, boolean deodex, String outputDirectory,
|
||||
String[] classPathDirs, String bootClassPath, boolean noParameterRegisters,
|
||||
boolean useLocalsDirective, boolean useSequentialLabels,
|
||||
boolean outputDebugInfo, boolean addCodeOffsets, int registerInfo,
|
||||
boolean verify)
|
||||
String[] classPathDirs, String bootClassPath, String extraBootClassPath,
|
||||
boolean noParameterRegisters, boolean useLocalsDirective,
|
||||
boolean useSequentialLabels, boolean outputDebugInfo, boolean addCodeOffsets,
|
||||
int registerInfo, boolean verify)
|
||||
{
|
||||
baksmali.noParameterRegisters = noParameterRegisters;
|
||||
baksmali.useLocalsDirective = useLocalsDirective;
|
||||
@ -68,8 +70,28 @@ public class baksmali {
|
||||
|
||||
if (registerInfo != 0 || deodex || verify) {
|
||||
try {
|
||||
ClassPath.InitializeClassPath(classPathDirs, bootClassPath==null?null:bootClassPath.split(":"),
|
||||
dexFilePath, dexFile);
|
||||
String[] extraBootClassPathArray = null;
|
||||
if (extraBootClassPath != null && extraBootClassPath.length() > 0) {
|
||||
assert extraBootClassPath.charAt(0) == ':';
|
||||
extraBootClassPathArray = extraBootClassPath.substring(1).split(":");
|
||||
}
|
||||
|
||||
if (dexFile.isOdex() && bootClassPath == null) {
|
||||
//ext.jar is a special case - it is typically the 2nd jar in the boot class path, but it also
|
||||
//depends on classes in framework.jar. If the user didn't specify a -c option, we should add
|
||||
//framework.jar to the boot class path by default, so that it "just works"
|
||||
if (extraBootClassPathArray == null && isExtJar(dexFilePath)) {
|
||||
extraBootClassPath = ":framework.jar";
|
||||
}
|
||||
ClassPath.InitializeClassPathFromOdex(classPathDirs, extraBootClassPathArray, dexFilePath, dexFile);
|
||||
} else {
|
||||
String[] bootClassPathArray = null;
|
||||
if (bootClassPath != null) {
|
||||
bootClassPathArray = bootClassPath.split(":");
|
||||
}
|
||||
ClassPath.InitializeClassPath(classPathDirs, bootClassPathArray, extraBootClassPathArray,
|
||||
dexFilePath, dexFile);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
System.err.println("\n\nError occured while loading boot class path files. Aborting.");
|
||||
ex.printStackTrace(System.err);
|
||||
@ -182,4 +204,10 @@ public class baksmali {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final Pattern extJarPattern = Pattern.compile("(?:^|\\\\|/)ext.(?:jar|odex)$");
|
||||
private static boolean isExtJar(String dexFilePath) {
|
||||
Matcher m = extJarPattern.matcher(dexFilePath);
|
||||
return m.find();
|
||||
}
|
||||
}
|
||||
|
@ -120,7 +120,7 @@ public class deodexCheck {
|
||||
bootClassPathDirsArray[i] = bootClassPathDirs.get(i);
|
||||
}
|
||||
|
||||
ClassPath.InitializeClassPath(bootClassPathDirsArray, bootClassPath==null?null:bootClassPath.split(":"),
|
||||
ClassPath.InitializeClassPath(bootClassPathDirsArray, bootClassPath==null?null:bootClassPath.split(":"), null,
|
||||
null, null);
|
||||
|
||||
ClassPath.validateAgainstDeodexerant(deodexerantHost, deodexerantPort, classStartIndex);
|
||||
|
@ -30,6 +30,8 @@ package org.jf.baksmali;
|
||||
|
||||
import org.apache.commons.cli.*;
|
||||
import org.jf.dexlib.DexFile;
|
||||
import org.jf.dexlib.OdexDependencies;
|
||||
import org.jf.dexlib.Util.ExceptionWithContext;
|
||||
import org.jf.util.*;
|
||||
|
||||
import java.io.File;
|
||||
@ -111,7 +113,8 @@ public class main {
|
||||
String dumpFileName = null;
|
||||
String outputDexFileName = null;
|
||||
String inputDexFileName = null;
|
||||
String bootClassPath = "core.jar:ext.jar:framework.jar:android.policy.jar:services.jar";
|
||||
String bootClassPath = null;
|
||||
StringBuffer extraBootClassPathEntries = new StringBuffer();
|
||||
List<String> bootClassPathDirs = new ArrayList<String>();
|
||||
bootClassPathDirs.add(".");
|
||||
|
||||
@ -193,7 +196,7 @@ public class main {
|
||||
case 'c':
|
||||
String bcp = commandLine.getOptionValue("c");
|
||||
if (bcp != null && bcp.charAt(0) == ':') {
|
||||
bootClassPath = bootClassPath + bcp;
|
||||
extraBootClassPathEntries.append(bcp);
|
||||
} else {
|
||||
bootClassPath = bcp;
|
||||
}
|
||||
@ -257,6 +260,10 @@ public class main {
|
||||
}
|
||||
} else {
|
||||
deodex = false;
|
||||
|
||||
if (bootClassPath == null) {
|
||||
bootClassPath = "core.jar:ext.jar:framework.jar:android.policy.jar:services.jar";
|
||||
}
|
||||
}
|
||||
|
||||
if (disassemble) {
|
||||
@ -266,8 +273,9 @@ public class main {
|
||||
}
|
||||
|
||||
baksmali.disassembleDexFile(dexFileFile.getPath(), dexFile, deodex, outputDirectory,
|
||||
bootClassPathDirsArray, bootClassPath, noParameterRegisters, useLocalsDirective,
|
||||
useSequentialLabels, outputDebugInfo, addCodeOffsets, registerInfo, verify);
|
||||
bootClassPathDirsArray, bootClassPath, extraBootClassPathEntries.toString(),
|
||||
noParameterRegisters, useLocalsDirective, useSequentialLabels, outputDebugInfo, addCodeOffsets,
|
||||
registerInfo, verify);
|
||||
}
|
||||
|
||||
if ((doDump || write) && !dexFile.isOdex()) {
|
||||
|
@ -38,6 +38,8 @@ import org.jf.dexlib.Util.SparseArray;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class ClassPath {
|
||||
private static ClassPath theClassPath = null;
|
||||
@ -48,21 +50,81 @@ public class ClassPath {
|
||||
//This is only used while initialing the class path. It is set to null after initialization has finished.
|
||||
private LinkedHashMap<String, TempClassInfo> tempClasses;
|
||||
|
||||
public static void InitializeClassPath(String[] classPathDirs, String[] bootClassPath, String dexFilePath,
|
||||
DexFile dexFile) {
|
||||
|
||||
private static final Pattern dalvikCacheOdexPattern = Pattern.compile("@([^@]+)@classes.dex$");
|
||||
|
||||
/**
|
||||
* Initialize the class path using the dependencies from an odex file
|
||||
* @param classPathDirs The directories to search for boot class path files
|
||||
* @param extraBootClassPathEntries any extra entries that should be added after the entries that are read
|
||||
* from the odex file
|
||||
* @param dexFilePath The path of the dex file (used for error reporting purposes only)
|
||||
* @param dexFile The DexFile to load - it must represents an odex file
|
||||
*/
|
||||
public static void InitializeClassPathFromOdex(String[] classPathDirs, String[] extraBootClassPathEntries,
|
||||
String dexFilePath, DexFile dexFile) {
|
||||
if (!dexFile.isOdex()) {
|
||||
throw new ExceptionWithContext("Cannot use InitialiazeClassPathFromOdex with a non-odex DexFile");
|
||||
}
|
||||
|
||||
if (theClassPath != null) {
|
||||
throw new ExceptionWithContext("Cannot initialize ClassPath multiple times");
|
||||
}
|
||||
|
||||
OdexDependencies odexDependencies = dexFile.getOdexDependencies();
|
||||
|
||||
String[] bootClassPath = new String[odexDependencies.getDependencyCount()];
|
||||
for (int i=0; i<bootClassPath.length; i++) {
|
||||
String dependency = odexDependencies.getDependency(i);
|
||||
|
||||
if (dependency.endsWith(".odex")) {
|
||||
int slashIndex = dependency.lastIndexOf("/");
|
||||
|
||||
if (slashIndex != -1) {
|
||||
dependency = dependency.substring(slashIndex+1);
|
||||
}
|
||||
} else if (dependency.endsWith("@classes.dex")) {
|
||||
Matcher m = dalvikCacheOdexPattern.matcher(dependency);
|
||||
|
||||
if (!m.find()) {
|
||||
throw new ExceptionWithContext(String.format("Cannot parse dependency value %s", dependency));
|
||||
}
|
||||
|
||||
dependency = m.group(1);
|
||||
} else {
|
||||
throw new ExceptionWithContext(String.format("Cannot parse dependency value %s", dependency));
|
||||
}
|
||||
|
||||
bootClassPath[i] = dependency;
|
||||
}
|
||||
|
||||
theClassPath = new ClassPath();
|
||||
theClassPath.initClassPath(classPathDirs, bootClassPath, extraBootClassPathEntries, dexFilePath, dexFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the class path using the given boot class path entries
|
||||
* @param classPathDirs The directories to search for boot class path files
|
||||
* @param bootClassPath A list of the boot class path entries to search for and load
|
||||
* @param dexFilePath The path of the dex file (used for error reporting purposes only)
|
||||
* @param dexFile the DexFile to load
|
||||
*/
|
||||
public static void InitializeClassPath(String[] classPathDirs, String[] bootClassPath,
|
||||
String[] extraBootClassPathEntries, String dexFilePath, DexFile dexFile) {
|
||||
if (theClassPath != null) {
|
||||
throw new ExceptionWithContext("Cannot initialize ClassPath multiple times");
|
||||
}
|
||||
|
||||
theClassPath = new ClassPath();
|
||||
theClassPath.initClassPath(classPathDirs, bootClassPath, dexFilePath, dexFile);
|
||||
theClassPath.initClassPath(classPathDirs, bootClassPath, extraBootClassPathEntries, dexFilePath, dexFile);
|
||||
}
|
||||
|
||||
private ClassPath() {
|
||||
classDefs = new HashMap<String, ClassDef>();
|
||||
}
|
||||
|
||||
private void initClassPath(String[] classPathDirs, String[] bootClassPath, String dexFilePath, DexFile dexFile) {
|
||||
private void initClassPath(String[] classPathDirs, String[] bootClassPath, String[] extraBootClassPathEntries,
|
||||
String dexFilePath, DexFile dexFile) {
|
||||
tempClasses = new LinkedHashMap<String, TempClassInfo>();
|
||||
|
||||
if (bootClassPath != null) {
|
||||
@ -71,6 +133,12 @@ public class ClassPath {
|
||||
}
|
||||
}
|
||||
|
||||
if (extraBootClassPathEntries != null) {
|
||||
for (String bootClassPathEntry: extraBootClassPathEntries) {
|
||||
loadBootClassPath(classPathDirs, bootClassPathEntry);
|
||||
}
|
||||
}
|
||||
|
||||
if (dexFile != null) {
|
||||
loadDexFile(dexFilePath, dexFile);
|
||||
}
|
||||
|
@ -29,7 +29,6 @@
|
||||
package org.jf.dexlib;
|
||||
|
||||
import org.jf.dexlib.Util.*;
|
||||
import org.jf.dexlib.*;
|
||||
import org.jf.dexlib.Item;
|
||||
import org.jf.dexlib.StringDataItem;
|
||||
|
||||
@ -37,13 +36,11 @@ import java.io.*;
|
||||
import java.security.DigestException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.Collections;
|
||||
import java.util.zip.Adler32;
|
||||
import java.util.zip.ZipFile;
|
||||
import java.util.zip.ZipException;
|
||||
import java.util.zip.ZipEntry;
|
||||
|
||||
/**
|
||||
@ -180,6 +177,7 @@ public class DexFile
|
||||
*/
|
||||
private boolean isOdex = false;
|
||||
|
||||
private OdexDependencies odexDependencies;
|
||||
|
||||
private int dataOffset;
|
||||
private int dataSize;
|
||||
@ -338,7 +336,7 @@ public class DexFile
|
||||
byte[] dexMagic, odexMagic;
|
||||
|
||||
dexMagic = org.jf.dexlib.HeaderItem.MAGIC;
|
||||
odexMagic = OdexHeaderItem.MAGIC;
|
||||
odexMagic = OdexHeader.MAGIC;
|
||||
|
||||
boolean isDex = true;
|
||||
this.isOdex = true;
|
||||
@ -354,9 +352,25 @@ public class DexFile
|
||||
if (isOdex) {
|
||||
byte[] odexHeaderBytes = FileUtils.readStream(inputStream, 40);
|
||||
Input odexHeaderIn = new ByteArrayInput(odexHeaderBytes);
|
||||
OdexHeaderItem odexHeader = new OdexHeaderItem(odexHeaderIn);
|
||||
OdexHeader odexHeader = new OdexHeader(odexHeaderIn);
|
||||
|
||||
int dependencySkip = odexHeader.depsOffset - odexHeader.dexOffset - odexHeader.dexLength;
|
||||
if (dependencySkip < 0) {
|
||||
throw new ExceptionWithContext("Unexpected placement of the odex dependency data");
|
||||
}
|
||||
|
||||
if (odexHeader.dexOffset > 40) {
|
||||
FileUtils.readStream(inputStream, odexHeader.dexOffset - 40);
|
||||
}
|
||||
|
||||
in = new ByteArrayInput(FileUtils.readStream(inputStream, odexHeader.dexLength));
|
||||
|
||||
if (dependencySkip > 0) {
|
||||
FileUtils.readStream(inputStream, dependencySkip);
|
||||
}
|
||||
|
||||
odexDependencies = new OdexDependencies(
|
||||
new ByteArrayInput(FileUtils.readStream(inputStream, odexHeader.depsLength)));
|
||||
} else if (isDex) {
|
||||
in = new ByteArrayInput(FileUtils.readStream(inputStream, (int)fileLength));
|
||||
} else {
|
||||
@ -515,6 +529,14 @@ public class DexFile
|
||||
return this.isOdex;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an OdexDependencies object that contains the dependencies for this odex, or null if this
|
||||
* DexFile represents a dex file instead of an odex file
|
||||
*/
|
||||
public OdexDependencies getOdexDependencies() {
|
||||
return odexDependencies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a boolean value indicating whether items in this dex file should be
|
||||
* written back out "in-place", or whether the normal layout logic should be
|
||||
|
@ -25,7 +25,53 @@
|
||||
* INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.jf.dexlib;
|
||||
|
||||
import org.jf.dexlib.Util.Input;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class OdexDependencies {
|
||||
public final int modificationTime;
|
||||
public final int crc;
|
||||
public final int dalvikBuild;
|
||||
|
||||
private final String[] dependencies;
|
||||
private final byte[][] dependencyChecksums;
|
||||
|
||||
public OdexDependencies (Input in) {
|
||||
modificationTime = in.readInt();
|
||||
crc = in.readInt();
|
||||
dalvikBuild = in.readInt();
|
||||
|
||||
int dependencyCount = in.readInt();
|
||||
|
||||
dependencies = new String[dependencyCount];
|
||||
dependencyChecksums = new byte[dependencyCount][];
|
||||
|
||||
for (int i=0; i<dependencyCount; i++) {
|
||||
int stringLength = in.readInt();
|
||||
|
||||
try {
|
||||
dependencies[i] = new String(in.readBytes(stringLength), 0, stringLength-1, "US-ASCII");
|
||||
} catch (UnsupportedEncodingException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
dependencyChecksums[i] = in.readBytes(20);
|
||||
}
|
||||
}
|
||||
|
||||
public int getDependencyCount() {
|
||||
return dependencies.length;
|
||||
}
|
||||
|
||||
public String getDependency(int index) {
|
||||
return dependencies[index];
|
||||
}
|
||||
|
||||
public byte[] getDependencyChecksum(int index) {
|
||||
return Arrays.copyOf(dependencyChecksums[index], dependencyChecksums.length);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user