Skip to content

Commit

Permalink
fix: improve class renaming and add checks for class alias usage (#532)
Browse files Browse the repository at this point in the history
  • Loading branch information
skylot committed Apr 22, 2019
1 parent 41abbb1 commit f283ef4
Show file tree
Hide file tree
Showing 8 changed files with 111 additions and 65 deletions.
35 changes: 19 additions & 16 deletions jadx-core/src/main/java/jadx/core/codegen/ClassGen.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
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 {

Expand Down Expand Up @@ -141,7 +142,7 @@ public void addClassDeclaration(CodeWriter clsCode) {
clsCode.add("class ");
}
clsCode.attachDefinition(cls);
clsCode.add(cls.getShortName());
clsCode.add(cls.getAlias().getShortName());

addGenericMap(clsCode, cls.getGenericMap(), true);
clsCode.add(' ');
Expand Down Expand Up @@ -199,9 +200,8 @@ public boolean addGenericMap(CodeWriter code, Map<ArgType, List<ArgType>> gmap,
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()) {
Expand Down Expand Up @@ -462,7 +462,7 @@ public void useType(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();
if (generics != null) {
code.add('<');
Expand Down Expand Up @@ -503,16 +503,16 @@ public void useClass(CodeWriter code, ClassNode classNode) {
}

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;
}
Expand All @@ -538,14 +538,14 @@ private String useClassInternal(ClassInfo useCls, ClassInfo extClsInfo) {
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;
Expand All @@ -558,8 +558,11 @@ private String useClassInternal(ClassInfo useCls, ClassInfo extClsInfo) {

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);
}
}
Expand Down Expand Up @@ -594,15 +597,15 @@ private static boolean searchCollision(DexNode dex, ClassInfo useCls, ClassInfo
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;
}
}
Expand Down
5 changes: 1 addition & 4 deletions jadx-core/src/main/java/jadx/core/codegen/MethodGen.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
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;
Expand Down Expand Up @@ -180,10 +179,8 @@ public void addInstructions(CodeWriter code) throws CodegenException {
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('(')
Expand Down
5 changes: 4 additions & 1 deletion jadx-core/src/main/java/jadx/core/codegen/NameGen.java
Original file line number Diff line number Diff line change
Expand Up @@ -177,12 +177,15 @@ private static String makeNameForPrimitive(ArgType type) {
}

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) {
Expand Down
2 changes: 1 addition & 1 deletion jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ private void processClass(ClassNode cls) {
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)) {
Expand Down
105 changes: 75 additions & 30 deletions jadx-core/src/main/java/jadx/core/dex/info/ClassInfo.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
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;
Expand All @@ -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) {
Expand All @@ -57,19 +82,27 @@ public static ClassInfo fromName(RootNode root, String clsName) {
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;
}
}
Expand All @@ -78,11 +111,18 @@ public boolean isRenamed() {
}

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) {
Expand All @@ -109,14 +149,18 @@ private void splitNames(RootNode root, boolean canBeInner) {
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;
}
return pkg.isEmpty() ? shortName : pkg + '.' + shortName;
}

public String makeFullName() {
return makeFullClsName(this.name, false);
}

public String makeRawFullName() {
return makeFullClsName(this.name, true);
}
Expand All @@ -129,7 +173,7 @@ public String getFullPath() {
}

public String getFullName() {
return fullName;
return makeFullName();
}

public String getShortName() {
Expand Down Expand Up @@ -172,11 +216,12 @@ public boolean isInner() {
}

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() {
Expand All @@ -185,7 +230,7 @@ public ArgType getType() {

@Override
public String toString() {
return fullName;
return makeFullName();
}

@Override
Expand Down
4 changes: 4 additions & 0 deletions jadx-core/src/main/java/jadx/core/dex/nodes/DexNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
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 {
Expand Down Expand Up @@ -93,6 +94,9 @@ public List<ClassNode> getClasses() {

@Nullable
ClassNode resolveClassLocal(ClassInfo clsInfo) {
if (clsInfo.isAlias()) {
throw new JadxRuntimeException("Don't resolve class by alias: " + clsInfo);
}
return clsMap.get(clsInfo);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@ private static void processCustomInsn(DexNode dex, Set<ClassNode> depList, InsnN

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) {
Expand Down
Loading

0 comments on commit f283ef4

Please sign in to comment.