JavaConvertLoader.java 5.8 KB
Newer Older
1 2
package jadx.plugins.input.javaconvert;

S
Skylot 已提交
3
import java.io.BufferedInputStream;
4
import java.io.IOException;
S
Skylot 已提交
5 6 7
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.FileSystems;
8
import java.nio.file.Files;
9
import java.nio.file.LinkOption;
10
import java.nio.file.Path;
S
Skylot 已提交
11
import java.nio.file.PathMatcher;
12
import java.util.List;
S
Skylot 已提交
13 14
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
15 16 17
import java.util.stream.Collectors;
import java.util.stream.Stream;

S
Skylot 已提交
18
import org.objectweb.asm.ClassReader;
19 20 21
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

S
Skylot 已提交
22 23
import jadx.api.plugins.utils.ZipSecurity;

24 25 26 27 28
public class JavaConvertLoader {
	private static final Logger LOG = LoggerFactory.getLogger(JavaConvertLoader.class);

	public static ConvertResult process(List<Path> input) {
		ConvertResult result = new ConvertResult();
S
Skylot 已提交
29
		processJars(input, result);
S
Skylot 已提交
30
		processAars(input, result);
S
Skylot 已提交
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
		processClassFiles(input, result);
		return result;
	}

	private static void processJars(List<Path> input, ConvertResult result) {
		PathMatcher jarMatcher = FileSystems.getDefault().getPathMatcher("glob:**.jar");
		input.stream()
				.filter(path -> Files.isRegularFile(path, LinkOption.NOFOLLOW_LINKS))
				.filter(jarMatcher::matches)
				.forEach(path -> {
					try {
						convertJar(result, path);
					} catch (Exception e) {
						LOG.error("Failed to convert file: " + path.toAbsolutePath(), e);
					}
				});
	}

	private static void processClassFiles(List<Path> input, ConvertResult result) {
		PathMatcher jarMatcher = FileSystems.getDefault().getPathMatcher("glob:**.class");
		List<Path> clsFiles = input.stream()
				.filter(path -> Files.isRegularFile(path, LinkOption.NOFOLLOW_LINKS))
				.filter(jarMatcher::matches)
				.collect(Collectors.toList());
		if (clsFiles.isEmpty()) {
			return;
		}
		try {
			Path jarFile = Files.createTempFile("jadx-", ".jar");
			try (JarOutputStream jo = new JarOutputStream(Files.newOutputStream(jarFile))) {
				for (Path file : clsFiles) {
					String clsName = getNameFromClassFile(file);
					if (clsName == null || !ZipSecurity.isValidZipEntryName(clsName)) {
						throw new IOException("Can't read class name from file: " + file);
					}
					addFileToJar(jo, file, clsName + ".class");
67 68
				}
			}
S
Skylot 已提交
69 70 71 72 73
			result.addTempPath(jarFile);
			LOG.debug("Packed class files {} into jar {}", clsFiles, jarFile);
			convertJar(result, jarFile);
		} catch (Exception e) {
			LOG.error("Error process class files", e);
74 75 76
		}
	}

S
Skylot 已提交
77 78 79 80 81
	public static String getNameFromClassFile(Path file) throws IOException {
		try (InputStream in = Files.newInputStream(file)) {
			ClassReader classReader = new ClassReader(in);
			return classReader.getClassName();
		}
82 83
	}

S
Skylot 已提交
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
	private static void processAars(List<Path> input, ConvertResult result) {
		PathMatcher aarMatcher = FileSystems.getDefault().getPathMatcher("glob:**.aar");
		input.stream()
				.filter(path -> Files.isRegularFile(path, LinkOption.NOFOLLOW_LINKS))
				.filter(aarMatcher::matches)
				.forEach(path -> ZipSecurity.readZipEntries(path.toFile(), (entry, in) -> {
					try {
						if (entry.getName().endsWith(".jar")) {
							Path tempJar = saveInputStreamToFile(in, ".jar");
							result.addTempPath(tempJar);
							convertJar(result, tempJar);
						}
					} catch (Exception e) {
						LOG.error("Failed to process zip entry: {}", entry, e);
					}
				}));
	}

102 103 104 105 106 107 108
	/**
	 * Process a jar file by converting the contained class files and extract the embedded JAR files
	 *
	 * @param result
	 * @param path
	 * @throws Exception
	 */
S
Skylot 已提交
109
	private static void convertJar(ConvertResult result, Path path) throws Exception {
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
		// Convert the contained class files
		convertSimpleJar(result, path);

		// Process the embedded jar files
		ZipSecurity.readZipEntries(path.toFile(), (entry, in) -> {
			try {
				if (entry.getName().endsWith(".jar")) {
					Path tempJar = saveInputStreamToFile(in, ".jar");
					result.addTempPath(tempJar);
					convertSimpleJar(result, tempJar);
				}
			} catch (Exception e) {
				LOG.error("Failed to process jar entry: {} in {}", entry, path, e);
			}
		});
	}

	private static void convertSimpleJar(ConvertResult result, Path toBeConvertedPath) throws Exception {
128 129 130
		Path tempDirectory = Files.createTempDirectory("jadx-");
		result.addTempPath(tempDirectory);

131
		D8Converter.run(toBeConvertedPath, tempDirectory);
132

133
		LOG.debug("Converted to dex: {}", toBeConvertedPath.toAbsolutePath());
134 135 136 137
		result.addConvertedFiles(collectFilesInDir(tempDirectory));
	}

	private static List<Path> collectFilesInDir(Path tempDirectory) throws IOException {
S
Skylot 已提交
138
		PathMatcher dexMatcher = FileSystems.getDefault().getPathMatcher("glob:**.dex");
139 140 141
		try (Stream<Path> pathStream = Files.walk(tempDirectory, 1)) {
			return pathStream
					.filter(p -> Files.isRegularFile(p, LinkOption.NOFOLLOW_LINKS))
S
Skylot 已提交
142
					.filter(dexMatcher::matches)
143
					.collect(Collectors.toList());
144 145
		}
	}
S
Skylot 已提交
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167

	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();
		}
	}

	public static void copyStream(InputStream input, OutputStream output) throws IOException {
		byte[] buffer = new byte[8 * 1024];
		while (true) {
			int count = input.read(buffer);
			if (count == -1) {
				break;
			}
			output.write(buffer, 0, count);
		}
	}
S
Skylot 已提交
168 169 170 171 172 173 174 175 176 177

	private static Path saveInputStreamToFile(InputStream in, String suffix) throws IOException {
		Path tempJar = Files.createTempFile("jadx-temp-", suffix);
		try (OutputStream out = Files.newOutputStream(tempJar)) {
			copyStream(in, out);
		} catch (Exception e) {
			throw new IOException("Failed to save temp file", e);
		}
		return tempJar;
	}
178
}