diff --git a/dexlib/src/main/java/org/jf/dexlib/DexFile.java b/dexlib/src/main/java/org/jf/dexlib/DexFile.java index bd9731b0..4ac21ff6 100644 --- a/dexlib/src/main/java/org/jf/dexlib/DexFile.java +++ b/dexlib/src/main/java/org/jf/dexlib/DexFile.java @@ -33,8 +33,7 @@ import org.jf.dexlib.*; import org.jf.dexlib.Item; import org.jf.dexlib.StringDataItem; -import java.io.File; -import java.io.UnsupportedEncodingException; +import java.io.*; import java.security.DigestException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -43,6 +42,9 @@ 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; /** *

Main use cases

@@ -236,8 +238,10 @@ public class DexFile /** * Construct a new DexFile instance by reading in the given dex file. * @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); } @@ -249,16 +253,20 @@ public class DexFile * @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 * getPreserveSignedRegisters() + * @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); } /** * Construct a new DexFile instance by reading in the given dex file. * @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); } @@ -270,14 +278,54 @@ public class DexFile * @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 #getPreserveSignedRegisters + * @throws IOException if an IOException occurs */ - public DexFile(File file, boolean preserveSignedRegisters) { + public DexFile(File file, boolean preserveSignedRegisters) + throws IOException { this(preserveSignedRegisters); + long fileLength; + InputStream inputStream; 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; - dexMagic = HeaderItem.MAGIC; + dexMagic = org.jf.dexlib.HeaderItem.MAGIC; odexMagic = OdexHeaderItem.MAGIC; boolean isDex = true; @@ -294,20 +342,18 @@ public class DexFile Input in; if (isOdex) { - byte[] odexHeaderBytes = FileUtils.readFile(file, 0, 40); + byte[] odexHeaderBytes = FileUtils.readStream(inputStream, 40); Input odexHeaderIn = new ByteArrayInput(odexHeaderBytes); 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) { - in = new ByteArrayInput(FileUtils.readFile(file)); + in = new ByteArrayInput(FileUtils.readStream(inputStream, (int)fileLength)); } else { - - StringBuilder sb = new StringBuilder(); - sb.append("bad magic value:"); + StringBuffer sb = new StringBuffer("bad magic value:"); for (int i=0; i<8; i++) { sb.append(" "); - sb.append(magic[i]); + sb.append(Hex.u1(magic[i])); } throw new RuntimeException(sb.toString()); } diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/FileUtils.java b/dexlib/src/main/java/org/jf/dexlib/Util/FileUtils.java index e081a832..668a529f 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Util/FileUtils.java +++ b/dexlib/src/main/java/org/jf/dexlib/Util/FileUtils.java @@ -19,6 +19,7 @@ package org.jf.dexlib.Util; import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; /** * File I/O utilities. @@ -38,7 +39,8 @@ public final class FileUtils { * @param fileName non-null; name of the file to read * @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); return readFile(file); } @@ -50,7 +52,8 @@ public final class FileUtils { * @param file non-null; the file to read * @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); } @@ -64,7 +67,8 @@ public final class FileUtils { * end 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()) { throw new RuntimeException(file + ": file not found"); } @@ -91,33 +95,38 @@ public final class FileUtils { throw new RuntimeException(file + ": file too short"); } - byte[] result = new byte[length]; + FileInputStream in = new FileInputStream(file); - try { - FileInputStream in = new FileInputStream(file); - - int at = offset; - while(at > 0) { - long amt = in.skip(at); - if (amt == -1) { - throw new RuntimeException(file + ": unexpected EOF"); - } - at -= amt; + int at = offset; + while(at > 0) { + long amt = in.skip(at); + if (amt == -1) { + throw new RuntimeException(file + ": unexpected EOF"); } - - 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); + at -= amt; } + 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; } }