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 org.jf.dexlib.StringIdItem;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
public class baksmali {
|
public class baksmali {
|
||||||
public static boolean noParameterRegisters = false;
|
public static boolean noParameterRegisters = false;
|
||||||
@ -51,10 +53,10 @@ public class baksmali {
|
|||||||
public static String bootClassPath;
|
public static String bootClassPath;
|
||||||
|
|
||||||
public static void disassembleDexFile(String dexFilePath, DexFile dexFile, boolean deodex, String outputDirectory,
|
public static void disassembleDexFile(String dexFilePath, DexFile dexFile, boolean deodex, String outputDirectory,
|
||||||
String[] classPathDirs, String bootClassPath, boolean noParameterRegisters,
|
String[] classPathDirs, String bootClassPath, String extraBootClassPath,
|
||||||
boolean useLocalsDirective, boolean useSequentialLabels,
|
boolean noParameterRegisters, boolean useLocalsDirective,
|
||||||
boolean outputDebugInfo, boolean addCodeOffsets, int registerInfo,
|
boolean useSequentialLabels, boolean outputDebugInfo, boolean addCodeOffsets,
|
||||||
boolean verify)
|
int registerInfo, boolean verify)
|
||||||
{
|
{
|
||||||
baksmali.noParameterRegisters = noParameterRegisters;
|
baksmali.noParameterRegisters = noParameterRegisters;
|
||||||
baksmali.useLocalsDirective = useLocalsDirective;
|
baksmali.useLocalsDirective = useLocalsDirective;
|
||||||
@ -68,8 +70,28 @@ public class baksmali {
|
|||||||
|
|
||||||
if (registerInfo != 0 || deodex || verify) {
|
if (registerInfo != 0 || deodex || verify) {
|
||||||
try {
|
try {
|
||||||
ClassPath.InitializeClassPath(classPathDirs, bootClassPath==null?null:bootClassPath.split(":"),
|
String[] extraBootClassPathArray = null;
|
||||||
dexFilePath, dexFile);
|
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) {
|
} catch (Exception ex) {
|
||||||
System.err.println("\n\nError occured while loading boot class path files. Aborting.");
|
System.err.println("\n\nError occured while loading boot class path files. Aborting.");
|
||||||
ex.printStackTrace(System.err);
|
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);
|
bootClassPathDirsArray[i] = bootClassPathDirs.get(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
ClassPath.InitializeClassPath(bootClassPathDirsArray, bootClassPath==null?null:bootClassPath.split(":"),
|
ClassPath.InitializeClassPath(bootClassPathDirsArray, bootClassPath==null?null:bootClassPath.split(":"), null,
|
||||||
null, null);
|
null, null);
|
||||||
|
|
||||||
ClassPath.validateAgainstDeodexerant(deodexerantHost, deodexerantPort, classStartIndex);
|
ClassPath.validateAgainstDeodexerant(deodexerantHost, deodexerantPort, classStartIndex);
|
||||||
|
@ -30,6 +30,8 @@ package org.jf.baksmali;
|
|||||||
|
|
||||||
import org.apache.commons.cli.*;
|
import org.apache.commons.cli.*;
|
||||||
import org.jf.dexlib.DexFile;
|
import org.jf.dexlib.DexFile;
|
||||||
|
import org.jf.dexlib.OdexDependencies;
|
||||||
|
import org.jf.dexlib.Util.ExceptionWithContext;
|
||||||
import org.jf.util.*;
|
import org.jf.util.*;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@ -111,7 +113,8 @@ public class main {
|
|||||||
String dumpFileName = null;
|
String dumpFileName = null;
|
||||||
String outputDexFileName = null;
|
String outputDexFileName = null;
|
||||||
String inputDexFileName = 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>();
|
List<String> bootClassPathDirs = new ArrayList<String>();
|
||||||
bootClassPathDirs.add(".");
|
bootClassPathDirs.add(".");
|
||||||
|
|
||||||
@ -193,7 +196,7 @@ public class main {
|
|||||||
case 'c':
|
case 'c':
|
||||||
String bcp = commandLine.getOptionValue("c");
|
String bcp = commandLine.getOptionValue("c");
|
||||||
if (bcp != null && bcp.charAt(0) == ':') {
|
if (bcp != null && bcp.charAt(0) == ':') {
|
||||||
bootClassPath = bootClassPath + bcp;
|
extraBootClassPathEntries.append(bcp);
|
||||||
} else {
|
} else {
|
||||||
bootClassPath = bcp;
|
bootClassPath = bcp;
|
||||||
}
|
}
|
||||||
@ -257,6 +260,10 @@ public class main {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
deodex = false;
|
deodex = false;
|
||||||
|
|
||||||
|
if (bootClassPath == null) {
|
||||||
|
bootClassPath = "core.jar:ext.jar:framework.jar:android.policy.jar:services.jar";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (disassemble) {
|
if (disassemble) {
|
||||||
@ -266,8 +273,9 @@ public class main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
baksmali.disassembleDexFile(dexFileFile.getPath(), dexFile, deodex, outputDirectory,
|
baksmali.disassembleDexFile(dexFileFile.getPath(), dexFile, deodex, outputDirectory,
|
||||||
bootClassPathDirsArray, bootClassPath, noParameterRegisters, useLocalsDirective,
|
bootClassPathDirsArray, bootClassPath, extraBootClassPathEntries.toString(),
|
||||||
useSequentialLabels, outputDebugInfo, addCodeOffsets, registerInfo, verify);
|
noParameterRegisters, useLocalsDirective, useSequentialLabels, outputDebugInfo, addCodeOffsets,
|
||||||
|
registerInfo, verify);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((doDump || write) && !dexFile.isOdex()) {
|
if ((doDump || write) && !dexFile.isOdex()) {
|
||||||
|
@ -38,6 +38,8 @@ import org.jf.dexlib.Util.SparseArray;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
public class ClassPath {
|
public class ClassPath {
|
||||||
private static ClassPath theClassPath = null;
|
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.
|
//This is only used while initialing the class path. It is set to null after initialization has finished.
|
||||||
private LinkedHashMap<String, TempClassInfo> tempClasses;
|
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) {
|
if (theClassPath != null) {
|
||||||
throw new ExceptionWithContext("Cannot initialize ClassPath multiple times");
|
throw new ExceptionWithContext("Cannot initialize ClassPath multiple times");
|
||||||
}
|
}
|
||||||
|
|
||||||
theClassPath = new ClassPath();
|
theClassPath = new ClassPath();
|
||||||
theClassPath.initClassPath(classPathDirs, bootClassPath, dexFilePath, dexFile);
|
theClassPath.initClassPath(classPathDirs, bootClassPath, extraBootClassPathEntries, dexFilePath, dexFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ClassPath() {
|
private ClassPath() {
|
||||||
classDefs = new HashMap<String, ClassDef>();
|
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>();
|
tempClasses = new LinkedHashMap<String, TempClassInfo>();
|
||||||
|
|
||||||
if (bootClassPath != null) {
|
if (bootClassPath != null) {
|
||||||
@ -71,6 +133,12 @@ public class ClassPath {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (extraBootClassPathEntries != null) {
|
||||||
|
for (String bootClassPathEntry: extraBootClassPathEntries) {
|
||||||
|
loadBootClassPath(classPathDirs, bootClassPathEntry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (dexFile != null) {
|
if (dexFile != null) {
|
||||||
loadDexFile(dexFilePath, dexFile);
|
loadDexFile(dexFilePath, dexFile);
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,6 @@
|
|||||||
package org.jf.dexlib;
|
package org.jf.dexlib;
|
||||||
|
|
||||||
import org.jf.dexlib.Util.*;
|
import org.jf.dexlib.Util.*;
|
||||||
import org.jf.dexlib.*;
|
|
||||||
import org.jf.dexlib.Item;
|
import org.jf.dexlib.Item;
|
||||||
import org.jf.dexlib.StringDataItem;
|
import org.jf.dexlib.StringDataItem;
|
||||||
|
|
||||||
@ -37,13 +36,11 @@ import java.io.*;
|
|||||||
import java.security.DigestException;
|
import java.security.DigestException;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.zip.Adler32;
|
import java.util.zip.Adler32;
|
||||||
import java.util.zip.ZipFile;
|
import java.util.zip.ZipFile;
|
||||||
import java.util.zip.ZipException;
|
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -180,6 +177,7 @@ public class DexFile
|
|||||||
*/
|
*/
|
||||||
private boolean isOdex = false;
|
private boolean isOdex = false;
|
||||||
|
|
||||||
|
private OdexDependencies odexDependencies;
|
||||||
|
|
||||||
private int dataOffset;
|
private int dataOffset;
|
||||||
private int dataSize;
|
private int dataSize;
|
||||||
@ -338,7 +336,7 @@ public class DexFile
|
|||||||
byte[] dexMagic, odexMagic;
|
byte[] dexMagic, odexMagic;
|
||||||
|
|
||||||
dexMagic = org.jf.dexlib.HeaderItem.MAGIC;
|
dexMagic = org.jf.dexlib.HeaderItem.MAGIC;
|
||||||
odexMagic = OdexHeaderItem.MAGIC;
|
odexMagic = OdexHeader.MAGIC;
|
||||||
|
|
||||||
boolean isDex = true;
|
boolean isDex = true;
|
||||||
this.isOdex = true;
|
this.isOdex = true;
|
||||||
@ -354,9 +352,25 @@ public class DexFile
|
|||||||
if (isOdex) {
|
if (isOdex) {
|
||||||
byte[] odexHeaderBytes = FileUtils.readStream(inputStream, 40);
|
byte[] odexHeaderBytes = FileUtils.readStream(inputStream, 40);
|
||||||
Input odexHeaderIn = new ByteArrayInput(odexHeaderBytes);
|
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));
|
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) {
|
} else if (isDex) {
|
||||||
in = new ByteArrayInput(FileUtils.readStream(inputStream, (int)fileLength));
|
in = new ByteArrayInput(FileUtils.readStream(inputStream, (int)fileLength));
|
||||||
} else {
|
} else {
|
||||||
@ -515,6 +529,14 @@ public class DexFile
|
|||||||
return this.isOdex;
|
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
|
* 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
|
* 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
|
* INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.jf.dexlib;
|
package org.jf.dexlib;
|
||||||
|
|
||||||
|
import org.jf.dexlib.Util.Input;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
public class OdexDependencies {
|
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