Commit f283ef43 authored by Skylot's avatar Skylot

fix: improve class renaming and add checks for class alias usage (#532)

parent 41abbb12
...@@ -36,6 +36,7 @@ import jadx.core.utils.CodegenUtils; ...@@ -36,6 +36,7 @@ import jadx.core.utils.CodegenUtils;
import jadx.core.utils.ErrorsCounter; import jadx.core.utils.ErrorsCounter;
import jadx.core.utils.Utils; import jadx.core.utils.Utils;
import jadx.core.utils.exceptions.CodegenException; import jadx.core.utils.exceptions.CodegenException;
import jadx.core.utils.exceptions.JadxRuntimeException;
public class ClassGen { public class ClassGen {
...@@ -141,7 +142,7 @@ public class ClassGen { ...@@ -141,7 +142,7 @@ public class ClassGen {
clsCode.add("class "); clsCode.add("class ");
} }
clsCode.attachDefinition(cls); clsCode.attachDefinition(cls);
clsCode.add(cls.getShortName()); clsCode.add(cls.getAlias().getShortName());
addGenericMap(clsCode, cls.getGenericMap(), true); addGenericMap(clsCode, cls.getGenericMap(), true);
clsCode.add(' '); clsCode.add(' ');
...@@ -199,9 +200,8 @@ public class ClassGen { ...@@ -199,9 +200,8 @@ public class ClassGen {
code.add(g.getObject()); code.add(g.getObject());
} else { } else {
useClass(code, g); useClass(code, g);
if (classDeclaration && !cls.getAlias().isInner()) { if (classDeclaration && !cls.getAlias().isInner()) {
addImport(ClassInfo.extCls(cls.root(), g)); addImport(ClassInfo.fromType(cls.root(), g));
} }
} }
if (it.hasNext()) { if (it.hasNext()) {
...@@ -462,7 +462,7 @@ public class ClassGen { ...@@ -462,7 +462,7 @@ public class ClassGen {
} }
public void useClass(CodeWriter code, ArgType type) { public void useClass(CodeWriter code, ArgType type) {
useClass(code, ClassInfo.extCls(cls.root(), type)); useClass(code, ClassInfo.fromType(cls.root(), type));
ArgType[] generics = type.getGenericTypes(); ArgType[] generics = type.getGenericTypes();
if (generics != null) { if (generics != null) {
code.add('<'); code.add('<');
...@@ -503,16 +503,16 @@ public class ClassGen { ...@@ -503,16 +503,16 @@ public class ClassGen {
} }
private void addClsName(CodeWriter code, ClassInfo classInfo) { private void addClsName(CodeWriter code, ClassInfo classInfo) {
String clsName = useClassInternal(cls.getAlias(), classInfo.getAlias()); String clsName = useClassInternal(cls.getClassInfo(), classInfo);
code.add(clsName); code.add(clsName);
} }
private String useClassInternal(ClassInfo useCls, ClassInfo extClsInfo) { private String useClassInternal(ClassInfo useCls, ClassInfo extClsInfo) {
String fullName = extClsInfo.getFullName(); String fullName = extClsInfo.getAlias().makeFullName();
if (fallback || !useImports) { if (fallback || !useImports) {
return fullName; return fullName;
} }
String shortName = extClsInfo.getShortName(); String shortName = extClsInfo.getAlias().getShortName();
if (extClsInfo.getPackage().equals("java.lang") && extClsInfo.getParentClass() == null) { if (extClsInfo.getPackage().equals("java.lang") && extClsInfo.getParentClass() == null) {
return shortName; return shortName;
} }
...@@ -538,14 +538,14 @@ public class ClassGen { ...@@ -538,14 +538,14 @@ public class ClassGen {
if (extClsInfo.isDefaultPackage()) { if (extClsInfo.isDefaultPackage()) {
return shortName; return shortName;
} }
if (extClsInfo.getPackage().equals(useCls.getPackage())) { if (extClsInfo.getAlias().getPackage().equals(useCls.getAlias().getPackage())) {
fullName = extClsInfo.getNameWithoutPackage(); fullName = extClsInfo.getAlias().getNameWithoutPackage();
} }
for (ClassInfo importCls : getImports()) { for (ClassInfo importCls : getImports()) {
if (!importCls.equals(extClsInfo) if (!importCls.equals(extClsInfo)
&& importCls.getShortName().equals(shortName)) { && importCls.getAlias().getShortName().equals(shortName)) {
if (extClsInfo.isInner()) { if (extClsInfo.isInner()) {
String parent = useClassInternal(useCls, extClsInfo.getParentClass().getAlias()); String parent = useClassInternal(useCls, extClsInfo.getParentClass());
return parent + '.' + shortName; return parent + '.' + shortName;
} else { } else {
return fullName; return fullName;
...@@ -558,8 +558,11 @@ public class ClassGen { ...@@ -558,8 +558,11 @@ public class ClassGen {
private void addImport(ClassInfo classInfo) { private void addImport(ClassInfo classInfo) {
if (parentGen != null) { if (parentGen != null) {
parentGen.addImport(classInfo.getAlias()); parentGen.addImport(classInfo);
} else { } else {
if (classInfo.isAlias()) {
throw new JadxRuntimeException("Don't add aliases class info to import list: " + classInfo);
}
imports.add(classInfo); imports.add(classInfo);
} }
} }
...@@ -594,15 +597,15 @@ public class ClassGen { ...@@ -594,15 +597,15 @@ public class ClassGen {
if (useCls == null) { if (useCls == null) {
return false; return false;
} }
String shortName = searchCls.getShortName(); String shortName = searchCls.getAlias().getShortName();
if (useCls.getShortName().equals(shortName)) { if (useCls.getAlias().getShortName().equals(shortName)) {
return true; return true;
} }
ClassNode classNode = dex.resolveClass(useCls); ClassNode classNode = dex.resolveClass(useCls);
if (classNode != null) { if (classNode != null) {
for (ClassNode inner : classNode.getInnerClasses()) { for (ClassNode inner : classNode.getInnerClasses()) {
if (inner.getShortName().equals(shortName) if (inner.getAlias().getShortName().equals(shortName)
&& !inner.getAlias().equals(searchCls)) { && !inner.getAlias().equals(searchCls.getAlias())) {
return true; return true;
} }
} }
......
...@@ -13,7 +13,6 @@ import jadx.core.dex.attributes.AType; ...@@ -13,7 +13,6 @@ import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.annotations.MethodParameters; import jadx.core.dex.attributes.annotations.MethodParameters;
import jadx.core.dex.attributes.nodes.JumpInfo; import jadx.core.dex.attributes.nodes.JumpInfo;
import jadx.core.dex.info.AccessInfo; import jadx.core.dex.info.AccessInfo;
import jadx.core.dex.info.ClassInfo;
import jadx.core.dex.instructions.IfNode; import jadx.core.dex.instructions.IfNode;
import jadx.core.dex.instructions.InsnType; import jadx.core.dex.instructions.InsnType;
import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.instructions.args.ArgType;
...@@ -180,10 +179,8 @@ public class MethodGen { ...@@ -180,10 +179,8 @@ public class MethodGen {
addFallbackMethodCode(code); addFallbackMethodCode(code);
code.startLine("*/"); code.startLine("*/");
ClassInfo clsAlias = mth.getParentClass().getAlias();
code.startLine("throw new UnsupportedOperationException(\"Method not decompiled: ") code.startLine("throw new UnsupportedOperationException(\"Method not decompiled: ")
.add(clsAlias.makeFullClsName(clsAlias.getShortName(), true)) .add(mth.getParentClass().getAlias().makeFullName())
.add('.') .add('.')
.add(mth.getAlias()) .add(mth.getAlias())
.add('(') .add('(')
......
...@@ -177,12 +177,15 @@ public class NameGen { ...@@ -177,12 +177,15 @@ public class NameGen {
} }
private String makeNameForObject(ArgType type) { private String makeNameForObject(ArgType type) {
if (type.isGenericType()) {
return StringUtils.escape(type.getObject().toLowerCase());
}
if (type.isObject()) { if (type.isObject()) {
String alias = getAliasForObject(type.getObject()); String alias = getAliasForObject(type.getObject());
if (alias != null) { if (alias != null) {
return alias; return alias;
} }
ClassInfo extClsInfo = ClassInfo.extCls(mth.root(), type); ClassInfo extClsInfo = ClassInfo.fromType(mth.root(), type);
String shortName = extClsInfo.getShortName(); String shortName = extClsInfo.getShortName();
String vName = fromName(shortName); String vName = fromName(shortName);
if (vName != null) { if (vName != null) {
......
...@@ -232,7 +232,7 @@ public class Deobfuscator { ...@@ -232,7 +232,7 @@ public class Deobfuscator {
ClassInfo clsInfo = cls.getClassInfo(); ClassInfo clsInfo = cls.getClassInfo();
String fullName = getClassFullName(clsInfo); String fullName = getClassFullName(clsInfo);
if (!fullName.equals(clsInfo.getFullName())) { if (!fullName.equals(clsInfo.getFullName())) {
clsInfo.rename(cls.dex().root(), fullName); clsInfo.rename(cls.root(), fullName);
} }
for (FieldNode field : cls.getFields()) { for (FieldNode field : cls.getFields()) {
if (field.contains(AFlag.DONT_RENAME)) { if (field.contains(AFlag.DONT_RENAME)) {
......
package jadx.core.dex.info; package jadx.core.dex.info;
import java.io.File; import java.io.File;
import java.util.Objects;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.nodes.DexNode; import jadx.core.dex.nodes.DexNode;
...@@ -20,30 +22,53 @@ public final class ClassInfo implements Comparable<ClassInfo> { ...@@ -20,30 +22,53 @@ public final class ClassInfo implements Comparable<ClassInfo> {
// class info after rename (deobfuscation) // class info after rename (deobfuscation)
private ClassInfo alias; private ClassInfo alias;
private ClassInfo(ArgType type) {
this.type = checkClassType(type);
this.alias = this;
}
private ClassInfo(RootNode root, ArgType type) { private ClassInfo(RootNode root, ArgType type) {
this(root, type, true); this(root, type, true);
} }
private ClassInfo(RootNode root, ArgType type, boolean inner) { private ClassInfo(RootNode root, ArgType type, boolean inner) {
if (!type.isObject() || type.isGeneric()) { this(type);
throw new JadxRuntimeException("Not class type: " + type); splitAndApplyNames(root, type, inner);
} }
this.type = type;
this.alias = this;
splitNames(root, inner); private ClassInfo(ArgType type, String pkg, String name, @Nullable ClassInfo parentClass) {
this(type);
this.pkg = pkg;
this.name = name;
this.parentClass = parentClass;
this.fullName = makeFullClsName(name, false);
} }
public static ClassInfo fromType(RootNode root, ArgType type) { public static ClassInfo fromType(RootNode root, ArgType type) {
if (type.isArray()) { ArgType clsType = checkClassType(type);
type = ArgType.OBJECT; ClassInfo cls = root.getInfoStorage().getCls(clsType);
}
ClassInfo cls = root.getInfoStorage().getCls(type);
if (cls != null) { if (cls != null) {
return cls; return cls;
} }
cls = new ClassInfo(root, type); ClassInfo newClsInfo = new ClassInfo(root, clsType);
return root.getInfoStorage().putCls(cls); return root.getInfoStorage().putCls(newClsInfo);
}
private static ArgType checkClassType(ArgType type) {
if (type == null) {
throw new JadxRuntimeException("Null class type");
}
if (type.isArray()) {
// TODO: check case with method declared in array class like ( clone in int[])
return ArgType.OBJECT;
}
if (!type.isObject() || type.isGenericType()) {
throw new JadxRuntimeException("Not class type: " + type);
}
if (type.isGeneric()) {
return ArgType.object(type.getObject());
}
return type;
} }
public static ClassInfo fromDex(DexNode dex, int clsIndex) { public static ClassInfo fromDex(DexNode dex, int clsIndex) {
...@@ -57,19 +82,27 @@ public final class ClassInfo implements Comparable<ClassInfo> { ...@@ -57,19 +82,27 @@ public final class ClassInfo implements Comparable<ClassInfo> {
return fromType(root, ArgType.object(clsName)); return fromType(root, ArgType.object(clsName));
} }
public static ClassInfo extCls(RootNode root, ArgType type) { public void rename(RootNode root, String fullName) {
ClassInfo classInfo = fromName(root, type.getObject()); if (!alias.makeFullName().equals(fullName)) {
return classInfo.alias; ClassInfo newAlias = new ClassInfo(type);
newAlias.splitAndApplyNames(root, fullName, isInner());
newAlias.alias = null;
this.alias = newAlias;
}
} }
public void rename(RootNode root, String fullName) { public void renameShortName(String aliasName) {
ArgType clsType = ArgType.object(fullName); if (!Objects.equals(name, aliasName)) {
ClassInfo newAlias = root.getInfoStorage().getCls(clsType); ClassInfo newAlias = new ClassInfo(type, alias.pkg, aliasName, parentClass);
if (newAlias == null) { newAlias.alias = null;
newAlias = new ClassInfo(root, clsType, isInner()); this.alias = newAlias;
root.getInfoStorage().putCls(newAlias);
} }
if (!alias.getFullName().equals(newAlias.getFullName())) { }
public void renamePkg(String aliasPkg) {
if (!Objects.equals(pkg, aliasPkg)) {
ClassInfo newAlias = new ClassInfo(type, aliasPkg, alias.name, parentClass);
newAlias.alias = null;
this.alias = newAlias; this.alias = newAlias;
} }
} }
...@@ -78,11 +111,18 @@ public final class ClassInfo implements Comparable<ClassInfo> { ...@@ -78,11 +111,18 @@ public final class ClassInfo implements Comparable<ClassInfo> {
} }
public ClassInfo getAlias() { public ClassInfo getAlias() {
return alias; return alias == null ? this : alias;
}
public boolean isAlias() {
return alias == null;
} }
private void splitNames(RootNode root, boolean canBeInner) { private void splitAndApplyNames(RootNode root, ArgType type, boolean canBeInner) {
String fullObjectName = type.getObject(); splitAndApplyNames(root, type.getObject(), canBeInner);
}
private void splitAndApplyNames(RootNode root, String fullObjectName, boolean canBeInner) {
String clsName; String clsName;
int dot = fullObjectName.lastIndexOf('.'); int dot = fullObjectName.lastIndexOf('.');
if (dot == -1) { if (dot == -1) {
...@@ -109,7 +149,7 @@ public final class ClassInfo implements Comparable<ClassInfo> { ...@@ -109,7 +149,7 @@ public final class ClassInfo implements Comparable<ClassInfo> {
this.fullName = makeFullClsName(clsName, false); this.fullName = makeFullClsName(clsName, false);
} }
public String makeFullClsName(String shortName, boolean raw) { private String makeFullClsName(String shortName, boolean raw) {
if (parentClass != null) { if (parentClass != null) {
String innerSep = raw ? "$" : "."; String innerSep = raw ? "$" : ".";
return parentClass.makeFullClsName(parentClass.getShortName(), raw) + innerSep + shortName; return parentClass.makeFullClsName(parentClass.getShortName(), raw) + innerSep + shortName;
...@@ -117,6 +157,10 @@ public final class ClassInfo implements Comparable<ClassInfo> { ...@@ -117,6 +157,10 @@ public final class ClassInfo implements Comparable<ClassInfo> {
return pkg.isEmpty() ? shortName : pkg + '.' + shortName; return pkg.isEmpty() ? shortName : pkg + '.' + shortName;
} }
public String makeFullName() {
return makeFullClsName(this.name, false);
}
public String makeRawFullName() { public String makeRawFullName() {
return makeFullClsName(this.name, true); return makeFullClsName(this.name, true);
} }
...@@ -129,7 +173,7 @@ public final class ClassInfo implements Comparable<ClassInfo> { ...@@ -129,7 +173,7 @@ public final class ClassInfo implements Comparable<ClassInfo> {
} }
public String getFullName() { public String getFullName() {
return fullName; return makeFullName();
} }
public String getShortName() { public String getShortName() {
...@@ -172,11 +216,12 @@ public final class ClassInfo implements Comparable<ClassInfo> { ...@@ -172,11 +216,12 @@ public final class ClassInfo implements Comparable<ClassInfo> {
} }
public void notInner(RootNode root) { public void notInner(RootNode root) {
splitNames(root, false); this.parentClass = null;
splitAndApplyNames(root, type, false);
} }
public void updateNames(RootNode root) { public void updateNames(RootNode root) {
splitNames(root, isInner()); splitAndApplyNames(root, type, isInner());
} }
public ArgType getType() { public ArgType getType() {
...@@ -185,7 +230,7 @@ public final class ClassInfo implements Comparable<ClassInfo> { ...@@ -185,7 +230,7 @@ public final class ClassInfo implements Comparable<ClassInfo> {
@Override @Override
public String toString() { public String toString() {
return fullName; return makeFullName();
} }
@Override @Override
......
...@@ -24,6 +24,7 @@ import jadx.core.dex.info.ClassInfo; ...@@ -24,6 +24,7 @@ import jadx.core.dex.info.ClassInfo;
import jadx.core.dex.info.FieldInfo; import jadx.core.dex.info.FieldInfo;
import jadx.core.dex.info.MethodInfo; import jadx.core.dex.info.MethodInfo;
import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.instructions.args.ArgType;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.core.utils.files.DexFile; import jadx.core.utils.files.DexFile;
public class DexNode implements IDexNode { public class DexNode implements IDexNode {
...@@ -93,6 +94,9 @@ public class DexNode implements IDexNode { ...@@ -93,6 +94,9 @@ public class DexNode implements IDexNode {
@Nullable @Nullable
ClassNode resolveClassLocal(ClassInfo clsInfo) { ClassNode resolveClassLocal(ClassInfo clsInfo) {
if (clsInfo.isAlias()) {
throw new JadxRuntimeException("Don't resolve class by alias: " + clsInfo);
}
return clsMap.get(clsInfo); return clsMap.get(clsInfo);
} }
......
...@@ -114,8 +114,8 @@ public class DependencyCollector extends AbstractVisitor { ...@@ -114,8 +114,8 @@ public class DependencyCollector extends AbstractVisitor {
private static void addDep(DexNode dex, Set<ClassNode> depList, ArgType type) { private static void addDep(DexNode dex, Set<ClassNode> depList, ArgType type) {
if (type != null) { if (type != null) {
if (type.isObject()) { if (type.isObject() && !type.isGenericType()) {
addDep(dex, depList, ClassInfo.fromName(dex.root(), type.getObject())); addDep(dex, depList, ClassInfo.fromType(dex.root(), type));
ArgType[] genericTypes = type.getGenericTypes(); ArgType[] genericTypes = type.getGenericTypes();
if (type.isGeneric() && genericTypes != null) { if (type.isGeneric() && genericTypes != null) {
for (ArgType argType : genericTypes) { for (ArgType argType : genericTypes) {
......
...@@ -60,9 +60,7 @@ public class RenameVisitor extends AbstractVisitor { ...@@ -60,9 +60,7 @@ public class RenameVisitor extends AbstractVisitor {
ClassInfo aliasClsInfo = clsInfo.getAlias(); ClassInfo aliasClsInfo = clsInfo.getAlias();
if (!clsFullPaths.add(aliasClsInfo.getFullPath().toLowerCase())) { if (!clsFullPaths.add(aliasClsInfo.getFullPath().toLowerCase())) {
String newShortName = deobfuscator.getClsAlias(cls); String newShortName = deobfuscator.getClsAlias(cls);
String newFullName = aliasClsInfo.makeFullClsName(newShortName, true); clsInfo.renameShortName(newShortName);
clsInfo.rename(root, newFullName);
clsFullPaths.add(clsInfo.getAlias().getFullPath().toLowerCase()); clsFullPaths.add(clsInfo.getAlias().getFullPath().toLowerCase());
} }
} }
...@@ -71,18 +69,14 @@ public class RenameVisitor extends AbstractVisitor { ...@@ -71,18 +69,14 @@ public class RenameVisitor extends AbstractVisitor {
private void checkClassName(ClassNode cls, JadxArgs args) { private void checkClassName(ClassNode cls, JadxArgs args) {
ClassInfo classInfo = cls.getClassInfo(); ClassInfo classInfo = cls.getClassInfo();
ClassInfo alias = classInfo.getAlias(); String clsName = classInfo.getAlias().getShortName();
String clsName = alias.getShortName();
String newShortName = fixClsShortName(args, clsName); String newShortName = fixClsShortName(args, clsName);
if (!newShortName.equals(clsName)) { if (!newShortName.equals(clsName)) {
classInfo.rename(cls.root(), alias.makeFullClsName(newShortName, true)); classInfo.renameShortName(newShortName);
alias = classInfo.getAlias();
} }
if (alias.getPackage().isEmpty()) { if (classInfo.getAlias().getPackage().isEmpty()) {
String fullName = alias.makeFullClsName(alias.getShortName(), true); classInfo.renamePkg(Consts.DEFAULT_PACKAGE_NAME);
String newFullName = Consts.DEFAULT_PACKAGE_NAME + '.' + fullName;
classInfo.rename(cls.root(), newFullName);
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment