diff --git a/.editorconfig b/.editorconfig index c773dd22b..95bdd9524 100644 --- a/.editorconfig +++ b/.editorconfig @@ -7,7 +7,10 @@ insert_final_newline = true indent_style = tab tab_width = 4 -continuation_indent_size = 8 #IntelliJ Idea specific workaround + +# IntelliJ Idea specific workarounds +continuation_indent_size = 8 +ij_java_class_count_to_use_import_on_demand = 99 charset = utf-8 trim_trailing_whitespace = true diff --git a/README.md b/README.md index 0c05fafc8..6c3ada36c 100644 --- a/README.md +++ b/README.md @@ -98,19 +98,27 @@ options: 'fallback' - raw instructions without modifications --show-bad-code - show inconsistent code (incorrectly decompiled) --no-imports - disable use of imports, always write entire package name - --no-debug-info - disable debug info + --no-debug-info - disable debug info parsing and processing --add-debug-lines - add comments with debug line numbers if available --no-inline-anonymous - disable anonymous classes inline --no-inline-methods - disable methods inline + --no-move-inner-classes - disable move inner classes into parent + --no-inline-kotlin-lambda - disable inline for Kotlin lambdas --no-finally - don't extract finally block --no-replace-consts - don't replace constant value with matching constant field --escape-unicode - escape non latin characters in strings (with \u) --respect-bytecode-access-modifiers - don't change original access modifiers + --mappings-path - deobfuscation mappings file or directory. Allowed formats: Tiny and Tiny v2 (both '.tiny'), Enigma (.mapping) or Enigma directory + --mappings-mode - set mode for handling the deobfuscation mapping file: + 'read' - just read, user can always save manually (default) + 'read-and-autosave-every-change' - read and autosave after every change + 'read-and-autosave-before-closing' - read and autosave before exiting the app or closing the project + 'ignore' - don't read or save (can be used to skip loading mapping files referenced in the project file) --deobf - activate deobfuscation --deobf-min - min length of name, renamed if shorter, default: 3 --deobf-max - max length of name, renamed if longer, default: 64 - --deobf-cfg-file - deobfuscation map file, default: same dir and name as input file with '.jobf' extension - --deobf-cfg-file-mode - set mode for handle deobfuscation map file: + --deobf-cfg-file - deobfuscation mappings file used for JADX auto-generated names (in the JOBF file format), default: same dir and name as input file with '.jobf' extension + --deobf-cfg-file-mode - set mode for handling the JADX auto-generated names' deobfuscation map file: 'read' - read if found, don't save (default) 'read-or-save' - read if found, save otherwise (don't overwrite) 'overwrite' - don't read, always save diff --git a/jadx-cli/src/main/java/jadx/cli/JCommanderWrapper.java b/jadx-cli/src/main/java/jadx/cli/JCommanderWrapper.java index f133e3cf0..41cb2f70e 100644 --- a/jadx-cli/src/main/java/jadx/cli/JCommanderWrapper.java +++ b/jadx-cli/src/main/java/jadx/cli/JCommanderWrapper.java @@ -178,6 +178,7 @@ public class JCommanderWrapper { // load and init all options plugins to print all options try (JadxDecompiler decompiler = new JadxDecompiler(new JadxArgs())) { JadxPluginManager pluginManager = decompiler.getPluginManager(); + pluginManager.load(); pluginManager.initAll(); for (PluginContext context : pluginManager.getAllPluginContexts()) { JadxPluginOptions options = context.getOptions(); diff --git a/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java b/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java index 61f57d35f..a6146ab2e 100644 --- a/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java +++ b/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java @@ -81,7 +81,7 @@ public class JadxCLIArgs { @Parameter(names = { "--no-imports" }, description = "disable use of imports, always write entire package name") protected boolean useImports = true; - @Parameter(names = { "--no-debug-info" }, description = "disable debug info") + @Parameter(names = { "--no-debug-info" }, description = "disable debug info parsing and processing") protected boolean debugInfo = true; @Parameter(names = { "--add-debug-lines" }, description = "add comments with debug line numbers if available") @@ -93,6 +93,9 @@ public class JadxCLIArgs { @Parameter(names = { "--no-inline-methods" }, description = "disable methods inline") protected boolean inlineMethods = true; + @Parameter(names = { "--no-move-inner-classes" }, description = "disable move inner classes into parent") + protected boolean moveInnerClasses = true; + @Parameter(names = { "--no-inline-kotlin-lambda" }, description = "disable inline for Kotlin lambdas") protected boolean allowInlineKotlinLambda = true; @@ -313,6 +316,7 @@ public class JadxCLIArgs { args.setInsertDebugLines(addDebugLines); args.setInlineAnonymousClasses(inlineAnonymousClasses); args.setInlineMethods(inlineMethods); + args.setMoveInnerClasses(moveInnerClasses); args.setAllowInlineKotlinLambda(allowInlineKotlinLambda); args.setExtractFinally(extractFinally); args.setRenameFlags(renameFlags); @@ -395,6 +399,10 @@ public class JadxCLIArgs { return inlineMethods; } + public boolean isMoveInnerClasses() { + return moveInnerClasses; + } + public boolean isAllowInlineKotlinLambda() { return allowInlineKotlinLambda; } diff --git a/jadx-core/src/main/java/jadx/api/JadxArgs.java b/jadx-core/src/main/java/jadx/api/JadxArgs.java index c3ac36a41..abeb8406f 100644 --- a/jadx-core/src/main/java/jadx/api/JadxArgs.java +++ b/jadx-core/src/main/java/jadx/api/JadxArgs.java @@ -69,6 +69,7 @@ public class JadxArgs implements Closeable { private boolean inlineAnonymousClasses = true; private boolean inlineMethods = true; private boolean allowInlineKotlinLambda = true; + private boolean moveInnerClasses = true; private boolean skipResources = false; private boolean skipSources = false; @@ -305,6 +306,14 @@ public class JadxArgs implements Closeable { this.allowInlineKotlinLambda = allowInlineKotlinLambda; } + public boolean isMoveInnerClasses() { + return moveInnerClasses; + } + + public void setMoveInnerClasses(boolean moveInnerClasses) { + this.moveInnerClasses = moveInnerClasses; + } + public boolean isExtractFinally() { return extractFinally; } @@ -628,7 +637,7 @@ public class JadxArgs implements Closeable { */ public String makeCodeArgsHash() { String argStr = "args:" + decompilationMode + useImports + showInconsistentCode - + inlineAnonymousClasses + inlineMethods + + inlineAnonymousClasses + inlineMethods + moveInnerClasses + allowInlineKotlinLambda + deobfuscationOn + deobfuscationMinLength + deobfuscationMaxLength + resourceNameSource + parseKotlinMetadata + useKotlinMethodsForVarNames diff --git a/jadx-core/src/main/java/jadx/core/dex/info/ClassInfo.java b/jadx-core/src/main/java/jadx/core/dex/info/ClassInfo.java index 2041381e0..96b5900c6 100644 --- a/jadx-core/src/main/java/jadx/core/dex/info/ClassInfo.java +++ b/jadx-core/src/main/java/jadx/core/dex/info/ClassInfo.java @@ -22,9 +22,9 @@ public final class ClassInfo implements Comparable { @Nullable private ClassAliasInfo alias; - private ClassInfo(RootNode root, ArgType type, boolean inner) { + private ClassInfo(RootNode root, ArgType type) { this.type = type; - splitAndApplyNames(root, type, inner); + splitAndApplyNames(root, type, root.getArgs().isMoveInnerClasses()); } public static ClassInfo fromType(RootNode root, ArgType type) { @@ -33,7 +33,7 @@ public final class ClassInfo implements Comparable { if (cls != null) { return cls; } - ClassInfo newClsInfo = new ClassInfo(root, clsType, true); + ClassInfo newClsInfo = new ClassInfo(root, clsType); return root.getInfoStorage().putCls(newClsInfo); } @@ -147,16 +147,21 @@ public final class ClassInfo implements Comparable { clsName = fullObjectName.substring(dot + 1); } - int sep = clsName.lastIndexOf('$'); - if (canBeInner && sep > 0 && sep != clsName.length() - 1) { - String parClsName = clsPkg + '.' + clsName.substring(0, sep); - if (clsPkg.isEmpty()) { - parClsName = clsName.substring(0, sep); + boolean innerCls = false; + if (canBeInner) { + int sep = clsName.lastIndexOf('$'); + if (sep > 0 && sep != clsName.length() - 1) { + String parClsName = clsPkg + '.' + clsName.substring(0, sep); + if (clsPkg.isEmpty()) { + parClsName = clsName.substring(0, sep); + } + pkg = null; + parentClass = fromName(root, parClsName); + clsName = clsName.substring(sep + 1); + innerCls = true; } - pkg = null; - parentClass = fromName(root, parClsName); - clsName = clsName.substring(sep + 1); - } else { + } + if (!innerCls) { pkg = clsPkg; parentClass = null; } diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java index f77580bfc..f66e9b454 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java @@ -75,7 +75,7 @@ public class ClassNode extends NotificationAttrNode // store smali private String smali; // store parent for inner classes or 'this' otherwise - private ClassNode parentClass; + private ClassNode parentClass = this; private volatile ProcessState state = ProcessState.NOT_LOADED; private LoadStage loadStage = LoadStage.NONE; @@ -225,7 +225,6 @@ public class ClassNode extends NotificationAttrNode this.methods = new ArrayList<>(); this.fields = new ArrayList<>(); this.accessFlags = new AccessInfo(accessFlags, AFType.CLASS); - this.parentClass = this; this.packageNode = PackageNode.getForClass(root, clsInfo.getPackage(), this); } @@ -555,6 +554,8 @@ public class ClassNode extends NotificationAttrNode parentClass = parent; return; } + // undo inner mark in class info + clsInfo.notInner(root); } parentClass = this; } @@ -667,8 +668,7 @@ public class ClassNode extends NotificationAttrNode /** * Get all inner and inlined classes recursively * - * @param resultClassesSet - * all identified inner and inlined classes are added to this set + * @param resultClassesSet all identified inner and inlined classes are added to this set */ public void getInnerAndInlinedClassesRecursive(Set resultClassesSet) { for (ClassNode innerCls : innerClasses) { diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java index 5d34a610f..c32600d31 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java @@ -142,9 +142,11 @@ public class RootNode { // sort classes by name, expect top classes before inner classes.sort(Comparator.comparing(ClassNode::getFullName)); - // move inner classes - initInnerClasses(); + if (args.isMoveInnerClasses()) { + // detect and move inner classes + initInnerClasses(); + } // sort packages Collections.sort(packages); } diff --git a/jadx-gui/src/main/java/jadx/gui/search/providers/ClassSearchProvider.java b/jadx-gui/src/main/java/jadx/gui/search/providers/ClassSearchProvider.java index 67d3755eb..269e28484 100644 --- a/jadx-gui/src/main/java/jadx/gui/search/providers/ClassSearchProvider.java +++ b/jadx-gui/src/main/java/jadx/gui/search/providers/ClassSearchProvider.java @@ -36,7 +36,8 @@ public final class ClassSearchProvider extends BaseSearchProvider { ClassInfo clsInfo = cls.getClassNode().getClassInfo(); return isMatch(clsInfo.getShortName()) || isMatch(clsInfo.getFullName()) - || isMatch(clsInfo.getAliasFullName()); + || isMatch(clsInfo.getAliasFullName()) + || isMatch(clsInfo.getRawName()); } @Override diff --git a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettings.java b/jadx-gui/src/main/java/jadx/gui/settings/JadxSettings.java index e682726ed..5deecbcc1 100644 --- a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettings.java +++ b/jadx-gui/src/main/java/jadx/gui/settings/JadxSettings.java @@ -1,10 +1,6 @@ package jadx.gui.settings; -import java.awt.Font; -import java.awt.GraphicsDevice; -import java.awt.GraphicsEnvironment; -import java.awt.Rectangle; -import java.awt.Window; +import java.awt.*; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; @@ -17,7 +13,7 @@ import java.util.Map; import java.util.Set; import java.util.function.Consumer; -import javax.swing.JFrame; +import javax.swing.*; import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; import org.jetbrains.annotations.Nullable; @@ -410,6 +406,10 @@ public class JadxSettings extends JadxCLIArgs { this.inlineMethods = inlineMethods; } + public void setMoveInnerClasses(boolean moveInnerClasses) { + this.moveInnerClasses = moveInnerClasses; + } + public void setAllowInlineKotlinLambda(boolean allowInlineKotlinLambda) { this.allowInlineKotlinLambda = allowInlineKotlinLambda; } diff --git a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsWindow.java b/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsWindow.java index 4216a9cc1..f62f4f54d 100644 --- a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsWindow.java +++ b/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsWindow.java @@ -559,6 +559,13 @@ public class JadxSettingsWindow extends JDialog { needReload(); }); + JCheckBox moveInnerClasses = new JCheckBox(); + moveInnerClasses.setSelected(settings.isMoveInnerClasses()); + moveInnerClasses.addItemListener(e -> { + settings.setMoveInnerClasses(e.getStateChange() == ItemEvent.SELECTED); + needReload(); + }); + JCheckBox extractFinally = new JCheckBox(); extractFinally.setSelected(settings.isExtractFinally()); extractFinally.addItemListener(e -> { @@ -602,8 +609,9 @@ public class JadxSettingsWindow extends JDialog { other.addRow(NLS.str("preferences.useImports"), useImports); other.addRow(NLS.str("preferences.useDebugInfo"), useDebugInfo); other.addRow(NLS.str("preferences.inlineAnonymous"), inlineAnonymous); - other.addRow(NLS.str("preferences.inlineMethods"), inlineMethods); + other.addRow(NLS.str("preferences.inlineMethods"), moveInnerClasses); other.addRow(NLS.str("preferences.inlineKotlinLambdas"), inlineKotlinLambdas); + other.addRow(NLS.str("preferences.moveInnerClasses"), moveInnerClasses); other.addRow(NLS.str("preferences.extractFinally"), extractFinally); other.addRow(NLS.str("preferences.fsCaseSensitive"), fsCaseSensitive); other.addRow(NLS.str("preferences.useDx"), useDx); diff --git a/jadx-gui/src/main/resources/i18n/Messages_de_DE.properties b/jadx-gui/src/main/resources/i18n/Messages_de_DE.properties index b0c304b74..3e31953c9 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_de_DE.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_de_DE.properties @@ -178,6 +178,7 @@ preferences.useDebugInfo=Debug-Infos verwenden preferences.inlineAnonymous=Anonyme Inline-Klassen preferences.inlineMethods=Inline-Methoden #preferences.inlineKotlinLambdas=Allow to inline Kotlin Lambdas +#preferences.moveInnerClasses=Move inner classes into parent #preferences.extractFinally=Extract finally block preferences.fsCaseSensitive=Dateisystem unterscheidet zwischen Groß/Kleinschreibung preferences.skipResourcesDecode=Keine Ressourcen dekodieren diff --git a/jadx-gui/src/main/resources/i18n/Messages_en_US.properties b/jadx-gui/src/main/resources/i18n/Messages_en_US.properties index e7440d2d7..bdfeb0996 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_en_US.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_en_US.properties @@ -178,6 +178,7 @@ preferences.useDebugInfo=Use debug info preferences.inlineAnonymous=Inline anonymous classes preferences.inlineMethods=Inline methods preferences.inlineKotlinLambdas=Allow to inline Kotlin Lambdas +preferences.moveInnerClasses=Move inner classes into parent preferences.extractFinally=Extract finally block preferences.fsCaseSensitive=File system is case-sensitive preferences.skipResourcesDecode=Don't decode resources diff --git a/jadx-gui/src/main/resources/i18n/Messages_es_ES.properties b/jadx-gui/src/main/resources/i18n/Messages_es_ES.properties index 2f9a88c74..463112315 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_es_ES.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_es_ES.properties @@ -178,6 +178,7 @@ preferences.replaceConsts=Reemplazar constantes #preferences.inlineAnonymous= #preferences.inlineMethods=Inline methods #preferences.inlineKotlinLambdas=Allow to inline Kotlin Lambdas +#preferences.moveInnerClasses=Move inner classes into parent #preferences.extractFinally=Extract finally block #preferences.fsCaseSensitive= preferences.skipResourcesDecode=No descodificar recursos diff --git a/jadx-gui/src/main/resources/i18n/Messages_ko_KR.properties b/jadx-gui/src/main/resources/i18n/Messages_ko_KR.properties index a93bf0c52..888b08731 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_ko_KR.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_ko_KR.properties @@ -178,6 +178,7 @@ preferences.useDebugInfo=디버그 정보 사용 preferences.inlineAnonymous=인라인 익명 클래스 preferences.inlineMethods=인라인 메서드 #preferences.inlineKotlinLambdas=Allow to inline Kotlin Lambdas +#preferences.moveInnerClasses=Move inner classes into parent preferences.extractFinally=finally 블록 추출 preferences.fsCaseSensitive=파일 시스템 대소문자 구별 preferences.skipResourcesDecode=리소스 디코딩 하지 않기 diff --git a/jadx-gui/src/main/resources/i18n/Messages_pt_BR.properties b/jadx-gui/src/main/resources/i18n/Messages_pt_BR.properties index d8cb21152..6f3334387 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_pt_BR.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_pt_BR.properties @@ -178,6 +178,7 @@ preferences.useDebugInfo=Utilizar informação de depuração preferences.inlineAnonymous=Classes anônimas de uma linha preferences.inlineMethods=Métodos de uma linha #preferences.inlineKotlinLambdas=Allow to inline Kotlin Lambdas +#preferences.moveInnerClasses=Move inner classes into parent preferences.extractFinally=Extrair blocos finally preferences.fsCaseSensitive=Sistema de arquivo diferencia maiúsculas de minúsculas preferences.skipResourcesDecode=Não decodificar recursos diff --git a/jadx-gui/src/main/resources/i18n/Messages_ru_RU.properties b/jadx-gui/src/main/resources/i18n/Messages_ru_RU.properties index 8d5ba241b..0781bf96a 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_ru_RU.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_ru_RU.properties @@ -178,6 +178,7 @@ preferences.useDebugInfo=Отладочная информация preferences.inlineAnonymous=Объединять анонимные классы preferences.inlineMethods=Объединять методы #preferences.inlineKotlinLambdas=Allow to inline Kotlin Lambdas +#preferences.moveInnerClasses=Move inner classes into parent preferences.extractFinally=Вычленять finally блоки preferences.fsCaseSensitive=Учитывать регистр в файловой системе preferences.skipResourcesDecode=Не декодировать ресурсы diff --git a/jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties b/jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties index 4c05549cf..81493875e 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties @@ -178,6 +178,7 @@ preferences.useDebugInfo=启用调试信息 preferences.inlineAnonymous=内联匿名类 preferences.inlineMethods=内联方法 preferences.inlineKotlinLambdas=允许内联Kotlin Lambda +#preferences.moveInnerClasses=Move inner classes into parent preferences.extractFinally=提取finally块 preferences.fsCaseSensitive=文件系统区分大小写 preferences.skipResourcesDecode=不反编译资源文件 diff --git a/jadx-gui/src/main/resources/i18n/Messages_zh_TW.properties b/jadx-gui/src/main/resources/i18n/Messages_zh_TW.properties index 1e755c819..f553c3b38 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_zh_TW.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_zh_TW.properties @@ -178,6 +178,7 @@ preferences.useDebugInfo=使用除錯資訊 preferences.inlineAnonymous=內嵌匿名類別 preferences.inlineMethods=內嵌方式 #preferences.inlineKotlinLambdas=Allow to inline Kotlin Lambdas +#preferences.moveInnerClasses=Move inner classes into parent preferences.extractFinally=擷取 finally 區塊 preferences.fsCaseSensitive=檔案系統區分大小寫 preferences.skipResourcesDecode=不要為資源解碼