Permalink
Browse files

suppor multidex on d2j-dex2jar

--HG--
branch : 2.x
  • Loading branch information...
1 parent 391a48d commit 52f26c63f0ee3746373de069ca25004f47c32a89 @pxb1988 committed May 29, 2015
@@ -0,0 +1,16 @@
+package com.googlecode.d2j.reader;
+
+import com.googlecode.d2j.visitors.DexFileVisitor;
+
+import java.util.List;
+
+public interface BaseDexFileReader {
+
+ void accept(DexFileVisitor dv);
+
+ List<String> getClassNames();
+
+ void accept(DexFileVisitor dv, int config);
+
+ void accept(DexFileVisitor dv, int classIdx, int config);
+}
@@ -39,8 +39,7 @@
* @author <a href="mailto:pxb1988@gmail.com">Panxiaobo</a>
* @version $Rev$
*/
-public class DexFileReader {
-
+public class DexFileReader implements BaseDexFileReader {
/**
* skip debug infos in dex file.
*/
@@ -70,7 +69,6 @@
*/
public static final int KEEP_CLINIT = 1 << 7;
-
// private static final int REVERSE_ENDIAN_CONSTANT = 0x78563412;
static final int DBG_END_SEQUENCE = 0x00;
@@ -109,7 +107,6 @@
private static final int VALUE_BOOLEAN = 31;
final ByteBuffer annotationSetRefListIn;
final ByteBuffer annotationsDirectoryItemIn;
-
final ByteBuffer annotationSetItemIn;
final ByteBuffer annotationItemIn;
final ByteBuffer classDataIn;
@@ -536,10 +533,12 @@ private void read_debug_info(int offset, int regSize, boolean isStatic, Method m
*
* @param dv
*/
+ @Override
public void accept(DexFileVisitor dv) {
this.accept(dv, 0);
}
+ @Override
public List<String> getClassNames() {
List<String> names = new ArrayList<>(class_defs_size);
ByteBuffer in = classDefIn;
@@ -560,6 +559,7 @@ public void accept(DexFileVisitor dv) {
* config flags, {@link #SKIP_CODE}, {@link #SKIP_DEBUG}, {@link #SKIP_ANNOTATION},
* {@link #SKIP_FIELD_CONSTANT}
*/
+ @Override
public void accept(DexFileVisitor dv, int config) {
for (int cid = 0; cid < class_defs_size; cid++) {
accept(dv, cid, config);
@@ -579,6 +579,7 @@ public void accept(DexFileVisitor dv, int config) {
* config flags, {@link #SKIP_CODE}, {@link #SKIP_DEBUG}, {@link #SKIP_ANNOTATION},
* {@link #SKIP_FIELD_CONSTANT}
*/
+ @Override
public void accept(DexFileVisitor dv, int classIdx, int config) {
classDefIn.position(classIdx * 32);
int class_idx = classDefIn.getInt();
@@ -1294,7 +1295,7 @@ private void findTryCatch(ByteBuffer in, DexCodeVisitor dcv, int tries_size, int
findTryCatch(in, dcv, tries_size, insns, labelsMap, handlers);
}
// 处理debug信息
- if (debug_info_off != 0 && (0 == (config & DexFileReader.SKIP_DEBUG))) {
+ if (debug_info_off != 0 && (0 == (config & SKIP_DEBUG))) {
DexDebugVisitor ddv = dcv.visitDebug();
if (ddv != null) {
read_debug_info(debug_info_off, registers_size, isStatic, method, labelsMap, ddv);
@@ -0,0 +1,118 @@
+package com.googlecode.d2j.reader;
+
+import com.googlecode.d2j.util.zip.AccessBufByteArrayOutputStream;
+import com.googlecode.d2j.util.zip.ZipEntry;
+import com.googlecode.d2j.util.zip.ZipFile;
+import com.googlecode.d2j.visitors.DexFileVisitor;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+
+public class MultiDexFileReader implements BaseDexFileReader {
+ final private List<DexFileReader> readers = new ArrayList<>();
+ final private List<Item> items = new ArrayList<>();
+
+ public MultiDexFileReader(Collection<DexFileReader> readers) {
+ this.readers.addAll(readers);
+ init();
+ }
+
+ private static byte[] toByteArray(InputStream is) throws IOException {
+ AccessBufByteArrayOutputStream out = new AccessBufByteArrayOutputStream();
+ byte[] buff = new byte[1024];
+ for (int c = is.read(buff); c > 0; c = is.read(buff)) {
+ out.write(buff, 0, c);
+ }
+ return out.getBuf();
+ }
+
+ public static BaseDexFileReader open(byte[] data) throws IOException {
+ if (data.length < 3) {
+ throw new IOException("File too small to be a dex/zip");
+ }
+ if ("dex".equals(new String(data, 0, 3, StandardCharsets.ISO_8859_1))) {// dex
+ return new DexFileReader(data);
+ } else if ("PK".equals(new String(data, 0, 2, StandardCharsets.ISO_8859_1))) {// ZIP
+ TreeMap<String, DexFileReader> dexFileReaders = new TreeMap<>();
+ try (ZipFile zipFile = new ZipFile(data)) {
+ for (ZipEntry e : zipFile.entries()) {
+ String entryName = e.getName();
+ if (entryName.startsWith("classes") && entryName.endsWith(".dex")) {
+ if (!dexFileReaders.containsKey(entryName)) { // only the first one
+ dexFileReaders.put(entryName, new DexFileReader(toByteArray(zipFile.getInputStream(e))));
+ }
+ }
+ }
+ }
+ if (dexFileReaders.size() == 0) {
+ throw new IOException("Can not find classes.dex in zip file");
+ } else if (dexFileReaders.size() == 1) {
+ return dexFileReaders.firstEntry().getValue();
+ } else {
+ return new MultiDexFileReader(dexFileReaders.values());
+ }
+ }
+ throw new IOException("the src file not a .dex or zip file");
+ }
+
+ void init() {
+ Set<String> classes = new HashSet<>();
+ for (DexFileReader reader : readers) {
+ List<String> classNames = reader.getClassNames();
+ for (int i = 0; i < classNames.size(); i++) {
+ String className = classNames.get(i);
+ if (classes.add(className)) {
+ items.add(new Item(i, reader, className));
+ }
+ }
+ }
+ }
+
+ @Override
+ public void accept(DexFileVisitor dv) {
+ accept(dv, 0);
+ }
+
+ @Override
+ public List<String> getClassNames() {
+ return new AbstractList<String>() {
+ @Override
+ public String get(int index) {
+ return items.get(index).className;
+ }
+
+ @Override
+ public int size() {
+ return items.size();
+ }
+ };
+ }
+
+ @Override
+ public void accept(DexFileVisitor dv, int config) {
+ int size = items.size();
+ for (int i = 0; i < size; i++) {
+ accept(dv, i, config);
+ }
+ }
+
+ @Override
+ public void accept(DexFileVisitor dv, int classIdx, int config) {
+ Item item = items.get(classIdx);
+ item.reader.accept(dv, item.idx, config);
+ }
+
+ static class Item {
+ int idx;
+ DexFileReader reader;
+ String className;
+
+ public Item(int i, DexFileReader reader, String className) {
+ idx = i;
+ this.reader = reader;
+ this.className = className;
+ }
+ }
+}
@@ -16,6 +16,7 @@
package com.googlecode.d2j.reader.zip;
+import com.googlecode.d2j.util.zip.AccessBufByteArrayOutputStream;
import com.googlecode.d2j.util.zip.ZipEntry;
import com.googlecode.d2j.util.zip.ZipFile;
@@ -33,12 +34,12 @@
*/
public class ZipUtil {
public static byte[] toByteArray(InputStream is) throws IOException {
- ByteArrayOutputStream out = new ByteArrayOutputStream();
+ AccessBufByteArrayOutputStream out = new AccessBufByteArrayOutputStream();
byte[] buff = new byte[1024];
for (int c = is.read(buff); c > 0; c = is.read(buff)) {
out.write(buff, 0, c);
}
- return out.toByteArray();
+ return out.getBuf();
}
/**
@@ -0,0 +1,9 @@
+package com.googlecode.d2j.util.zip;
+
+import java.io.ByteArrayOutputStream;
+
+public class AccessBufByteArrayOutputStream extends ByteArrayOutputStream {
+ public byte[] getBuf() {
+ return buf;
+ }
+}
@@ -16,7 +16,6 @@
*/
package com.googlecode.d2j.util.zip;
-import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.zip.*;
@@ -30,12 +29,6 @@ public AutoSTOREDZipOutputStream(OutputStream out) {
super(out);
}
- static class AccessBufByteArrayOutputStream extends ByteArrayOutputStream {
- public byte[] getBuf() {
- return buf;
- }
- }
-
private CRC32 crc = new CRC32();
private ZipEntry delayedEntry;
private AccessBufByteArrayOutputStream delayedOutputStream;
@@ -16,15 +16,16 @@
*/
package com.googlecode.dex2jar.tools;
-import java.io.File;
-import java.nio.file.Files;
-import java.nio.file.Path;
-
import com.googlecode.d2j.dex.Dex2jar;
+import com.googlecode.d2j.reader.BaseDexFileReader;
import com.googlecode.d2j.reader.DexFileReader;
-import com.googlecode.d2j.reader.zip.ZipUtil;
+import com.googlecode.d2j.reader.MultiDexFileReader;
import com.googlecode.dex2jar.ir.ET;
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
@BaseCmd.Syntax(cmd = "d2j-dex2jar", syntax = "[options] <file0> [file1 ... fileN]", desc = "convert dex to jar")
public class Dex2jarCmd extends BaseCmd {
@@ -101,7 +102,7 @@ protected void doCommandLine() throws Exception {
Path file = output == null ? currentDir.resolve(baseName + "-dex2jar.jar") : output;
System.err.println("dex2jar " + fileName + " -> " + file);
- DexFileReader reader = new DexFileReader(ZipUtil.readDex(new File(fileName)));
+ BaseDexFileReader reader = MultiDexFileReader.open(Files.readAllBytes(new File(fileName).toPath()));
BaksmaliBaseDexExceptionHandler handler = notHandleException ? null : new BaksmaliBaseDexExceptionHandler();
Dex2jar.from(reader).withExceptionHandler(handler).reUseReg(reuseReg).topoLogicalSort()
.skipDebug(!debugInfo).optimizeSynchronized(this.optmizeSynchronized).printIR(printIR)
@@ -25,6 +25,8 @@
import java.util.*;
import java.util.concurrent.*;
+import com.googlecode.d2j.reader.BaseDexFileReader;
+import com.googlecode.d2j.reader.MultiDexFileReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
@@ -34,7 +36,6 @@
import com.googlecode.d2j.node.DexClassNode;
import com.googlecode.d2j.node.DexFileNode;
import com.googlecode.d2j.reader.DexFileReader;
-import com.googlecode.d2j.reader.zip.ZipUtil;
@BaseCmd.Syntax(cmd = "d2j-mt-dex2jar", syntax = "[options] <file0> [file1 ... fileN]", desc = "convert dex to jar")
public class Dex2jarMultiThreadCmd extends BaseCmd {
@@ -90,7 +91,7 @@ private void run0(String fileName, final ExecutorService executorService) throws
final Path errorFile = currentDir.resolve(baseName + "-error.zip");
System.err.println("dex2jar " + fileName + " -> " + file);
final BaksmaliBaseDexExceptionHandler exceptionHandler = new BaksmaliBaseDexExceptionHandler();
- DexFileReader reader = new DexFileReader(ZipUtil.readDex(new File(fileName)));
+ BaseDexFileReader reader = MultiDexFileReader.open(Files.readAllBytes(new File(fileName).toPath()));
DexFileNode fileNode = new DexFileNode();
try {
reader.accept(fileNode, DexFileReader.SKIP_DEBUG | DexFileReader.IGNORE_READ_EXCEPTION);
@@ -28,6 +28,7 @@
import java.util.Map;
import com.googlecode.d2j.node.DexMethodNode;
+import com.googlecode.d2j.reader.BaseDexFileReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
@@ -50,7 +51,7 @@ public static Dex2jar from(ByteBuffer in) throws IOException {
return from(new DexFileReader(in));
}
- public static Dex2jar from(DexFileReader reader) {
+ public static Dex2jar from(BaseDexFileReader reader) {
return new Dex2jar(reader);
}
@@ -68,11 +69,11 @@ public static Dex2jar from(String in) throws IOException {
private DexExceptionHandler exceptionHandler;
- final private DexFileReader reader;
+ final private BaseDexFileReader reader;
private int readerConfig;
private int v3Config;
- private Dex2jar(DexFileReader reader) {
+ private Dex2jar(BaseDexFileReader reader) {
super();
this.reader = reader;
readerConfig |= DexFileReader.SKIP_DEBUG;
@@ -175,7 +176,7 @@ public DexExceptionHandler getExceptionHandler() {
return exceptionHandler;
}
- public DexFileReader getReader() {
+ public BaseDexFileReader getReader() {
return reader;
}

0 comments on commit 52f26c6

Please sign in to comment.