Add support for reading a classes.dex file from an apk/jar

git-svn-id: https://smali.googlecode.com/svn/trunk@470 55b6fa8a-2a1e-11de-a435-ffa8d773f76a
This commit is contained in:
JesusFreke@JesusFreke.com 2009-09-13 21:55:24 +00:00
parent 4080fe659d
commit c8d83493d1
2 changed files with 95 additions and 40 deletions

View File

@ -33,8 +33,7 @@ import org.jf.dexlib.*;
import org.jf.dexlib.Item; import org.jf.dexlib.Item;
import org.jf.dexlib.StringDataItem; import org.jf.dexlib.StringDataItem;
import java.io.File; import java.io.*;
import java.io.UnsupportedEncodingException;
import java.security.DigestException; import java.security.DigestException;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
@ -43,6 +42,9 @@ 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.ZipException;
import java.util.zip.ZipEntry;
/** /**
* <h3>Main use cases</h3> * <h3>Main use cases</h3>
@ -236,8 +238,10 @@ public class DexFile
/** /**
* Construct a new DexFile instance by reading in the given dex file. * Construct a new DexFile instance by reading in the given dex file.
* @param file The dex file to read in * @param file The dex file to read in
* @throws IOException if an IOException occurs
*/ */
public DexFile(String file) { public DexFile(String file)
throws IOException {
this(new File(file), true); this(new File(file), true);
} }
@ -249,16 +253,20 @@ public class DexFile
* @param preserveSignedRegisters If true, keep track of any registers in the debug information * @param preserveSignedRegisters If true, keep track of any registers in the debug information
* that are signed, so they will be written in the same format. See * that are signed, so they will be written in the same format. See
* <code>getPreserveSignedRegisters()</code> * <code>getPreserveSignedRegisters()</code>
* @throws IOException if an IOException occurs
*/ */
public DexFile(String file, boolean preserveSignedRegisters) { public DexFile(String file, boolean preserveSignedRegisters)
throws IOException {
this(new File(file), preserveSignedRegisters); this(new File(file), preserveSignedRegisters);
} }
/** /**
* Construct a new DexFile instance by reading in the given dex file. * Construct a new DexFile instance by reading in the given dex file.
* @param file The dex file to read in * @param file The dex file to read in
* @throws IOException if an IOException occurs
*/ */
public DexFile(File file) { public DexFile(File file)
throws IOException {
this(file, true); this(file, true);
} }
@ -270,14 +278,54 @@ public class DexFile
* @param preserveSignedRegisters If true, keep track of any registers in the debug information * @param preserveSignedRegisters If true, keep track of any registers in the debug information
* that are signed, so they will be written in the same format. * that are signed, so they will be written in the same format.
* @see #getPreserveSignedRegisters * @see #getPreserveSignedRegisters
* @throws IOException if an IOException occurs
*/ */
public DexFile(File file, boolean preserveSignedRegisters) { public DexFile(File file, boolean preserveSignedRegisters)
throws IOException {
this(preserveSignedRegisters); this(preserveSignedRegisters);
long fileLength;
InputStream inputStream;
byte[] magic = FileUtils.readFile(file, 0, 8); byte[] magic = FileUtils.readFile(file, 0, 8);
//do we have a zip file?
if (magic[0] == 0x50 && magic[1] == 0x4B) {
ZipFile zipFile = new ZipFile(file);
ZipEntry zipEntry = zipFile.getEntry("classes.dex");
if (zipEntry == null) {
throw new RuntimeException("zip file " + file.getName() + " does not contain a classes.dex file");
}
fileLength = zipEntry.getSize();
if (fileLength < 40) {
throw new RuntimeException("The classes.dex file in " + file.getName() + " is too small to be a" +
" valid dex file");
} else if (fileLength > Integer.MAX_VALUE) {
throw new RuntimeException("The classes.dex file in " + file.getName() + " is too large to read in");
}
inputStream = new BufferedInputStream(zipFile.getInputStream(zipEntry));
inputStream.mark(8);
for (int i=0; i<8; i++) {
magic[i] = (byte)inputStream.read();
}
inputStream.reset();
} else {
fileLength = file.length();
if (fileLength < 40) {
throw new RuntimeException(file.getName() + " is too small to be a valid dex file");
}
if (fileLength < 40) {
throw new RuntimeException(file.getName() + " is too small to be a valid dex file");
} else if (fileLength > Integer.MAX_VALUE) {
throw new RuntimeException(file.getName() + " is too large to read in");
}
inputStream = new FileInputStream(file);
}
byte[] dexMagic, odexMagic; byte[] dexMagic, odexMagic;
dexMagic = HeaderItem.MAGIC; dexMagic = org.jf.dexlib.HeaderItem.MAGIC;
odexMagic = OdexHeaderItem.MAGIC; odexMagic = OdexHeaderItem.MAGIC;
boolean isDex = true; boolean isDex = true;
@ -294,20 +342,18 @@ public class DexFile
Input in; Input in;
if (isOdex) { if (isOdex) {
byte[] odexHeaderBytes = FileUtils.readFile(file, 0, 40); byte[] odexHeaderBytes = FileUtils.readStream(inputStream, 40);
Input odexHeaderIn = new ByteArrayInput(odexHeaderBytes); Input odexHeaderIn = new ByteArrayInput(odexHeaderBytes);
OdexHeaderItem odexHeader = new OdexHeaderItem(odexHeaderIn); OdexHeaderItem odexHeader = new OdexHeaderItem(odexHeaderIn);
in = new ByteArrayInput(FileUtils.readFile(file, odexHeader.dexOffset, odexHeader.dexLength)); in = new ByteArrayInput(FileUtils.readStream(inputStream, odexHeader.dexLength));
} else if (isDex) { } else if (isDex) {
in = new ByteArrayInput(FileUtils.readFile(file)); in = new ByteArrayInput(FileUtils.readStream(inputStream, (int)fileLength));
} else { } else {
StringBuffer sb = new StringBuffer("bad magic value:");
StringBuilder sb = new StringBuilder();
sb.append("bad magic value:");
for (int i=0; i<8; i++) { for (int i=0; i<8; i++) {
sb.append(" "); sb.append(" ");
sb.append(magic[i]); sb.append(Hex.u1(magic[i]));
} }
throw new RuntimeException(sb.toString()); throw new RuntimeException(sb.toString());
} }

View File

@ -19,6 +19,7 @@ package org.jf.dexlib.Util;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
/** /**
* File I/O utilities. * File I/O utilities.
@ -38,7 +39,8 @@ public final class FileUtils {
* @param fileName non-null; name of the file to read * @param fileName non-null; name of the file to read
* @return non-null; contents of the file * @return non-null; contents of the file
*/ */
public static byte[] readFile(String fileName) { public static byte[] readFile(String fileName)
throws IOException {
File file = new File(fileName); File file = new File(fileName);
return readFile(file); return readFile(file);
} }
@ -50,7 +52,8 @@ public final class FileUtils {
* @param file non-null; the file to read * @param file non-null; the file to read
* @return non-null; contents of the file * @return non-null; contents of the file
*/ */
public static byte[] readFile(File file) { public static byte[] readFile(File file)
throws IOException {
return readFile(file, 0, -1); return readFile(file, 0, -1);
} }
@ -64,7 +67,8 @@ public final class FileUtils {
* end of the file * end of the file
* @return non-null; contents of the file * @return non-null; contents of the file
*/ */
public static byte[] readFile(File file, int offset, int length) { public static byte[] readFile(File file, int offset, int length)
throws IOException {
if (!file.exists()) { if (!file.exists()) {
throw new RuntimeException(file + ": file not found"); throw new RuntimeException(file + ": file not found");
} }
@ -91,33 +95,38 @@ public final class FileUtils {
throw new RuntimeException(file + ": file too short"); throw new RuntimeException(file + ": file too short");
} }
byte[] result = new byte[length]; FileInputStream in = new FileInputStream(file);
try { int at = offset;
FileInputStream in = new FileInputStream(file); while(at > 0) {
long amt = in.skip(at);
int at = offset; if (amt == -1) {
while(at > 0) { throw new RuntimeException(file + ": unexpected EOF");
long amt = in.skip(at);
if (amt == -1) {
throw new RuntimeException(file + ": unexpected EOF");
}
at -= amt;
} }
at -= amt;
while (length > 0) {
int amt = in.read(result, at, length);
if (amt == -1) {
throw new RuntimeException(file + ": unexpected EOF");
}
at += amt;
length -= amt;
}
in.close();
} catch (IOException ex) {
throw new RuntimeException(file + ": trouble reading", ex);
} }
byte[] result = readStream(in, length);
in.close();
return result;
}
public static byte[] readStream(InputStream in, int length)
throws IOException {
byte[] result = new byte[length];
int at=0;
while (length > 0) {
int amt = in.read(result, at, length);
if (amt == -1) {
throw new RuntimeException("unexpected EOF");
}
at += amt;
length -= amt;
}
return result; return result;
} }
} }