From 16a1ab31ac226c66f8dab3dee63aa26e8dfa2b53 Mon Sep 17 00:00:00 2001 From: NebelNidas Date: Thu, 30 Mar 2023 14:03:07 +0200 Subject: [PATCH 01/11] Add JADX decompiler --- build.gradle | 8 ++ gradle.properties | 1 + .../matcher/srcprocess/BuiltinDecompiler.java | 1 + src/main/java/matcher/srcprocess/Jadx.java | 74 +++++++++++++++++++ .../java/matcher/srcprocess/TypeResolver.java | 12 ++- src/main/java/module-info.java | 2 + 6 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 src/main/java/matcher/srcprocess/Jadx.java diff --git a/build.gradle b/build.gradle index 55a51f9d..344063ab 100644 --- a/build.gradle +++ b/build.gradle @@ -65,6 +65,10 @@ 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}" runtimeOnly "org.tinylog:tinylog-impl:${tinylog_version}" runtimeOnly "org.tinylog:slf4j-tinylog:${tinylog_version}" @@ -97,6 +101,10 @@ extraJavaModuleInfo { // Procyon automaticModule("org.bitbucket.mstrobel:procyon-compilertools", "procyon.compilertools") + + // JADX + automaticModule("io.github.skylot:jadx-core", "jadx.core") + automaticModule("io.github.skylot:jadx-java-input", "jadx.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/srcprocess/BuiltinDecompiler.java b/src/main/java/matcher/srcprocess/BuiltinDecompiler.java index 5ccffd91..52912b0c 100644 --- a/src/main/java/matcher/srcprocess/BuiltinDecompiler.java +++ b/src/main/java/matcher/srcprocess/BuiltinDecompiler.java @@ -4,6 +4,7 @@ public enum BuiltinDecompiler { CFR("CFR", Cfr::new), + JADX("JADX", Jadx::new), VINEFLOWER("Vineflower", Vineflower::new), PROCYON("Procyon", Procyon::new); diff --git a/src/main/java/matcher/srcprocess/Jadx.java b/src/main/java/matcher/srcprocess/Jadx.java new file mode 100644 index 00000000..b7393046 --- /dev/null +++ b/src/main/java/matcher/srcprocess/Jadx.java @@ -0,0 +1,74 @@ +package matcher.srcprocess; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import jadx.api.CommentsLevel; +import jadx.api.JadxArgs; +import jadx.api.JadxDecompiler; +import jadx.api.JavaClass; +import jadx.api.impl.NoOpCodeCache; +import jadx.core.Consts; +import jadx.core.utils.Utils; + +import matcher.NameType; +import matcher.type.ClassFeatureExtractor; +import matcher.type.ClassInstance; +import matcher.type.InputFile; + +public class Jadx implements Decompiler { + @Override + public synchronized String decompile(ClassInstance cls, ClassFeatureExtractor env, NameType nameType) { + String errorMessage = null; + String fullClassName = cls.getName(NameType.PLAIN, true); + + if (fullClassName.contains("$")) { + errorMessage = "JADX doesn't support decompiling inner classes!"; + } else { + JadxArgs jadxArgs = new JadxArgs(); + jadxArgs.setInputFiles(toActualFiles(env.getInputFiles())); + jadxArgs.setCodeCache(new NoOpCodeCache()); + jadxArgs.setShowInconsistentCode(true); + jadxArgs.setInlineAnonymousClasses(false); + jadxArgs.setInlineMethods(false); + jadxArgs.setSkipResources(true); + jadxArgs.setRespectBytecodeAccModifiers(true); + jadxArgs.setCommentsLevel(CommentsLevel.INFO); + + try (JadxDecompiler jadx = new JadxDecompiler(jadxArgs)) { + jadx.load(); + String defpackage = Consts.DEFAULT_PACKAGE_NAME + "."; + String jadxFullClassName; + + for (JavaClass jadxCls : jadx.getClassesWithInners()) { + jadxFullClassName = jadxCls.getFullName().replace(defpackage, ""); + fullClassName = fullClassName + .replace('/', '.') + .replace('$', '.'); + + if (jadxFullClassName.equals(fullClassName)) { + return jadxCls.getCode(); + } + } + } catch (Exception e) { + errorMessage = Utils.getStackTrace(e); + } + } + + throw new RuntimeException(errorMessage != null ? errorMessage : "JADX couldn't find the requested class"); + } + + private List toActualFiles(Collection inputFiles) { + List files = new ArrayList<>(); + + for (InputFile inputFile : inputFiles) { + if (inputFile.path != null && inputFile.path.toFile().exists()) { + files.add(inputFile.path.toFile()); + } + } + + return files; + } +} diff --git a/src/main/java/matcher/srcprocess/TypeResolver.java b/src/main/java/matcher/srcprocess/TypeResolver.java index 1faeb31a..247245a8 100644 --- a/src/main/java/matcher/srcprocess/TypeResolver.java +++ b/src/main/java/matcher/srcprocess/TypeResolver.java @@ -61,11 +61,19 @@ public void setup(ClassInstance rootCls, NameType nameType, CompilationUnit cu) } } + // TODO: Clean up once you can disable JADX repackaging orphaned classes public ClassInstance getCls(Node node) { StringBuilder sb = new StringBuilder(); + String jadxDefaultPackage = jadx.core.Consts.DEFAULT_PACKAGE_NAME; - if (pkg != null) { - sb.append(pkg); + if (pkg != null && !pkg.equals(jadxDefaultPackage)) { + String pkgCleaned = pkg; + + if (pkg.startsWith(jadxDefaultPackage)) { + pkgCleaned = pkg.substring(jadxDefaultPackage.length() + 1); + } + + sb.append(pkgCleaned); sb.append('/'); } diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index c5f2b99c..a1064f3b 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -26,6 +26,8 @@ requires org.objectweb.asm.tree.analysis; requires org.objectweb.asm.util; requires procyon.compilertools; + requires jadx.core; + requires jadx.java_input; requires transitive net.fabricmc.mappingio; uses matcher.Plugin; From 8a42316a2581b7f212f377ea99c4deaf0c983c18 Mon Sep 17 00:00:00 2001 From: NebelNidas Date: Thu, 30 Mar 2023 21:59:29 +0200 Subject: [PATCH 02/11] Use memory cache for JADX Speeds up things significantly. And yes, the memory is never reclaimed, but once we move the cache to the disk instead, that won't be an issue anymore --- src/main/java/matcher/srcprocess/Jadx.java | 43 +++++++++++++++------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/src/main/java/matcher/srcprocess/Jadx.java b/src/main/java/matcher/srcprocess/Jadx.java index b7393046..64496224 100644 --- a/src/main/java/matcher/srcprocess/Jadx.java +++ b/src/main/java/matcher/srcprocess/Jadx.java @@ -4,12 +4,15 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import jadx.api.CommentsLevel; +import jadx.api.ICodeCache; import jadx.api.JadxArgs; import jadx.api.JadxDecompiler; import jadx.api.JavaClass; -import jadx.api.impl.NoOpCodeCache; +import jadx.api.impl.InMemoryCodeCache; import jadx.core.Consts; import jadx.core.utils.Utils; @@ -27,18 +30,29 @@ public synchronized String decompile(ClassInstance cls, ClassFeatureExtractor en if (fullClassName.contains("$")) { errorMessage = "JADX doesn't support decompiling inner classes!"; } else { - JadxArgs jadxArgs = new JadxArgs(); - jadxArgs.setInputFiles(toActualFiles(env.getInputFiles())); - jadxArgs.setCodeCache(new NoOpCodeCache()); - jadxArgs.setShowInconsistentCode(true); - jadxArgs.setInlineAnonymousClasses(false); - jadxArgs.setInlineMethods(false); - jadxArgs.setSkipResources(true); - jadxArgs.setRespectBytecodeAccModifiers(true); - jadxArgs.setCommentsLevel(CommentsLevel.INFO); - - try (JadxDecompiler jadx = new JadxDecompiler(jadxArgs)) { - jadx.load(); + try { + JadxDecompiler jadx = envDecompilerMap.get(env); + + if (jadx == null) { + ICodeCache cache = new InMemoryCodeCache(); + + JadxArgs jadxArgs = new JadxArgs(); + jadxArgs.setInputFiles(toActualFiles(env.getInputFiles())); + jadxArgs.setCodeCache(cache); + jadxArgs.setShowInconsistentCode(true); + jadxArgs.setInlineAnonymousClasses(false); + jadxArgs.setInlineMethods(false); + jadxArgs.setSkipResources(true); + jadxArgs.setRespectBytecodeAccModifiers(true); + jadxArgs.setCommentsLevel(CommentsLevel.INFO); + + jadx = new JadxDecompiler(jadxArgs); + envDecompilerMap.put(env, jadx); + envCacheMap.put(env, cache); + + jadx.load(); + } + String defpackage = Consts.DEFAULT_PACKAGE_NAME + "."; String jadxFullClassName; @@ -71,4 +85,7 @@ private List toActualFiles(Collection inputFiles) { return files; } + + private static final Map envDecompilerMap = new ConcurrentHashMap<>(); + private static final Map envCacheMap = new ConcurrentHashMap<>(); } From 53881b1c4a1db916612994d870abd15c2d21653b Mon Sep 17 00:00:00 2001 From: NebelNidas Date: Mon, 10 Apr 2023 18:26:16 +0200 Subject: [PATCH 03/11] Much more efficient JADX implementation Now only passes the requested class instead of all input files. --- src/main/java/matcher/srcprocess/Jadx.java | 104 ++++++++++----------- 1 file changed, 47 insertions(+), 57 deletions(-) diff --git a/src/main/java/matcher/srcprocess/Jadx.java b/src/main/java/matcher/srcprocess/Jadx.java index 64496224..35338c70 100644 --- a/src/main/java/matcher/srcprocess/Jadx.java +++ b/src/main/java/matcher/srcprocess/Jadx.java @@ -1,71 +1,62 @@ package matcher.srcprocess; -import java.io.File; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; import jadx.api.CommentsLevel; -import jadx.api.ICodeCache; import jadx.api.JadxArgs; import jadx.api.JadxDecompiler; -import jadx.api.JavaClass; -import jadx.api.impl.InMemoryCodeCache; -import jadx.core.Consts; +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.core.utils.Utils; +import jadx.plugins.input.java.JavaClassReader; +import jadx.plugins.input.java.data.JavaClassData; import matcher.NameType; import matcher.type.ClassFeatureExtractor; import matcher.type.ClassInstance; -import matcher.type.InputFile; public class Jadx implements Decompiler { @Override public synchronized String decompile(ClassInstance cls, ClassFeatureExtractor env, NameType nameType) { String errorMessage = null; - String fullClassName = cls.getName(NameType.PLAIN, true); + final String fullClassName = cls.getName(NameType.PLAIN, true); if (fullClassName.contains("$")) { errorMessage = "JADX doesn't support decompiling inner classes!"; } else { - try { - JadxDecompiler jadx = envDecompilerMap.get(env); - - if (jadx == null) { - ICodeCache cache = new InMemoryCodeCache(); - - JadxArgs jadxArgs = new JadxArgs(); - jadxArgs.setInputFiles(toActualFiles(env.getInputFiles())); - jadxArgs.setCodeCache(cache); - jadxArgs.setShowInconsistentCode(true); - jadxArgs.setInlineAnonymousClasses(false); - jadxArgs.setInlineMethods(false); - jadxArgs.setSkipResources(true); - jadxArgs.setRespectBytecodeAccModifiers(true); - jadxArgs.setCommentsLevel(CommentsLevel.INFO); - - jadx = new JadxDecompiler(jadxArgs); - envDecompilerMap.put(env, jadx); - envCacheMap.put(env, cache); - - jadx.load(); - } + try (JadxDecompiler jadx = new JadxDecompiler(jadxArgs)) { + jadx.addCustomLoad(new ILoadResult() { + @Override + public void close() throws IOException { + return; + } - String defpackage = Consts.DEFAULT_PACKAGE_NAME + "."; - String jadxFullClassName; + @Override + public void visitClasses(Consumer consumer) { + consumer.accept(new JavaClassData(new JavaClassReader(idGenerator.getAndIncrement(), + fullClassName + ".class", cls.serialize(nameType)))); + } - for (JavaClass jadxCls : jadx.getClassesWithInners()) { - jadxFullClassName = jadxCls.getFullName().replace(defpackage, ""); - fullClassName = fullClassName - .replace('/', '.') - .replace('$', '.'); + @Override + public void visitResources(Consumer consumer) { + return; + } - if (jadxFullClassName.equals(fullClassName)) { - return jadxCls.getCode(); + @Override + public boolean isEmpty() { + return false; } - } + }); + jadx.load(); + + assert jadx.getClassesWithInners().size() == 1; + return jadx.getClassesWithInners().get(0).getCode(); } catch (Exception e) { errorMessage = Utils.getStackTrace(e); } @@ -74,18 +65,17 @@ public synchronized String decompile(ClassInstance cls, ClassFeatureExtractor en throw new RuntimeException(errorMessage != null ? errorMessage : "JADX couldn't find the requested class"); } - private List toActualFiles(Collection inputFiles) { - List files = new ArrayList<>(); - - for (InputFile inputFile : inputFiles) { - if (inputFile.path != null && inputFile.path.toFile().exists()) { - files.add(inputFile.path.toFile()); - } - } - - return files; + private static final JadxArgs jadxArgs; + private static final AtomicInteger idGenerator = new AtomicInteger(); + + static { + jadxArgs = new JadxArgs(); + jadxArgs.setCodeCache(NoOpCodeCache.INSTANCE); + jadxArgs.setShowInconsistentCode(true); + jadxArgs.setInlineAnonymousClasses(false); + jadxArgs.setInlineMethods(false); + jadxArgs.setSkipResources(true); + jadxArgs.setRespectBytecodeAccModifiers(true); + jadxArgs.setCommentsLevel(CommentsLevel.INFO); } - - private static final Map envDecompilerMap = new ConcurrentHashMap<>(); - private static final Map envCacheMap = new ConcurrentHashMap<>(); } From f970a228c366a23583af8f7887cf29606c2a068d Mon Sep 17 00:00:00 2001 From: NebelNidas Date: Mon, 10 Apr 2023 18:50:17 +0200 Subject: [PATCH 04/11] Fix crash on second decompile --- src/main/java/matcher/srcprocess/Jadx.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/matcher/srcprocess/Jadx.java b/src/main/java/matcher/srcprocess/Jadx.java index 35338c70..1f77ec2d 100644 --- a/src/main/java/matcher/srcprocess/Jadx.java +++ b/src/main/java/matcher/srcprocess/Jadx.java @@ -69,7 +69,12 @@ public boolean isEmpty() { private static final AtomicInteger idGenerator = new AtomicInteger(); static { - jadxArgs = new JadxArgs(); + jadxArgs = new JadxArgs() { + @Override + public void close() { + return; + } + }; jadxArgs.setCodeCache(NoOpCodeCache.INSTANCE); jadxArgs.setShowInconsistentCode(true); jadxArgs.setInlineAnonymousClasses(false); From 7ebddf6d1d99e5ecc7e6719d144e98fa0eef8df7 Mon Sep 17 00:00:00 2001 From: NebelNidas Date: Mon, 10 Apr 2023 18:58:50 +0200 Subject: [PATCH 05/11] Un-`synchronize` JADX decompiling --- src/main/java/matcher/srcprocess/Jadx.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/matcher/srcprocess/Jadx.java b/src/main/java/matcher/srcprocess/Jadx.java index 1f77ec2d..9478fc11 100644 --- a/src/main/java/matcher/srcprocess/Jadx.java +++ b/src/main/java/matcher/srcprocess/Jadx.java @@ -23,7 +23,7 @@ public class Jadx implements Decompiler { @Override - public synchronized String decompile(ClassInstance cls, ClassFeatureExtractor env, NameType nameType) { + public String decompile(ClassInstance cls, ClassFeatureExtractor env, NameType nameType) { String errorMessage = null; final String fullClassName = cls.getName(NameType.PLAIN, true); From b4286f27551dcda4f7629c81f9d6aa42b3b757cd Mon Sep 17 00:00:00 2001 From: NebelNidas Date: Wed, 12 Apr 2023 13:07:47 +0200 Subject: [PATCH 06/11] New JADX implementation can decompile inner classes Also fix checkstyle issues --- src/main/java/matcher/srcprocess/Jadx.java | 56 ++++++++++------------ 1 file changed, 25 insertions(+), 31 deletions(-) diff --git a/src/main/java/matcher/srcprocess/Jadx.java b/src/main/java/matcher/srcprocess/Jadx.java index 9478fc11..a18bcf22 100644 --- a/src/main/java/matcher/srcprocess/Jadx.java +++ b/src/main/java/matcher/srcprocess/Jadx.java @@ -1,8 +1,6 @@ package matcher.srcprocess; -import java.io.ByteArrayInputStream; import java.io.IOException; -import java.io.InputStream; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; @@ -27,39 +25,35 @@ public String decompile(ClassInstance cls, ClassFeatureExtractor env, NameType n String errorMessage = null; final String fullClassName = cls.getName(NameType.PLAIN, true); - if (fullClassName.contains("$")) { - errorMessage = "JADX doesn't support decompiling inner classes!"; - } else { - try (JadxDecompiler jadx = new JadxDecompiler(jadxArgs)) { - jadx.addCustomLoad(new ILoadResult() { - @Override - public void close() throws IOException { - return; - } + try (JadxDecompiler jadx = new JadxDecompiler(jadxArgs)) { + jadx.addCustomLoad(new ILoadResult() { + @Override + public void close() throws IOException { + return; + } - @Override - public void visitClasses(Consumer consumer) { - consumer.accept(new JavaClassData(new JavaClassReader(idGenerator.getAndIncrement(), - fullClassName + ".class", cls.serialize(nameType)))); - } + @Override + public void visitClasses(Consumer consumer) { + consumer.accept(new JavaClassData(new JavaClassReader(idGenerator.getAndIncrement(), + fullClassName + ".class", cls.serialize(nameType)))); + } - @Override - public void visitResources(Consumer consumer) { - return; - } + @Override + public void visitResources(Consumer consumer) { + return; + } - @Override - public boolean isEmpty() { - return false; - } - }); - jadx.load(); + @Override + public boolean isEmpty() { + return false; + } + }); + jadx.load(); - assert jadx.getClassesWithInners().size() == 1; - return jadx.getClassesWithInners().get(0).getCode(); - } catch (Exception e) { - errorMessage = Utils.getStackTrace(e); - } + assert jadx.getClassesWithInners().size() == 1; + return jadx.getClassesWithInners().get(0).getCode(); + } catch (Exception e) { + errorMessage = Utils.getStackTrace(e); } throw new RuntimeException(errorMessage != null ? errorMessage : "JADX couldn't find the requested class"); From 3cbcbb5f2793a8d0ccdb6f1cbf68c52591277e95 Mon Sep 17 00:00:00 2001 From: NebelNidas Date: Sun, 10 Mar 2024 21:27:42 +0100 Subject: [PATCH 07/11] Fix JPMS issues --- build.gradle | 7 +++++-- src/main/java/module-info.java | 3 ++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 344063ab..2e1e89f4 100644 --- a/build.gradle +++ b/build.gradle @@ -68,7 +68,9 @@ dependencies { 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}" + 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}" @@ -104,7 +106,8 @@ extraJavaModuleInfo { // JADX automaticModule("io.github.skylot:jadx-core", "jadx.core") - automaticModule("io.github.skylot:jadx-java-input", "jadx.java_input") + 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/src/main/java/module-info.java b/src/main/java/module-info.java index a1064f3b..3ac85dbe 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -27,7 +27,8 @@ requires org.objectweb.asm.util; requires procyon.compilertools; requires jadx.core; - requires jadx.java_input; + requires jadx.plugins.api; + requires jadx.plugins.java_input; requires transitive net.fabricmc.mappingio; uses matcher.Plugin; From 9f72330a9ba8c3d1ae3b3fb607c24d6cfdb984e0 Mon Sep 17 00:00:00 2001 From: NebelNidas Date: Sun, 10 Mar 2024 21:46:16 +0100 Subject: [PATCH 08/11] Put JADX menu entry underneath Vineflower --- src/main/java/matcher/srcprocess/BuiltinDecompiler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/matcher/srcprocess/BuiltinDecompiler.java b/src/main/java/matcher/srcprocess/BuiltinDecompiler.java index 52912b0c..ff5c58d9 100644 --- a/src/main/java/matcher/srcprocess/BuiltinDecompiler.java +++ b/src/main/java/matcher/srcprocess/BuiltinDecompiler.java @@ -4,8 +4,8 @@ public enum BuiltinDecompiler { CFR("CFR", Cfr::new), - JADX("JADX", Jadx::new), VINEFLOWER("Vineflower", Vineflower::new), + JADX("JADX", Jadx::new), PROCYON("Procyon", Procyon::new); BuiltinDecompiler(String name, Supplier supplier) { From 30eaa0cbad13e9f8513616456bdce3d7f2dcab24 Mon Sep 17 00:00:00 2001 From: NebelNidas Date: Sun, 10 Mar 2024 22:24:35 +0100 Subject: [PATCH 09/11] Small clean-up --- src/main/java/matcher/Util.java | 11 +++++++++++ src/main/java/matcher/gui/tab/SourcecodeTab.java | 3 ++- src/main/java/matcher/srcprocess/Jadx.java | 16 +++++----------- .../java/matcher/srcprocess/SrcDecorator.java | 4 ++-- .../java/matcher/srcprocess/TypeResolver.java | 9 +++++++-- 5 files changed, 27 insertions(+), 16 deletions(-) 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/gui/tab/SourcecodeTab.java b/src/main/java/matcher/gui/tab/SourcecodeTab.java index 1c782df3..10713c9a 100644 --- a/src/main/java/matcher/gui/tab/SourcecodeTab.java +++ b/src/main/java/matcher/gui/tab/SourcecodeTab.java @@ -8,6 +8,7 @@ import matcher.gui.Gui; import matcher.gui.ISelectionProvider; import matcher.srcprocess.HtmlUtil; +import matcher.srcprocess.Jadx; import matcher.srcprocess.SrcDecorator; import matcher.srcprocess.SrcDecorator.SrcParseException; import matcher.type.ClassInstance; @@ -88,7 +89,7 @@ private void update() { NameType nameType = gui.getNameType().withUnmatchedTmp(unmatchedTmp); //Gui.runAsyncTask(() -> gui.getEnv().decompile(selectedClass, true)) - Gui.runAsyncTask(() -> SrcDecorator.decorate(gui.getEnv().decompile(gui.getDecompiler().get(), selectedClass, nameType), selectedClass, nameType)) + Gui.runAsyncTask(() -> SrcDecorator.decorate(gui.getEnv().decompile(gui.getDecompiler().get(), selectedClass, nameType), selectedClass, nameType, gui.getDecompiler().get() instanceof Jadx)) .whenComplete((res, exc) -> { if (cDecompId == decompId) { if (exc != null) { diff --git a/src/main/java/matcher/srcprocess/Jadx.java b/src/main/java/matcher/srcprocess/Jadx.java index a18bcf22..d0d36b57 100644 --- a/src/main/java/matcher/srcprocess/Jadx.java +++ b/src/main/java/matcher/srcprocess/Jadx.java @@ -1,7 +1,6 @@ package matcher.srcprocess; import java.io.IOException; -import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import jadx.api.CommentsLevel; @@ -11,11 +10,11 @@ import jadx.api.plugins.input.data.IClassData; import jadx.api.plugins.input.data.ILoadResult; import jadx.api.plugins.input.data.IResourceData; -import jadx.core.utils.Utils; 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; @@ -28,20 +27,16 @@ public String decompile(ClassInstance cls, ClassFeatureExtractor env, NameType n try (JadxDecompiler jadx = new JadxDecompiler(jadxArgs)) { jadx.addCustomLoad(new ILoadResult() { @Override - public void close() throws IOException { - return; - } + public void close() throws IOException { } @Override public void visitClasses(Consumer consumer) { - consumer.accept(new JavaClassData(new JavaClassReader(idGenerator.getAndIncrement(), + consumer.accept(new JavaClassData(new JavaClassReader(0, fullClassName + ".class", cls.serialize(nameType)))); } @Override - public void visitResources(Consumer consumer) { - return; - } + public void visitResources(Consumer consumer) { } @Override public boolean isEmpty() { @@ -53,14 +48,13 @@ public boolean isEmpty() { assert jadx.getClassesWithInners().size() == 1; return jadx.getClassesWithInners().get(0).getCode(); } catch (Exception e) { - errorMessage = Utils.getStackTrace(e); + errorMessage = Util.getStackTrace(e); } throw new RuntimeException(errorMessage != null ? errorMessage : "JADX couldn't find the requested class"); } private static final JadxArgs jadxArgs; - private static final AtomicInteger idGenerator = new AtomicInteger(); static { jadxArgs = new JadxArgs() { diff --git a/src/main/java/matcher/srcprocess/SrcDecorator.java b/src/main/java/matcher/srcprocess/SrcDecorator.java index f694af43..f648c699 100644 --- a/src/main/java/matcher/srcprocess/SrcDecorator.java +++ b/src/main/java/matcher/srcprocess/SrcDecorator.java @@ -37,7 +37,7 @@ import matcher.type.MethodVarInstance; public class SrcDecorator { - public static String decorate(String src, ClassInstance cls, NameType nameType) { + public static String decorate(String src, ClassInstance cls, NameType nameType, boolean decompiledByJadx) { String name = cls.getName(nameType); if (cls.getOuterClass() != null && name.contains("$")) { @@ -93,7 +93,7 @@ public static String decorate(String src, ClassInstance cls, NameType nameType) } } - TypeResolver resolver = new TypeResolver(); + TypeResolver resolver = new TypeResolver(decompiledByJadx); resolver.setup(cls, nameType, cu); cu.accept(remapVisitor, resolver); diff --git a/src/main/java/matcher/srcprocess/TypeResolver.java b/src/main/java/matcher/srcprocess/TypeResolver.java index 247245a8..3253387a 100644 --- a/src/main/java/matcher/srcprocess/TypeResolver.java +++ b/src/main/java/matcher/srcprocess/TypeResolver.java @@ -32,6 +32,10 @@ import matcher.type.MethodInstance; class TypeResolver { + TypeResolver(boolean decmpiledByJadx) { + this.decompiledByJadx = decmpiledByJadx; + } + public void setup(ClassInstance rootCls, NameType nameType, CompilationUnit cu) { this.rootCls = rootCls; this.env = rootCls.getEnv(); @@ -66,10 +70,10 @@ public ClassInstance getCls(Node node) { StringBuilder sb = new StringBuilder(); String jadxDefaultPackage = jadx.core.Consts.DEFAULT_PACKAGE_NAME; - if (pkg != null && !pkg.equals(jadxDefaultPackage)) { + if (pkg != null && (!decompiledByJadx || !pkg.equals(jadxDefaultPackage))) { String pkgCleaned = pkg; - if (pkg.startsWith(jadxDefaultPackage)) { + if (decompiledByJadx && pkg.startsWith(jadxDefaultPackage)) { pkgCleaned = pkg.substring(jadxDefaultPackage.length() + 1); } @@ -275,4 +279,5 @@ public String getName(Matchable e) { private String pkg; private final Map imports = new HashMap<>(); private final List wildcardImports = new ArrayList<>(); + private final boolean decompiledByJadx; } From 449789cb0871eb79e81916c2c82f1b07a4951be6 Mon Sep 17 00:00:00 2001 From: NebelNidas Date: Sun, 10 Mar 2024 22:30:05 +0100 Subject: [PATCH 10/11] Reduce JADX log spam --- src/main/resources/tinylog-dev.properties | 2 ++ src/main/resources/tinylog.properties | 2 ++ 2 files changed, 4 insertions(+) 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 From 85820c55a665cf68574eaa8553322e58cc5c3a98 Mon Sep 17 00:00:00 2001 From: NebelNidas Date: Sun, 10 Mar 2024 23:15:28 +0100 Subject: [PATCH 11/11] Remove JADX repackaging top-level classes workaround --- .../java/matcher/gui/tab/SourcecodeTab.java | 3 +-- src/main/java/matcher/srcprocess/Jadx.java | 1 + .../java/matcher/srcprocess/SrcDecorator.java | 4 ++-- .../java/matcher/srcprocess/TypeResolver.java | 17 ++--------------- 4 files changed, 6 insertions(+), 19 deletions(-) diff --git a/src/main/java/matcher/gui/tab/SourcecodeTab.java b/src/main/java/matcher/gui/tab/SourcecodeTab.java index 10713c9a..1c782df3 100644 --- a/src/main/java/matcher/gui/tab/SourcecodeTab.java +++ b/src/main/java/matcher/gui/tab/SourcecodeTab.java @@ -8,7 +8,6 @@ import matcher.gui.Gui; import matcher.gui.ISelectionProvider; import matcher.srcprocess.HtmlUtil; -import matcher.srcprocess.Jadx; import matcher.srcprocess.SrcDecorator; import matcher.srcprocess.SrcDecorator.SrcParseException; import matcher.type.ClassInstance; @@ -89,7 +88,7 @@ private void update() { NameType nameType = gui.getNameType().withUnmatchedTmp(unmatchedTmp); //Gui.runAsyncTask(() -> gui.getEnv().decompile(selectedClass, true)) - Gui.runAsyncTask(() -> SrcDecorator.decorate(gui.getEnv().decompile(gui.getDecompiler().get(), selectedClass, nameType), selectedClass, nameType, gui.getDecompiler().get() instanceof Jadx)) + Gui.runAsyncTask(() -> SrcDecorator.decorate(gui.getEnv().decompile(gui.getDecompiler().get(), selectedClass, nameType), selectedClass, nameType)) .whenComplete((res, exc) -> { if (cDecompId == decompId) { if (exc != null) { diff --git a/src/main/java/matcher/srcprocess/Jadx.java b/src/main/java/matcher/srcprocess/Jadx.java index d0d36b57..7ccc9aa9 100644 --- a/src/main/java/matcher/srcprocess/Jadx.java +++ b/src/main/java/matcher/srcprocess/Jadx.java @@ -68,6 +68,7 @@ public void close() { 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/matcher/srcprocess/SrcDecorator.java b/src/main/java/matcher/srcprocess/SrcDecorator.java index f648c699..f694af43 100644 --- a/src/main/java/matcher/srcprocess/SrcDecorator.java +++ b/src/main/java/matcher/srcprocess/SrcDecorator.java @@ -37,7 +37,7 @@ import matcher.type.MethodVarInstance; public class SrcDecorator { - public static String decorate(String src, ClassInstance cls, NameType nameType, boolean decompiledByJadx) { + public static String decorate(String src, ClassInstance cls, NameType nameType) { String name = cls.getName(nameType); if (cls.getOuterClass() != null && name.contains("$")) { @@ -93,7 +93,7 @@ public static String decorate(String src, ClassInstance cls, NameType nameType, } } - TypeResolver resolver = new TypeResolver(decompiledByJadx); + TypeResolver resolver = new TypeResolver(); resolver.setup(cls, nameType, cu); cu.accept(remapVisitor, resolver); diff --git a/src/main/java/matcher/srcprocess/TypeResolver.java b/src/main/java/matcher/srcprocess/TypeResolver.java index 3253387a..1faeb31a 100644 --- a/src/main/java/matcher/srcprocess/TypeResolver.java +++ b/src/main/java/matcher/srcprocess/TypeResolver.java @@ -32,10 +32,6 @@ import matcher.type.MethodInstance; class TypeResolver { - TypeResolver(boolean decmpiledByJadx) { - this.decompiledByJadx = decmpiledByJadx; - } - public void setup(ClassInstance rootCls, NameType nameType, CompilationUnit cu) { this.rootCls = rootCls; this.env = rootCls.getEnv(); @@ -65,19 +61,11 @@ public void setup(ClassInstance rootCls, NameType nameType, CompilationUnit cu) } } - // TODO: Clean up once you can disable JADX repackaging orphaned classes public ClassInstance getCls(Node node) { StringBuilder sb = new StringBuilder(); - String jadxDefaultPackage = jadx.core.Consts.DEFAULT_PACKAGE_NAME; - - if (pkg != null && (!decompiledByJadx || !pkg.equals(jadxDefaultPackage))) { - String pkgCleaned = pkg; - - if (decompiledByJadx && pkg.startsWith(jadxDefaultPackage)) { - pkgCleaned = pkg.substring(jadxDefaultPackage.length() + 1); - } - sb.append(pkgCleaned); + if (pkg != null) { + sb.append(pkg); sb.append('/'); } @@ -279,5 +267,4 @@ public String getName(Matchable e) { private String pkg; private final Map imports = new HashMap<>(); private final List wildcardImports = new ArrayList<>(); - private final boolean decompiledByJadx; }