core: decompile '.class' files
This commit is contained in:
@@ -20,7 +20,7 @@ import com.beust.jcommander.ParameterException;
|
||||
|
||||
public final class JadxCLIArgs implements IJadxArgs {
|
||||
|
||||
@Parameter(description = "<input file> (.dex, .apk or .jar)")
|
||||
@Parameter(description = "<input file> (.dex, .apk, .jar or .class)")
|
||||
protected List<String> files;
|
||||
|
||||
@Parameter(names = {"-d", "--output-dir"}, description = "output directory")
|
||||
|
||||
@@ -2,6 +2,7 @@ ext.jadxClasspath = 'clsp-data/android-4.3.jar'
|
||||
|
||||
dependencies {
|
||||
compile files('lib/dx-1.8.jar')
|
||||
compile 'org.ow2.asm:asm:5.0.3'
|
||||
runtime files(jadxClasspath)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package jadx.core.utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.objectweb.asm.ClassReader;
|
||||
|
||||
public class AsmUtils {
|
||||
|
||||
private AsmUtils() {
|
||||
}
|
||||
|
||||
public static String getNameFromClassFile(File file) throws IOException {
|
||||
FileInputStream in = new FileInputStream(file);
|
||||
ClassReader classReader = new ClassReader(in);
|
||||
return classReader.getClassName();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package jadx.core.utils.files;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarOutputStream;
|
||||
|
||||
public class FileUtils {
|
||||
|
||||
private FileUtils() {
|
||||
}
|
||||
|
||||
public static void addFileToJar(JarOutputStream jar, File source, String entryName) throws IOException {
|
||||
BufferedInputStream in = null;
|
||||
try {
|
||||
JarEntry entry = new JarEntry(entryName);
|
||||
entry.setTime(source.lastModified());
|
||||
jar.putNextEntry(entry);
|
||||
in = new BufferedInputStream(new FileInputStream(source));
|
||||
|
||||
byte[] buffer = new byte[8192];
|
||||
while (true) {
|
||||
int count = in.read(buffer);
|
||||
if (count == -1) {
|
||||
break;
|
||||
}
|
||||
jar.write(buffer, 0, count);
|
||||
}
|
||||
jar.closeEntry();
|
||||
} finally {
|
||||
if (in != null) {
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,15 @@
|
||||
package jadx.core.utils.files;
|
||||
|
||||
import jadx.core.utils.AsmUtils;
|
||||
import jadx.core.utils.exceptions.DecodeException;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.jar.JarOutputStream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
@@ -35,37 +37,44 @@ public class InputFile {
|
||||
if (fileName.endsWith(".dex")) {
|
||||
return new Dex(file);
|
||||
}
|
||||
if (fileName.endsWith(".class")) {
|
||||
return loadFromClassFile(file);
|
||||
}
|
||||
if (fileName.endsWith(".apk")) {
|
||||
byte[] data = openDexFromZip(file);
|
||||
if (data == null) {
|
||||
throw new JadxRuntimeException("File 'classes.dex' not found in file: " + file);
|
||||
Dex dex = loadFromZip(file);
|
||||
if (dex == null) {
|
||||
throw new IOException("File 'classes.dex' not found in file: " + file);
|
||||
}
|
||||
return new Dex(data);
|
||||
return dex;
|
||||
}
|
||||
if (fileName.endsWith(".jar")) {
|
||||
// check if jar contains 'classes.dex'
|
||||
byte[] data = openDexFromZip(file);
|
||||
if (data != null) {
|
||||
return new Dex(data);
|
||||
}
|
||||
try {
|
||||
LOG.info("converting to dex: {} ...", fileName);
|
||||
JavaToDex j2d = new JavaToDex();
|
||||
byte[] ba = j2d.convert(file.getAbsolutePath());
|
||||
if (ba.length == 0) {
|
||||
throw new JadxException(j2d.isError() ? j2d.getDxErrors() : "Empty dx output");
|
||||
} else if (j2d.isError()) {
|
||||
LOG.warn("dx message: " + j2d.getDxErrors());
|
||||
}
|
||||
return new Dex(ba);
|
||||
} catch (Throwable e) {
|
||||
throw new DecodeException("java class to dex conversion error:\n " + e.getMessage(), e);
|
||||
Dex dex = loadFromZip(file);
|
||||
if (dex != null) {
|
||||
return dex;
|
||||
}
|
||||
return loadFromJar(file);
|
||||
}
|
||||
throw new DecodeException("Unsupported input file format: " + file);
|
||||
}
|
||||
|
||||
private byte[] openDexFromZip(File file) throws IOException {
|
||||
private static Dex loadFromJar(File jarFile) throws DecodeException {
|
||||
try {
|
||||
LOG.info("converting to dex: {} ...", jarFile.getName());
|
||||
JavaToDex j2d = new JavaToDex();
|
||||
byte[] ba = j2d.convert(jarFile.getAbsolutePath());
|
||||
if (ba.length == 0) {
|
||||
throw new JadxException(j2d.isError() ? j2d.getDxErrors() : "Empty dx output");
|
||||
} else if (j2d.isError()) {
|
||||
LOG.warn("dx message: " + j2d.getDxErrors());
|
||||
}
|
||||
return new Dex(ba);
|
||||
} catch (Throwable e) {
|
||||
throw new DecodeException("java class to dex conversion error:\n " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Dex loadFromZip(File file) throws IOException {
|
||||
ZipFile zf = new ZipFile(file);
|
||||
ZipEntry dex = zf.getEntry("classes.dex");
|
||||
if (dex == null) {
|
||||
@@ -87,7 +96,19 @@ public class InputFile {
|
||||
}
|
||||
zf.close();
|
||||
}
|
||||
return bytesOut.toByteArray();
|
||||
return new Dex(bytesOut.toByteArray());
|
||||
}
|
||||
|
||||
private static Dex loadFromClassFile(File file) throws IOException, DecodeException {
|
||||
File outFile = File.createTempFile("jadx-tmp-", System.nanoTime() + ".jar");
|
||||
outFile.deleteOnExit();
|
||||
FileOutputStream out = new FileOutputStream(outFile);
|
||||
JarOutputStream jo = new JarOutputStream(out);
|
||||
String clsName = AsmUtils.getNameFromClassFile(file);
|
||||
FileUtils.addFileToJar(jo, file, clsName + ".class");
|
||||
jo.close();
|
||||
out.close();
|
||||
return loadFromJar(outFile);
|
||||
}
|
||||
|
||||
public File getFile() {
|
||||
|
||||
@@ -7,17 +7,15 @@ import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.visitors.DepthTraversal;
|
||||
import jadx.core.dex.visitors.IDexTreeVisitor;
|
||||
import jadx.core.utils.files.FileUtils;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarOutputStream;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
@@ -94,7 +92,7 @@ public abstract class InternalJadxTest extends TestUtils {
|
||||
File temp = File.createTempFile("jadx-tmp-", System.nanoTime() + ".jar");
|
||||
JarOutputStream jo = new JarOutputStream(new FileOutputStream(temp));
|
||||
for (File file : list) {
|
||||
add(file, path + "/" + file.getName(), jo);
|
||||
FileUtils.addFileToJar(jo, file, path + "/" + file.getName());
|
||||
}
|
||||
jo.close();
|
||||
if (deleteTmpJar) {
|
||||
@@ -127,29 +125,6 @@ public abstract class InternalJadxTest extends TestUtils {
|
||||
return list;
|
||||
}
|
||||
|
||||
private void add(File source, String entryName, JarOutputStream target) throws IOException {
|
||||
BufferedInputStream in = null;
|
||||
try {
|
||||
JarEntry entry = new JarEntry(entryName);
|
||||
entry.setTime(source.lastModified());
|
||||
target.putNextEntry(entry);
|
||||
in = new BufferedInputStream(new FileInputStream(source));
|
||||
|
||||
byte[] buffer = new byte[1024];
|
||||
while (true) {
|
||||
int count = in.read(buffer);
|
||||
if (count == -1) {
|
||||
break;
|
||||
}
|
||||
target.write(buffer, 0, count);
|
||||
}
|
||||
target.closeEntry();
|
||||
} finally {
|
||||
if (in != null) {
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use only for debug purpose
|
||||
@Deprecated
|
||||
|
||||
Reference in New Issue
Block a user