package jadx.core.utils.files;
import jadx.core.utils.AsmUtils;
import jadx.core.utils.exceptions.DecodeException;
import jadx.core.utils.exceptions.JadxException;
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;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.android.dex.Dex;
public class InputFile {
private static final Logger LOG = LoggerFactory.getLogger(InputFile.class);
private final File file;
private final Dex dexBuf;
public int nextDexIndex = -1;
private final int dexIndex;
public InputFile(File file) throws IOException, DecodeException {
this(file, 0);
}
public InputFile(File file, int dexIndex) throws IOException, DecodeException {
if (!file.exists()) {
throw new IOException("File not found: " + file.getAbsolutePath());
}
this.dexIndex = dexIndex;
this.file = file;
this.dexBuf = loadDexBuffer();
}
private Dex loadDexBuffer() throws IOException, DecodeException {
String fileName = file.getName();
if (fileName.endsWith(".dex")) {
return new Dex(file);
}
if (fileName.endsWith(".class")) {
return loadFromClassFile(file);
}
if (fileName.endsWith(".apk") || fileName.endsWith(".zip")) {
Dex dex = loadFromZip(this,file);
if (dex == null) {
throw new IOException("File 'classes.dex' not found in file: " + file);
}
return dex;
}
if (fileName.endsWith(".jar")) {
// check if jar contains 'classes.dex'
Dex dex = loadFromZip(this,file);
if (dex != null) {
return dex;
}
return loadFromJar(file);
}
throw new DecodeException("Unsupported input file format: " + file);
}
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(InputFile ipf, File file) throws IOException {
ZipFile zf = new ZipFile(file);
String dexName = "classes.dex";
String futureDexName = "classes2.dex";
if (ipf.dexIndex != 0) {
dexName = "classes" + ipf.dexIndex + ".dex";
futureDexName = "classes" + (ipf.dexIndex + 1) + ".dex";
}
ZipEntry dex = zf.getEntry(dexName);
if (dex == null) {
zf.close();
return null;
}
try {
ZipEntry futureDex = zf.getEntry(futureDexName);
if (futureDex != null) {
ipf.nextDexIndex = ipf.dexIndex == 0 ? 2 : ipf.dexIndex + 1;
}
} catch (Exception ex) {
}
ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
InputStream in = null;
try {
in = zf.getInputStream(dex);
byte[] buffer = new byte[8192];
int count;
while ((count = in.read(buffer)) != -1) {
bytesOut.write(buffer, 0, count);
}
} finally {
if (in != null) {
in.close();
}
zf.close();
}
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 = null;
JarOutputStream jo = null;
try {
out = new FileOutputStream(outFile);
jo = new JarOutputStream(out);
String clsName = AsmUtils.getNameFromClassFile(file);
if (clsName == null) {
throw new IOException("Can't read class name from file: " + file);
}
FileUtils.addFileToJar(jo, file, clsName + ".class");
} finally {
if (jo != null) {
jo.close();
}
if (out != null) {
out.close();
}
}
return loadFromJar(outFile);
}
public File getFile() {
return file;
}
public Dex getDexBuffer() {
return dexBuf;
}
@Override
public String toString() {
return file.toString();
}
}