fix: allow to load Spring Boot jar (#1066)

This commit is contained in:
Skylot
2021-01-04 20:04:28 +03:00
parent 8dd76420c8
commit c47e9cdde4
4 changed files with 141 additions and 29 deletions
@@ -0,0 +1,26 @@
package jadx.plugins.input.javaconvert;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import org.objectweb.asm.ClassReader;
public class AsmUtils {
public static String getNameFromClassFile(Path file) throws IOException {
try (InputStream in = Files.newInputStream(file)) {
return getClassFullName(new ClassReader(in));
}
}
public static String getNameFromClassFile(byte[] content) throws IOException {
return getClassFullName(new ClassReader(content));
}
private static String getClassFullName(ClassReader classReader) {
return classReader.getClassName();
}
}
@@ -1,6 +1,6 @@
package jadx.plugins.input.javaconvert;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -9,13 +9,14 @@ import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.attribute.FileTime;
import java.util.List;
import java.util.Objects;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.objectweb.asm.ClassReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -41,7 +42,7 @@ public class JavaConvertLoader {
try {
convertJar(result, path);
} catch (Exception e) {
LOG.error("Failed to convert file: " + path.toAbsolutePath(), e);
LOG.error("Failed to convert file: {}", path.toAbsolutePath(), e);
}
});
}
@@ -59,7 +60,7 @@ public class JavaConvertLoader {
Path jarFile = Files.createTempFile("jadx-", ".jar");
try (JarOutputStream jo = new JarOutputStream(Files.newOutputStream(jarFile))) {
for (Path file : clsFiles) {
String clsName = getNameFromClassFile(file);
String clsName = AsmUtils.getNameFromClassFile(file);
if (clsName == null || !ZipSecurity.isValidZipEntryName(clsName)) {
throw new IOException("Can't read class name from file: " + file);
}
@@ -74,13 +75,6 @@ public class JavaConvertLoader {
}
}
public static String getNameFromClassFile(Path file) throws IOException {
try (InputStream in = Files.newInputStream(file)) {
ClassReader classReader = new ClassReader(in);
return classReader.getClassName();
}
}
private static void processAars(List<Path> input, ConvertResult result) {
PathMatcher aarMatcher = FileSystems.getDefault().getPathMatcher("glob:**.aar");
input.stream()
@@ -100,6 +94,68 @@ public class JavaConvertLoader {
}
private static void convertJar(ConvertResult result, Path path) throws Exception {
if (repackAndConvertJar(result, path)) {
return;
}
convertSimpleJar(result, path);
}
private static boolean repackAndConvertJar(ConvertResult result, Path path) throws Exception {
// check if jar need a full repackage
Boolean repackNeeded = ZipSecurity.visitZipEntries(path.toFile(), (zipFile, zipEntry) -> {
String entryName = zipEntry.getName();
if (zipEntry.isDirectory()) {
if (entryName.equals("BOOT-INF/")) {
return true; // Spring Boot jar
}
if (entryName.equals("META-INF/versions/")) {
return true; // exclude duplicated classes
}
}
if (entryName.endsWith(".jar")) {
return true; // contains sub jars
}
if (entryName.endsWith("module-info.class")) {
return true; // need to exclude module files
}
return null;
});
if (!Objects.equals(repackNeeded, Boolean.TRUE)) {
return false;
}
Path jarFile = Files.createTempFile("jadx-classes-", ".jar");
result.addTempPath(jarFile);
try (JarOutputStream jo = new JarOutputStream(Files.newOutputStream(jarFile))) {
ZipSecurity.readZipEntries(path.toFile(), (entry, in) -> {
try {
String entryName = entry.getName();
if (entryName.endsWith(".class")) {
if (entryName.endsWith("module-info.class")
|| entryName.startsWith("META-INF/versions/")) {
return;
}
byte[] clsFileContent = inputStreamToByteArray(in);
String clsName = AsmUtils.getNameFromClassFile(clsFileContent);
if (clsName == null || !ZipSecurity.isValidZipEntryName(clsName)) {
throw new IOException("Can't read class name from file: " + entryName);
}
addJarEntry(jo, clsName + ".class", clsFileContent, entry.getLastModifiedTime());
} else if (entryName.endsWith(".jar")) {
Path tempJar = saveInputStreamToFile(in, ".jar");
result.addTempPath(tempJar);
convertJar(result, tempJar);
}
} catch (Exception e) {
LOG.error("Failed to process jar entry: {} in {}", entry, path, e);
}
});
}
convertSimpleJar(result, jarFile);
return true;
}
private static void convertSimpleJar(ConvertResult result, Path path) throws Exception {
Path tempDirectory = Files.createTempDirectory("jadx-");
result.addTempPath(tempDirectory);
@@ -119,18 +175,24 @@ public class JavaConvertLoader {
}
}
public static void addFileToJar(JarOutputStream jar, Path source, String entryName) throws IOException {
try (BufferedInputStream in = new BufferedInputStream(Files.newInputStream(source))) {
JarEntry entry = new JarEntry(entryName);
entry.setTime(Files.getLastModifiedTime(source, LinkOption.NOFOLLOW_LINKS).toMillis());
jar.putNextEntry(entry);
copyStream(in, jar);
jar.closeEntry();
}
private static void addFileToJar(JarOutputStream jar, Path source, String entryName) throws IOException {
byte[] fileContent = Files.readAllBytes(source);
FileTime lastModifiedTime = Files.getLastModifiedTime(source, LinkOption.NOFOLLOW_LINKS);
addJarEntry(jar, entryName, fileContent, lastModifiedTime);
}
public static void copyStream(InputStream input, OutputStream output) throws IOException {
private static void addJarEntry(JarOutputStream jar, String entryName, byte[] content,
FileTime modTime) throws IOException {
JarEntry entry = new JarEntry(entryName);
if (modTime != null) {
entry.setTime(modTime.toMillis());
}
jar.putNextEntry(entry);
jar.write(content);
jar.closeEntry();
}
private static void copyStream(InputStream input, OutputStream output) throws IOException {
byte[] buffer = new byte[8 * 1024];
while (true) {
int count = input.read(buffer);
@@ -141,6 +203,13 @@ public class JavaConvertLoader {
}
}
private static byte[] inputStreamToByteArray(InputStream input) throws IOException {
try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
copyStream(input, output);
return output.toByteArray();
}
}
private static Path saveInputStreamToFile(InputStream in, String suffix) throws IOException {
Path tempJar = Files.createTempFile("jadx-temp-", suffix);
try (OutputStream out = Files.newOutputStream(tempJar)) {