Skip to content

Commit

Permalink
fix: handle big .jar files (using multi-dex option) (#390) (PR #568)
Browse files Browse the repository at this point in the history
  • Loading branch information
asashour authored and skylot committed Apr 8, 2019
1 parent ecaa87e commit 79ccaad
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 24 deletions.
47 changes: 29 additions & 18 deletions jadx-core/src/main/java/jadx/core/utils/files/InputFile.java
Original file line number Diff line number Diff line change
@@ -1,30 +1,33 @@
package jadx.core.utils.files;

import static jadx.core.utils.files.FileUtils.isApkFile;
import static jadx.core.utils.files.FileUtils.isZipDexFile;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import com.android.dex.Dex;
import org.apache.commons.io.IOUtils;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.android.dex.Dex;

import jadx.core.utils.AsmUtils;
import jadx.core.utils.exceptions.DecodeException;
import jadx.core.utils.exceptions.JadxException;
import jadx.core.utils.exceptions.JadxRuntimeException;

import static jadx.core.utils.files.FileUtils.isApkFile;
import static jadx.core.utils.files.FileUtils.isZipDexFile;

public class InputFile {
private static final Logger LOG = LoggerFactory.getLogger(InputFile.class);

Expand Down Expand Up @@ -52,7 +55,9 @@ private void searchDexFiles(boolean skipSources) throws IOException, DecodeExcep
return;
}
if (fileName.endsWith(".class")) {
addDexFile(loadFromClassFile(file));
for (Dex dex : loadFromClassFile(file)) {
addDexFile(dex);
}
return;
}
if (isApkFile(file) || isZipDexFile(file)) {
Expand All @@ -65,7 +70,9 @@ private void searchDexFiles(boolean skipSources) throws IOException, DecodeExcep
return;
}
if (fileName.endsWith(".jar")) {
addDexFile(loadFromJar(file));
for (Dex dex : loadFromJar(file.toPath())) {
addDexFile(dex);
}
return;
}
if (fileName.endsWith(".aar")) {
Expand Down Expand Up @@ -116,11 +123,11 @@ private boolean loadFromZip(String ext) throws IOException, DecodeException {

case ".jar":
index++;
File jarFile = FileUtils.createTempFile(entryName);
try (FileOutputStream fos = new FileOutputStream(jarFile)) {
IOUtils.copy(inputStream, fos);
Path jarFile = Files.createTempFile(entryName, ".jar");
Files.copy(inputStream, jarFile);
for (Dex dex : loadFromJar(jarFile)) {
addDexFile(entryName, dex);
}
addDexFile(entryName, loadFromJar(jarFile));
break;

default:
Expand Down Expand Up @@ -155,15 +162,19 @@ private Dex makeDexBuf(String entryName, InputStream inputStream) {
}
}

private static Dex loadFromJar(File jarFile) throws DecodeException {
private static List<Dex> loadFromJar(Path jar) throws DecodeException {
JavaToDex j2d = new JavaToDex();
try {
LOG.info("converting to dex: {} ...", jarFile.getName());
byte[] ba = j2d.convert(jarFile.getAbsolutePath());
if (ba.length == 0) {
LOG.info("converting to dex: {} ...", jar.getFileName());
List<byte[]> byteList = j2d.convert(jar);
if (byteList.isEmpty()) {
throw new JadxException("Empty dx output");
}
return new Dex(ba);
List<Dex> dexList = new ArrayList<>(byteList.size());
for (byte[] b : byteList) {
dexList.add(new Dex(b));
}
return dexList;
} catch (Exception e) {
throw new DecodeException("java class to dex conversion error:\n " + e.getMessage(), e);
} finally {
Expand All @@ -173,9 +184,9 @@ private static Dex loadFromJar(File jarFile) throws DecodeException {
}
}

private static Dex loadFromClassFile(File file) throws IOException, DecodeException {
File outFile = FileUtils.createTempFile("cls.jar");
try (JarOutputStream jo = new JarOutputStream(new FileOutputStream(outFile))) {
private static List<Dex> loadFromClassFile(File file) throws IOException, DecodeException {
Path outFile = Files.createTempFile("cls", ".jar");
try (JarOutputStream jo = new JarOutputStream(Files.newOutputStream(outFile))) {
String clsName = AsmUtils.getNameFromClassFile(file);
if (clsName == null || !ZipSecurity.isValidZipEntryName(clsName)) {
throw new IOException("Can't read class name from file: " + file);
Expand Down
30 changes: 24 additions & 6 deletions jadx-core/src/main/java/jadx/core/utils/files/JavaToDex.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package jadx.core.utils.files;

import java.io.ByteArrayOutputStream;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;

import com.android.dx.command.dexer.DxContext;
import com.android.dx.command.dexer.Main;
Expand All @@ -13,11 +18,12 @@ public class JavaToDex {
private static final String CHARSET_NAME = "UTF-8";

private static class DxArgs extends Arguments {
public DxArgs(DxContext context, String dexFile, String[] input) {
public DxArgs(DxContext context, String dexDir, String[] input) {
super(context);
outName = dexFile;
outName = dexDir;
fileNames = input;
jarOutput = false;
multiDex = true;

optimize = true;
localInfo = true;
Expand All @@ -31,17 +37,29 @@ public DxArgs(DxContext context, String dexFile, String[] input) {

private String dxErrors;

public byte[] convert(String javaFile) throws JadxException {
public List<byte[]> convert(Path jar) throws JadxException {
try (ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayOutputStream errOut = new ByteArrayOutputStream()) {
DxContext context = new DxContext(out, errOut);
DxArgs args = new DxArgs(context, "-", new String[]{javaFile});
int result = (new Main(context)).runDx(args);
Path dir = Files.createTempDirectory("jadx");
DxArgs args = new DxArgs(
context,
dir.toAbsolutePath().toString(),
new String[]{jar.toAbsolutePath().toString()});
int result = new Main(context).runDx(args);
dxErrors = errOut.toString(CHARSET_NAME);
if (result != 0) {
throw new JadxException("Java to dex conversion error, code: " + result);
}
return out.toByteArray();
List<byte[]> list = new ArrayList<>();
try (DirectoryStream<Path> ds = Files.newDirectoryStream(dir)) {
for (Path child : ds) {
list.add(Files.readAllBytes(child));
Files.delete(child);
}
}
Files.delete(dir);
return list;
} catch (Exception e) {
throw new JadxException("dx exception: " + e.getMessage(), e);
}
Expand Down

0 comments on commit 79ccaad

Please sign in to comment.