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