Декомпилирование .class
файлов внутри jar
файлов для сравнения этих самых jar
файлов. Использует cfr для декомпилирования.
Build
docker build -t decompiler .
Usage
docker run --rm -v /path/to/jars:/jars decompiler /jars/SomeJar.jar
Dockerfile
####
# Build image
####
FROM openjdk:21 AS build
LABEL maintainer=avvero
RUN microdnf install findutils
COPY gradlew /app/
COPY gradle /app/gradle
WORKDIR /app
RUN ./gradlew --version
WORKDIR /app
COPY . .
RUN ./gradlew build --no-daemon
####
# Runtime image
####
FROM openjdk:21
COPY --from=build /app/build/classes/java/main/Decompiler.class /app/Decompiler.class
COPY --from=build /app/cfr.jar /app/cfr.jar
WORKDIR /app
ENTRYPOINT ["java", "Decompiler"]
Code
import java.io.*;
import java.nio.file.*;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class Decompiler {
private static final Set<String> IGNORED_FILES = new HashSet<>(Arrays.asList(
"META-INF/MANIFEST.MF"
));
public static void main(String[] args) throws IOException {
if (args.length == 0) {
System.out.println("Usage: java Decompiler <path-to-jar1> <path-to-jar2> ...");
System.exit(1);
}
for (String jarFilePath : args) {
File jarFile = new File(jarFilePath);
if (!jarFile.exists() || !jarFile.isFile()) {
System.err.println("File not found or not a file: " + jarFilePath);
continue;
}
String baseName = jarFile.getName().substring(0, jarFile.getName().lastIndexOf('.'));
Path outputDir = jarFile.getParentFile().toPath().resolve(baseName + "_decompiled");
Files.createDirectories(outputDir);
try (JarFile jar = new JarFile(jarFile)) {
decompileJar(jar, outputDir);
} catch (IOException e) {
System.err.println("Failed to decompile JAR: " + jarFilePath);
e.printStackTrace();
}
}
}
private static void decompileJar(JarFile jarFile, Path outputDir) throws IOException {
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (IGNORED_FILES.contains(entry.getName())) {
continue;
}
File entryFile = new File(outputDir.toFile(), entry.getName());
if (entry.isDirectory()) {
entryFile.mkdirs();
} else if (entry.getName().endsWith(".class")) {
entryFile.getParentFile().mkdirs();
Files.copy(jarFile.getInputStream(entry), entryFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
decompileClassFile(entryFile.toPath());
Files.delete(entryFile.toPath());
} else {
entryFile.getParentFile().mkdirs();
Files.copy(jarFile.getInputStream(entry), entryFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
}
}
}
private static void decompileClassFile(Path classFile) throws IOException {
Path outputDir = classFile.getParent();
ProcessBuilder processBuilder = new ProcessBuilder("java", "-jar", "cfr.jar", classFile.toString(), "--outputdir", outputDir.toString());
processBuilder.redirectErrorStream(true);
Process process = processBuilder.start();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
process.waitFor();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IOException("Decompilation interrupted", e);
}
}
}