Skip to content

Commit

Permalink
feat(deobf): add classname parsing for Kotlin metadata (PR #842, #758)
Browse files Browse the repository at this point in the history
  • Loading branch information
yaroslavyadrov authored Feb 2, 2020
1 parent 1ce8fa8 commit 492a3f6
Show file tree
Hide file tree
Showing 9 changed files with 83 additions and 4 deletions.
8 changes: 8 additions & 0 deletions jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java
Original file line number Diff line number Diff line change
Expand Up @@ -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,"
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -275,6 +279,10 @@ public boolean isDeobfuscationUseSourceNameAsAlias() {
return deobfuscationUseSourceNameAsAlias;
}

public boolean isDeobfuscationParseKotlinMetadata() {
return deobfuscationParseKotlinMetadata;
}

public boolean isEscapeUnicode() {
return escapeUnicode;
}
Expand Down
10 changes: 10 additions & 0 deletions jadx-core/src/main/java/jadx/api/JadxArgs.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -355,6 +364,7 @@ public String toString() {
+ ", deobfuscationOn=" + deobfuscationOn
+ ", deobfuscationForceSave=" + deobfuscationForceSave
+ ", useSourceNameAsClassAlias=" + useSourceNameAsClassAlias
+ ", parseKotlinMetadata=" + parseKotlinMetadata
+ ", deobfuscationMinLength=" + deobfuscationMinLength
+ ", deobfuscationMaxLength=" + deobfuscationMaxLength
+ ", escapeUnicode=" + escapeUnicode
Expand Down
49 changes: 46 additions & 3 deletions jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
Expand All @@ -70,6 +74,7 @@ public Deobfuscator(JadxArgs args, @NotNull List<DexNode> 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);
}
Expand Down Expand Up @@ -392,21 +397,59 @@ 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) {
alias = getAliasFromSourceFile(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);
Expand Down
5 changes: 5 additions & 0 deletions jadx-gui/src/main/java/jadx/gui/settings/JadxSettings.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -387,6 +391,7 @@ private void upgradeSettings(int fromVersion) {
if (fromVersion == 0) {
setDeobfuscationMinLength(3);
setDeobfuscationUseSourceNameAsAlias(true);
setDeobfuscationParseKotlinMetadata(true);
setDeobfuscationForceSave(true);
setThreadsCount(1);
setReplaceConsts(true);
Expand Down
11 changes: 10 additions & 1 deletion jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsWindow.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<JComponent> connectedComponents = Arrays.asList(deobfForce, minLenSpinner, maxLenSpinner, deobfSourceAlias);
Collection<JComponent> connectedComponents =
Arrays.asList(deobfForce, minLenSpinner, maxLenSpinner, deobfSourceAlias, deobfKotlinMetadata);
deobfOn.addItemListener(e -> enableComponentList(connectedComponents, e.getStateChange() == ItemEvent.SELECTED));
enableComponentList(connectedComponents, settings.isDeobfuscationOn());
return deobfGroup;
Expand Down
1 change: 1 addition & 0 deletions jadx-gui/src/main/resources/i18n/Messages_de_DE.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions jadx-gui/src/main/resources/i18n/Messages_en_US.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions jadx-gui/src/main/resources/i18n/Messages_es_ES.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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=重置
Expand Down

0 comments on commit 492a3f6

Please sign in to comment.