From 8ab40bef910d403e3b0bcdbfa4b7e7fb802b8219 Mon Sep 17 00:00:00 2001 From: yrslv Date: Sun, 2 Feb 2020 01:51:55 +0300 Subject: [PATCH] Add classname parsing for Kotlin metadata --- .../src/main/java/jadx/cli/JadxCLIArgs.java | 8 +++ .../src/main/java/jadx/api/JadxArgs.java | 10 ++++ .../java/jadx/core/deobf/Deobfuscator.java | 49 +++++++++++++++++-- .../java/jadx/gui/settings/JadxSettings.java | 5 ++ .../jadx/gui/settings/JadxSettingsWindow.java | 11 ++++- .../resources/i18n/Messages_de_DE.properties | 1 + .../resources/i18n/Messages_en_US.properties | 1 + .../resources/i18n/Messages_es_ES.properties | 1 + .../resources/i18n/Messages_zh_CN.properties | 1 + 9 files changed, 83 insertions(+), 4 deletions(-) diff --git a/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java b/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java index 56ecf59729c..31a2aea7cac 100644 --- a/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java +++ b/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java @@ -85,6 +85,9 @@ public class JadxCLIArgs { @Parameter(names = { "--deobf-use-sourcename" }, description = "use source file name as class name alias") protected boolean deobfuscationUseSourceNameAsAlias = false; + @Parameter(names = { "--deobf-parse-kotlin-metadata" }, description = "parse kotlin metadata to class and package names") + protected boolean deobfuscationParseKotlinMetadata = false; + @Parameter( names = { "--rename-flags" }, description = "what to rename, comma-separated," @@ -194,6 +197,7 @@ public JadxArgs toJadxArgs() { args.setDeobfuscationMinLength(deobfuscationMinLength); args.setDeobfuscationMaxLength(deobfuscationMaxLength); args.setUseSourceNameAsClassAlias(deobfuscationUseSourceNameAsAlias); + args.setParseKotlinMetadata(deobfuscationParseKotlinMetadata); args.setEscapeUnicode(escapeUnicode); args.setRespectBytecodeAccModifiers(respectBytecodeAccessModifiers); args.setExportAsGradleProject(exportAsGradleProject); @@ -275,6 +279,10 @@ public boolean isDeobfuscationUseSourceNameAsAlias() { return deobfuscationUseSourceNameAsAlias; } + public boolean isDeobfuscationParseKotlinMetadata() { + return deobfuscationParseKotlinMetadata; + } + public boolean isEscapeUnicode() { return escapeUnicode; } diff --git a/jadx-core/src/main/java/jadx/api/JadxArgs.java b/jadx-core/src/main/java/jadx/api/JadxArgs.java index a7cc08e8ae8..79f566ed4b4 100644 --- a/jadx-core/src/main/java/jadx/api/JadxArgs.java +++ b/jadx-core/src/main/java/jadx/api/JadxArgs.java @@ -49,6 +49,7 @@ public class JadxArgs { private boolean deobfuscationOn = false; private boolean deobfuscationForceSave = false; private boolean useSourceNameAsClassAlias = false; + private boolean parseKotlinMetadata = false; private int deobfuscationMinLength = 0; private int deobfuscationMaxLength = Integer.MAX_VALUE; @@ -230,6 +231,14 @@ public void setUseSourceNameAsClassAlias(boolean useSourceNameAsClassAlias) { this.useSourceNameAsClassAlias = useSourceNameAsClassAlias; } + public boolean isParseKotlinMetadata() { + return parseKotlinMetadata; + } + + public void setParseKotlinMetadata(boolean parseKotlinMetadata) { + this.parseKotlinMetadata = parseKotlinMetadata; + } + public int getDeobfuscationMinLength() { return deobfuscationMinLength; } @@ -355,6 +364,7 @@ public String toString() { + ", deobfuscationOn=" + deobfuscationOn + ", deobfuscationForceSave=" + deobfuscationForceSave + ", useSourceNameAsClassAlias=" + useSourceNameAsClassAlias + + ", parseKotlinMetadata=" + parseKotlinMetadata + ", deobfuscationMinLength=" + deobfuscationMinLength + ", deobfuscationMaxLength=" + deobfuscationMaxLength + ", escapeUnicode=" + escapeUnicode diff --git a/jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java b/jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java index 56a355697b0..54fc89373ff 100644 --- a/jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java +++ b/jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java @@ -37,6 +37,9 @@ public class Deobfuscator { public static final String CLASS_NAME_SEPARATOR = "."; public static final String INNER_CLASS_SEPARATOR = "$"; + public static final String KOTLIN_METADATA_ANNOTATION = "kotlin.Metadata"; + public static final String KOTLIN_METADATA_D2_PARAMETER = "d2"; + public static final String KOTLIN_METADATA_CLASSNAME_REGEX = "(L.*;)"; private final JadxArgs args; @NotNull @@ -57,6 +60,7 @@ public class Deobfuscator { private final int maxLength; private final int minLength; private final boolean useSourceNameAsAlias; + private final boolean parseKotlinMetadata; private int pkgIndex = 0; private int clsIndex = 0; @@ -70,6 +74,7 @@ public Deobfuscator(JadxArgs args, @NotNull List dexNodes, Path deobfMa this.minLength = args.getDeobfuscationMinLength(); this.maxLength = args.getDeobfuscationMaxLength(); this.useSourceNameAsAlias = args.isUseSourceNameAsClassAlias(); + this.parseKotlinMetadata = args.isParseKotlinMetadata(); this.deobfPresets = new DeobfPresets(this, deobfMapFile); } @@ -392,6 +397,18 @@ public String getPkgAlias(ClassNode cls) { private String makeClsAlias(ClassNode cls) { ClassInfo classInfo = cls.getClassInfo(); + + String metadataClassName = ""; + String metadataPackageName = ""; + if (this.parseKotlinMetadata) { + String rawClassName = getRawClassNameFromMetadata(cls); + if (rawClassName != null) { + metadataClassName = rawClassName.substring(rawClassName.lastIndexOf(".") + 1, rawClassName.length() - 1); + if (rawClassName.lastIndexOf(".") != -1) { + metadataPackageName = rawClassName.substring(1, rawClassName.lastIndexOf(".")); + } + } + } String alias = null; if (this.useSourceNameAsAlias) { @@ -399,14 +416,40 @@ private String makeClsAlias(ClassNode cls) { } if (alias == null) { - String clsName = classInfo.getShortName(); - alias = String.format("C%04d%s", clsIndex++, prepareNamePart(clsName)); + if (metadataClassName.isEmpty()) { + String clsName = classInfo.getShortName(); + alias = String.format("C%04d%s", clsIndex++, prepareNamePart(clsName)); + } else { + alias = metadataClassName; + } + } + PackageNode pkg; + if (metadataPackageName.isEmpty()) { + pkg = getPackageNode(classInfo.getPackage(), true); + } else { + pkg = getPackageNode(metadataPackageName, true); } - PackageNode pkg = getPackageNode(classInfo.getPackage(), true); clsMap.put(classInfo, new DeobfClsInfo(this, cls, pkg, alias)); return alias; } + @Nullable + private String getRawClassNameFromMetadata(ClassNode cls) { + if (cls.getAnnotation(KOTLIN_METADATA_ANNOTATION) != null + && cls.getAnnotation(KOTLIN_METADATA_ANNOTATION).getValues().get(KOTLIN_METADATA_D2_PARAMETER) != null + && cls.getAnnotation(KOTLIN_METADATA_ANNOTATION).getValues().get(KOTLIN_METADATA_D2_PARAMETER) instanceof List) { + Object rawClassNameObject = + ((List) cls.getAnnotation(KOTLIN_METADATA_ANNOTATION).getValues().get(KOTLIN_METADATA_D2_PARAMETER)).get(0); + if (rawClassNameObject instanceof String) { + String rawClassName = ((String) rawClassNameObject).trim().replace("/", "."); + if (rawClassName.length() > 1 && rawClassName.matches(KOTLIN_METADATA_CLASSNAME_REGEX)) { + return rawClassName; + } + } + } + return null; + } + @Nullable private String getAliasFromSourceFile(ClassNode cls) { SourceFileAttr sourceFileAttr = cls.get(AType.SOURCE_FILE); 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 12ce00a69f7..81abde3d997 100644 --- a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettings.java +++ b/jadx-gui/src/main/java/jadx/gui/settings/JadxSettings.java @@ -279,6 +279,10 @@ public void setDeobfuscationUseSourceNameAsAlias(boolean deobfuscationUseSourceN this.deobfuscationUseSourceNameAsAlias = deobfuscationUseSourceNameAsAlias; } + public void setDeobfuscationParseKotlinMetadata(boolean deobfuscationParseKotlinMetadata) { + this.deobfuscationParseKotlinMetadata = deobfuscationParseKotlinMetadata; + } + public void updateRenameFlag(JadxArgs.RenameEnum flag, boolean enabled) { if (enabled) { renameFlags.add(flag); @@ -387,6 +391,7 @@ private void upgradeSettings(int fromVersion) { if (fromVersion == 0) { setDeobfuscationMinLength(3); setDeobfuscationUseSourceNameAsAlias(true); + setDeobfuscationParseKotlinMetadata(true); setDeobfuscationForceSave(true); setThreadsCount(1); setReplaceConsts(true); 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 140eedf28e9..3557c57d034 100644 --- a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsWindow.java +++ b/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsWindow.java @@ -167,15 +167,24 @@ private SettingsGroup makeDeobfuscationGroup() { needReload(); }); + JCheckBox deobfKotlinMetadata = new JCheckBox(); + deobfKotlinMetadata.setSelected(settings.isDeobfuscationParseKotlinMetadata()); + deobfKotlinMetadata.addItemListener(e -> { + settings.setDeobfuscationParseKotlinMetadata(e.getStateChange() == ItemEvent.SELECTED); + needReload(); + }); + SettingsGroup deobfGroup = new SettingsGroup(NLS.str("preferences.deobfuscation")); deobfGroup.addRow(NLS.str("preferences.deobfuscation_on"), deobfOn); deobfGroup.addRow(NLS.str("preferences.deobfuscation_force"), deobfForce); deobfGroup.addRow(NLS.str("preferences.deobfuscation_min_len"), minLenSpinner); deobfGroup.addRow(NLS.str("preferences.deobfuscation_max_len"), maxLenSpinner); deobfGroup.addRow(NLS.str("preferences.deobfuscation_source_alias"), deobfSourceAlias); + deobfGroup.addRow(NLS.str("preferences.deobfuscation_kotlin_metadata"), deobfKotlinMetadata); deobfGroup.end(); - Collection connectedComponents = Arrays.asList(deobfForce, minLenSpinner, maxLenSpinner, deobfSourceAlias); + Collection connectedComponents = + Arrays.asList(deobfForce, minLenSpinner, maxLenSpinner, deobfSourceAlias, deobfKotlinMetadata); deobfOn.addItemListener(e -> enableComponentList(connectedComponents, e.getStateChange() == ItemEvent.SELECTED)); enableComponentList(connectedComponents, settings.isDeobfuscationOn()); return deobfGroup; 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 70573379eb8..df4db452cd7 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_de_DE.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_de_DE.properties @@ -118,6 +118,7 @@ preferences.deobfuscation_force=Deobfuscationskartendatei umschreiben erzwingen preferences.deobfuscation_min_len=Minimale Namenlänge preferences.deobfuscation_max_len=Maximale Namenlänge preferences.deobfuscation_source_alias=Quelldateiname als Klassennamen-Alias verwenden +preferences.deobfuscation_kotlin_metadata=Analysieren Sie Kotlin-Metadaten nach Klassen- und Paketnamen preferences.save=Speichern preferences.cancel=Abbrechen preferences.reset=Zurücksetzen 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 4f08db2c59a..636d06faa67 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_en_US.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_en_US.properties @@ -118,6 +118,7 @@ preferences.deobfuscation_force=Force rewrite deobfuscation map file preferences.deobfuscation_min_len=Minimum name length preferences.deobfuscation_max_len=Maximum name length preferences.deobfuscation_source_alias=Use source file name as class name alias +preferences.deobfuscation_kotlin_metadata=Parse Kotlin metadata for class and package names preferences.save=Save preferences.cancel=Cancel preferences.reset=Reset 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 07e73eea9cc..727e2cbac9a 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_es_ES.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_es_ES.properties @@ -118,6 +118,7 @@ preferences.deobfuscation_force=Forzar reescritura del fichero de ofuscación preferences.deobfuscation_min_len=Longitud mínima del nombre preferences.deobfuscation_max_len=Longitud máxima del nombre preferences.deobfuscation_source_alias=Usar el nombre del source como alias para la clase +preferences.deobfuscation_kotlin_metadata=Parse Kotlin metadatos para nombres de clase y paquete preferences.save=Guardar preferences.cancel=Cancelar preferences.reset=Reestablecer 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 2e12bc098bd..9e23bddc8b6 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties @@ -118,6 +118,7 @@ preferences.deobfuscation_force=强制覆盖反混淆映射文件 preferences.deobfuscation_min_len=最小命名长度 preferences.deobfuscation_max_len=最大命名长度 preferences.deobfuscation_source_alias=使用资源名作为类的别名 +preferences.deobfuscation_kotlin_metadata=解析Kotlin元数据以获得类和包名 preferences.save=保存 preferences.cancel=取消 preferences.reset=重置