diff --git a/build.gradle b/build.gradle index 55a51f9d..2e1e89f4 100644 --- a/build.gradle +++ b/build.gradle @@ -65,6 +65,12 @@ dependencies { implementation "net.fabricmc:cfr:${fabric_cfr_version}" implementation "org.vineflower:vineflower:${vineflower_version}" implementation "org.bitbucket.mstrobel:procyon-compilertools:${procyon_version}" + implementation ("io.github.skylot:jadx-core:${jadx_version}") { + exclude group: 'com.android.tools.build', module: 'aapt2-proto' + } + implementation ("io.github.skylot:jadx-java-input:${jadx_version}") { + exclude group: 'io.github.skylot', module: 'raung-disasm' + } runtimeOnly "org.tinylog:tinylog-impl:${tinylog_version}" runtimeOnly "org.tinylog:slf4j-tinylog:${tinylog_version}" @@ -97,6 +103,11 @@ extraJavaModuleInfo { // Procyon automaticModule("org.bitbucket.mstrobel:procyon-compilertools", "procyon.compilertools") + + // JADX + automaticModule("io.github.skylot:jadx-core", "jadx.core") + automaticModule("io.github.skylot:jadx-plugins-api", "jadx.plugins.api") + automaticModule("io.github.skylot:jadx-java-input", "jadx.plugins.java_input") } application { diff --git a/gradle.properties b/gradle.properties index 1bbdf7a5..457ad746 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,6 +14,7 @@ asm_version = 9.6 fabric_cfr_version = 0.2.1 vineflower_version = 1.9.3 procyon_version = 0.6.0 +jadx_version = 1.4.7 mappingio_version = 0.5.0 javaparser_version = 3.25.6 javafx_version = 21.0.1 diff --git a/src/main/java/matcher/Util.java b/src/main/java/matcher/Util.java index 6670b9bf..33e1fb19 100644 --- a/src/main/java/matcher/Util.java +++ b/src/main/java/matcher/Util.java @@ -2,6 +2,8 @@ import java.io.Closeable; import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; import java.io.UncheckedIOException; import java.net.URI; import java.net.URISyntaxException; @@ -48,6 +50,15 @@ public static Set copySet(Set set) { } } + public static String getStackTrace(Throwable t) { + if (t == null) return null; + + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + t.printStackTrace(pw); + return sw.toString(); + } + public static FileSystem iterateJar(Path archive, boolean autoClose, Consumer handler) { boolean existing = false; FileSystem fs = null; diff --git a/src/main/java/matcher/srcprocess/BuiltinDecompiler.java b/src/main/java/matcher/srcprocess/BuiltinDecompiler.java index 5ccffd91..ff5c58d9 100644 --- a/src/main/java/matcher/srcprocess/BuiltinDecompiler.java +++ b/src/main/java/matcher/srcprocess/BuiltinDecompiler.java @@ -5,6 +5,7 @@ public enum BuiltinDecompiler { CFR("CFR", Cfr::new), VINEFLOWER("Vineflower", Vineflower::new), + JADX("JADX", Jadx::new), PROCYON("Procyon", Procyon::new); BuiltinDecompiler(String name, Supplier supplier) { diff --git a/src/main/java/matcher/srcprocess/Jadx.java b/src/main/java/matcher/srcprocess/Jadx.java new file mode 100644 index 00000000..7ccc9aa9 --- /dev/null +++ b/src/main/java/matcher/srcprocess/Jadx.java @@ -0,0 +1,75 @@ +package matcher.srcprocess; + +import java.io.IOException; +import java.util.function.Consumer; + +import jadx.api.CommentsLevel; +import jadx.api.JadxArgs; +import jadx.api.JadxDecompiler; +import jadx.api.impl.NoOpCodeCache; +import jadx.api.plugins.input.data.IClassData; +import jadx.api.plugins.input.data.ILoadResult; +import jadx.api.plugins.input.data.IResourceData; +import jadx.plugins.input.java.JavaClassReader; +import jadx.plugins.input.java.data.JavaClassData; + +import matcher.NameType; +import matcher.Util; +import matcher.type.ClassFeatureExtractor; +import matcher.type.ClassInstance; + +public class Jadx implements Decompiler { + @Override + public String decompile(ClassInstance cls, ClassFeatureExtractor env, NameType nameType) { + String errorMessage = null; + final String fullClassName = cls.getName(NameType.PLAIN, true); + + try (JadxDecompiler jadx = new JadxDecompiler(jadxArgs)) { + jadx.addCustomLoad(new ILoadResult() { + @Override + public void close() throws IOException { } + + @Override + public void visitClasses(Consumer consumer) { + consumer.accept(new JavaClassData(new JavaClassReader(0, + fullClassName + ".class", cls.serialize(nameType)))); + } + + @Override + public void visitResources(Consumer consumer) { } + + @Override + public boolean isEmpty() { + return false; + } + }); + jadx.load(); + + assert jadx.getClassesWithInners().size() == 1; + return jadx.getClassesWithInners().get(0).getCode(); + } catch (Exception e) { + errorMessage = Util.getStackTrace(e); + } + + throw new RuntimeException(errorMessage != null ? errorMessage : "JADX couldn't find the requested class"); + } + + private static final JadxArgs jadxArgs; + + static { + jadxArgs = new JadxArgs() { + @Override + public void close() { + return; + } + }; + jadxArgs.setCodeCache(NoOpCodeCache.INSTANCE); + jadxArgs.setShowInconsistentCode(true); + jadxArgs.setInlineAnonymousClasses(false); + jadxArgs.setInlineMethods(false); + jadxArgs.setSkipResources(true); + jadxArgs.setRenameValid(false); + jadxArgs.setRespectBytecodeAccModifiers(true); + jadxArgs.setCommentsLevel(CommentsLevel.INFO); + } +} diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index c5f2b99c..3ac85dbe 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -26,6 +26,9 @@ requires org.objectweb.asm.tree.analysis; requires org.objectweb.asm.util; requires procyon.compilertools; + requires jadx.core; + requires jadx.plugins.api; + requires jadx.plugins.java_input; requires transitive net.fabricmc.mappingio; uses matcher.Plugin; diff --git a/src/main/resources/tinylog-dev.properties b/src/main/resources/tinylog-dev.properties index 5688dfda..b7f71f24 100644 --- a/src/main/resources/tinylog-dev.properties +++ b/src/main/resources/tinylog-dev.properties @@ -1,3 +1,5 @@ writer = console writer.format = {date: HH:mm:ss.SSS} [{thread}/{level}]: {message} writer.level = debug + +level@jadx = warn diff --git a/src/main/resources/tinylog.properties b/src/main/resources/tinylog.properties index d90124ce..ddbcb23d 100644 --- a/src/main/resources/tinylog.properties +++ b/src/main/resources/tinylog.properties @@ -1,3 +1,5 @@ writer = console writer.format = {date: HH:mm:ss} [{level}]: {message} writer.level = info + +level@jadx = error