fix(deobf): don't rename unresolved or classpath overridden methods

Change details:
- use common code for process override methods in deobfuscator (move OverrideMethodVisitor to pre-decompile stage)
- add all public methods to jadx class set to allow search of override base method
- add don't rename flag if override hierarchy have unresolved methods
This commit is contained in:
Skylot
2020-12-18 11:37:47 +00:00
parent 549f346d5e
commit 2ca3c65300
21 changed files with 346 additions and 239 deletions
@@ -60,7 +60,7 @@ public class ConvertToClsSet {
set.loadFrom(root);
set.save(output);
LOG.info("Output: {}, file size: {}B", output, output.toFile().length());
LOG.info("Output: {}", output);
LOG.info("done");
}
}
+1 -1
View File
@@ -81,6 +81,7 @@ public class Jadx {
public static List<IDexTreeVisitor> getPreDecompilePassesList() {
List<IDexTreeVisitor> passes = new ArrayList<>();
passes.add(new SignatureProcessor());
passes.add(new OverrideMethodVisitor());
passes.add(new RenameVisitor());
passes.add(new UsageInfoVisitor());
return passes;
@@ -107,7 +108,6 @@ public class Jadx {
passes.add(new BlockFinish());
passes.add(new AttachMethodDetails());
passes.add(new OverrideMethodVisitor());
passes.add(new SSATransform());
passes.add(new MoveInlineVisitor());
@@ -39,8 +39,6 @@ import jadx.core.utils.exceptions.DecodeException;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.core.utils.files.FileUtils;
import static jadx.core.utils.Utils.notEmpty;
/**
* Classes list for import into classpath graph
*/
@@ -131,25 +129,13 @@ public class ClsSet {
private void processMethodDetails(MethodNode mth, List<ClspMethod> methods) {
AccessInfo accessFlags = mth.getAccessFlags();
if (accessFlags.isPrivate()) {
if (accessFlags.isPrivate() || accessFlags.isSynthetic() || accessFlags.isBridge()) {
return;
}
ArgType genericRetType = mth.getReturnType();
boolean varArgs = accessFlags.isVarArgs();
List<ArgType> throwList = mth.getThrows();
List<ArgType> typeParameters = mth.getTypeParameters();
// add only methods with additional info
if (varArgs
|| notEmpty(throwList)
|| notEmpty(typeParameters)
|| genericRetType.containsGeneric()
|| mth.containsGenericArgs()
|| mth.isArgsOverloaded()) {
ClspMethod clspMethod = new ClspMethod(mth.getMethodInfo(),
mth.getArgTypes(), genericRetType,
typeParameters, varArgs, throwList);
methods.add(clspMethod);
}
ClspMethod clspMethod = new ClspMethod(mth.getMethodInfo(), mth.getArgTypes(),
mth.getReturnType(), mth.getTypeParameters(),
mth.getThrows(), accessFlags.rawValue());
methods.add(clspMethod);
}
public static ArgType[] makeParentsArray(ClassNode cls) {
@@ -245,7 +231,7 @@ public class ClsSet {
}
}
int methodsCount = Stream.of(classes).mapToInt(c -> c.getMethodsMap().size()).sum();
LOG.info("Classes: {}, methods: {}, file size: {}B", classes.length, methodsCount, out.size());
LOG.info("Classes: {}, methods: {}, file size: {} bytes", classes.length, methodsCount, out.size());
}
private static void writeMethod(DataOutputStream out, ClspMethod method, Map<String, ClspClass> names) throws IOException {
@@ -257,7 +243,7 @@ public class ClsSet {
writeArgTypesList(out, method.containsGenericArgs() ? method.getArgTypes() : Collections.emptyList(), names);
writeArgType(out, method.getReturnType(), names);
writeArgTypesList(out, method.getTypeParameters(), names);
out.writeBoolean(method.isVarArg());
out.writeInt(method.getRawAccessFlags());
writeArgTypesList(out, method.getThrows(), names);
}
@@ -392,12 +378,12 @@ public class ClsSet {
genericRetType = retType;
}
List<ArgType> typeParameters = readArgTypesList(in);
boolean varArgs = in.readBoolean();
int accFlags = in.readInt();
List<ArgType> throwList = readArgTypesList(in);
MethodInfo methodInfo = MethodInfo.fromDetails(root, clsInfo, name, argTypes, retType);
return new ClspMethod(methodInfo,
genericArgTypes, genericRetType,
typeParameters, varArgs, throwList);
typeParameters, throwList, accFlags);
}
private List<ArgType> readArgTypesList(DataInputStream in) throws IOException {
@@ -91,7 +91,7 @@ public class ClspGraph {
}
}
}
// all other methods in known ClspClass are 'simple'
// unknown method
return new SimpleMethodDetails(methodInfo);
}
@@ -5,6 +5,7 @@ import java.util.Objects;
import org.jetbrains.annotations.NotNull;
import jadx.api.plugins.input.data.AccessFlags;
import jadx.core.dex.info.MethodInfo;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.nodes.IMethodDetails;
@@ -20,18 +21,17 @@ public class ClspMethod implements IMethodDetails, Comparable<ClspMethod> {
private final ArgType returnType;
private final List<ArgType> typeParameters;
private final List<ArgType> throwList;
private final boolean varArg;
private final int accFlags;
public ClspMethod(MethodInfo methodInfo,
List<ArgType> argTypes, ArgType returnType,
List<ArgType> typeParameters,
boolean varArgs, List<ArgType> throwList) {
List<ArgType> typeParameters, List<ArgType> throwList, int accFlags) {
this.methodInfo = methodInfo;
this.argTypes = argTypes;
this.returnType = returnType;
this.typeParameters = typeParameters;
this.throwList = throwList;
this.varArg = varArgs;
this.accFlags = accFlags;
}
@Override
@@ -69,7 +69,12 @@ public class ClspMethod implements IMethodDetails, Comparable<ClspMethod> {
@Override
public boolean isVarArg() {
return varArg;
return (accFlags & AccessFlags.VARARGS) != 0;
}
@Override
public int getRawAccessFlags() {
return accFlags;
}
@Override
@@ -3,10 +3,15 @@ package jadx.core.clsp;
import java.util.Collections;
import java.util.List;
import jadx.api.plugins.input.data.AccessFlags;
import jadx.core.dex.info.MethodInfo;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.nodes.IMethodDetails;
/**
* Method details build from MethodInfo.
* Note: some fields have unknown values.
*/
public class SimpleMethodDetails implements IMethodDetails {
private final MethodInfo methodInfo;
@@ -45,6 +50,11 @@ public class SimpleMethodDetails implements IMethodDetails {
return false;
}
@Override
public int getRawAccessFlags() {
return AccessFlags.PUBLIC;
}
@Override
public String toString() {
return "SimpleMethodDetails{" + methodInfo + '}';
@@ -27,7 +27,6 @@ import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.instructions.args.CodeVar;
import jadx.core.dex.instructions.args.RegisterArg;
import jadx.core.dex.instructions.args.SSAVar;
import jadx.core.dex.nodes.IMethodDetails;
import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.trycatch.CatchAttr;
@@ -166,15 +165,14 @@ public class MethodGen {
if (overrideAttr == null) {
return;
}
code.startLine("@Override");
code.add(" // ");
Iterator<IMethodDetails> it = overrideAttr.getOverrideList().iterator();
while (it.hasNext()) {
IMethodDetails methodDetails = it.next();
code.add(methodDetails.getMethodInfo().getDeclClass().getAliasFullName());
if (it.hasNext()) {
code.add(", ");
}
if (!overrideAttr.isAtBaseMth()) {
code.startLine("@Override");
code.add(" // ");
code.add(Utils.listToString(overrideAttr.getOverrideList(), ", ", md -> md.getMethodInfo().getDeclClass().getAliasFullName()));
}
if (Consts.DEBUG) {
code.startLine("// related by override: ");
code.add(Utils.listToString(overrideAttr.getRelatedMthNodes(), ", ", m -> m.getParentClass().getFullName()));
}
}
@@ -1,13 +1,10 @@
package jadx.core.deobf;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
@@ -19,6 +16,7 @@ import org.slf4j.LoggerFactory;
import jadx.api.JadxArgs;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.nodes.MethodOverrideAttr;
import jadx.core.dex.attributes.nodes.SourceFileAttr;
import jadx.core.dex.info.ClassInfo;
import jadx.core.dex.info.FieldInfo;
@@ -46,9 +44,6 @@ public class Deobfuscator {
private final Map<FieldInfo, String> fldMap = new HashMap<>();
private final Map<MethodInfo, String> mthMap = new HashMap<>();
private final Map<MethodInfo, OverridedMethodsNode> ovrdMap = new HashMap<>();
private final List<OverridedMethodsNode> ovrd = new ArrayList<>();
private final PackageNode rootPackage = new PackageNode("");
private final Set<String> pkgSet = new TreeSet<>();
private final Set<String> reservedClsNames = new HashSet<>();
@@ -92,9 +87,6 @@ public class Deobfuscator {
clsMap.clear();
fldMap.clear();
mthMap.clear();
ovrd.clear();
ovrdMap.clear();
}
private void initIndexes() {
@@ -121,100 +113,6 @@ public class Deobfuscator {
for (ClassNode cls : root.getClasses()) {
processClass(cls);
}
postProcess();
}
private void postProcess() {
int id = 1;
for (OverridedMethodsNode o : ovrd) {
boolean aliasFromPreset = false;
String aliasToUse = null;
for (MethodInfo mth : o.getMethods()) {
if (mth.isAliasFromPreset()) {
aliasToUse = mth.getAlias();
aliasFromPreset = true;
}
}
for (MethodInfo mth : o.getMethods()) {
if (aliasToUse == null) {
if (mth.hasAlias() && !mth.isAliasFromPreset()) {
mth.setAlias(String.format("mo%d%s", id, prepareNamePart(mth.getName())));
}
aliasToUse = mth.getAlias();
}
mth.setAlias(aliasToUse);
mth.setAliasFromPreset(aliasFromPreset);
}
id++;
}
}
private void resolveOverriding(MethodNode mth) {
Set<ClassNode> clsParents = new LinkedHashSet<>();
collectClassHierarchy(mth.getParentClass(), clsParents);
String mthSignature = mth.getMethodInfo().makeSignature(false);
Set<MethodInfo> overrideSet = new LinkedHashSet<>();
for (ClassNode classNode : clsParents) {
MethodInfo methodInfo = getMthOverride(classNode.getMethods(), mthSignature);
if (methodInfo != null) {
overrideSet.add(methodInfo);
}
}
if (overrideSet.isEmpty()) {
return;
}
OverridedMethodsNode overrideNode = getOverrideMethodsNode(overrideSet);
if (overrideNode == null) {
overrideNode = new OverridedMethodsNode(overrideSet);
ovrd.add(overrideNode);
}
for (MethodInfo overrideMth : overrideSet) {
if (!ovrdMap.containsKey(overrideMth)) {
ovrdMap.put(overrideMth, overrideNode);
overrideNode.add(overrideMth);
}
}
}
private OverridedMethodsNode getOverrideMethodsNode(Set<MethodInfo> overrideSet) {
for (MethodInfo overrideMth : overrideSet) {
OverridedMethodsNode node = ovrdMap.get(overrideMth);
if (node != null) {
return node;
}
}
return null;
}
private MethodInfo getMthOverride(List<MethodNode> methods, String mthSignature) {
for (MethodNode m : methods) {
MethodInfo mthInfo = m.getMethodInfo();
if (mthInfo.getShortId().startsWith(mthSignature)) {
return mthInfo;
}
}
return null;
}
private void collectClassHierarchy(ClassNode cls, Set<ClassNode> collected) {
boolean added = collected.add(cls);
if (added) {
ArgType superClass = cls.getSuperClass();
if (superClass != null) {
ClassNode superNode = cls.root().resolveClass(superClass);
if (superNode != null) {
collectClassHierarchy(superNode, collected);
}
}
for (ArgType argType : cls.getInterfaces()) {
ClassNode interfaceNode = cls.root().resolveClass(argType);
if (interfaceNode != null) {
collectClassHierarchy(interfaceNode, collected);
}
}
}
}
private void processClass(ClassNode cls) {
@@ -266,16 +164,27 @@ public class Deobfuscator {
String alias = getMethodAlias(mth);
if (alias != null) {
mth.getMethodInfo().setAlias(alias);
}
if (mth.isVirtual()) {
resolveOverriding(mth);
resolveOverriding(mth, alias);
}
}
public void forceRenameMethod(MethodNode mth) {
mth.getMethodInfo().setAlias(makeMethodAlias(mth));
if (mth.isVirtual()) {
resolveOverriding(mth);
String alias = makeMethodAlias(mth);
mth.getMethodInfo().setAlias(alias);
resolveOverriding(mth, alias);
}
private void resolveOverriding(MethodNode mth, String alias) {
MethodOverrideAttr overrideAttr = mth.get(AType.METHOD_OVERRIDE);
if (overrideAttr != null) {
for (MethodNode ovrdMth : overrideAttr.getRelatedMthNodes()) {
if (ovrdMth == mth) {
continue;
}
MethodInfo methodInfo = ovrdMth.getMethodInfo();
methodInfo.setAlias(alias);
mthMap.put(methodInfo, alias);
}
}
}
@@ -523,22 +432,42 @@ public class Deobfuscator {
@Nullable
private String getMethodAlias(MethodNode mth) {
if (mth.contains(AFlag.DONT_RENAME)) {
return null;
}
MethodInfo methodInfo = mth.getMethodInfo();
if (methodInfo.isClassInit() || methodInfo.isConstructor()) {
return null;
}
String alias = getAssignedAlias(methodInfo);
if (alias != null) {
return alias;
}
MethodOverrideAttr overrideAttr = mth.get(AType.METHOD_OVERRIDE);
if (overrideAttr != null) {
for (MethodNode relatedMthNode : overrideAttr.getRelatedMthNodes()) {
String assignedAlias = getAssignedAlias(relatedMthNode.getMethodInfo());
if (assignedAlias != null) {
return assignedAlias;
}
}
}
if (shouldRename(mth.getName())) {
return makeMethodAlias(mth);
}
return null;
}
@Nullable
private String getAssignedAlias(MethodInfo methodInfo) {
String alias = mthMap.get(methodInfo);
if (alias != null) {
return alias;
}
alias = deobfPresets.getForMth(methodInfo);
if (alias != null) {
mthMap.put(methodInfo, alias);
methodInfo.setAliasFromPreset(true);
return alias;
}
if (shouldRename(mth.getName())) {
return makeMethodAlias(mth);
String presetAlias = deobfPresets.getForMth(methodInfo);
if (presetAlias != null) {
mthMap.put(methodInfo, presetAlias);
return presetAlias;
}
return null;
}
@@ -550,7 +479,13 @@ public class Deobfuscator {
}
public String makeMethodAlias(MethodNode mth) {
String alias = String.format("m%d%s", mthIndex++, prepareNamePart(mth.getName()));
String prefix;
if (mth.contains(AType.METHOD_OVERRIDE)) {
prefix = "mo";
} else {
prefix = "m";
}
String alias = String.format("%s%d%s", prefix, mthIndex++, prepareNamePart(mth.getName()));
mthMap.put(mth.getMethodInfo(), alias);
return alias;
}
@@ -1,26 +0,0 @@
package jadx.core.deobf;
import java.util.Set;
import jadx.core.dex.info.MethodInfo;
class OverridedMethodsNode {
private final Set<MethodInfo> methods;
public OverridedMethodsNode(Set<MethodInfo> methodsSet) {
methods = methodsSet;
}
public boolean contains(MethodInfo mth) {
return methods.contains(mth);
}
public void add(MethodInfo mth) {
methods.add(mth);
}
public Set<MethodInfo> getMethods() {
return methods;
}
}
@@ -97,5 +97,6 @@ public class AType<T extends IAttribute> {
FIELD_INIT,
FIELD_REPLACE,
METHOD_INLINE,
METHOD_OVERRIDE,
SKIP_MTH_ARGS));
}
@@ -5,19 +5,45 @@ import java.util.List;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.IAttribute;
import jadx.core.dex.nodes.IMethodDetails;
import jadx.core.dex.nodes.MethodNode;
public class MethodOverrideAttr implements IAttribute {
private final List<IMethodDetails> overrideList;
/**
* All methods overridden by current method. Current method excluded, empty for base method.
*/
private List<IMethodDetails> overrideList;
public MethodOverrideAttr(List<IMethodDetails> overrideList) {
/**
* All method nodes from override hierarchy. Current method included.
*/
private List<MethodNode> relatedMthNodes;
public MethodOverrideAttr(List<IMethodDetails> overrideList, List<MethodNode> relatedMthNodes) {
this.overrideList = overrideList;
this.relatedMthNodes = relatedMthNodes;
}
public boolean isAtBaseMth() {
return overrideList.isEmpty();
}
public List<IMethodDetails> getOverrideList() {
return overrideList;
}
public void setOverrideList(List<IMethodDetails> overrideList) {
this.overrideList = overrideList;
}
public List<MethodNode> getRelatedMthNodes() {
return relatedMthNodes;
}
public void setRelatedMthNodes(List<MethodNode> relatedMthNodes) {
this.relatedMthNodes = relatedMthNodes;
}
@Override
public AType<MethodOverrideAttr> getType() {
return AType.METHOD_OVERRIDE;
@@ -19,12 +19,10 @@ public final class MethodInfo implements Comparable<MethodInfo> {
private final ClassInfo declClass;
private final String shortId;
private String alias;
private boolean aliasFromPreset;
private MethodInfo(ClassInfo declClass, String name, List<ArgType> args, ArgType retType) {
this.name = name;
this.alias = name;
this.aliasFromPreset = false;
this.declClass = declClass;
this.argTypes = args;
this.retType = retType;
@@ -143,14 +141,6 @@ public final class MethodInfo implements Comparable<MethodInfo> {
return !name.equals(alias);
}
public boolean isAliasFromPreset() {
return aliasFromPreset;
}
public void setAliasFromPreset(boolean value) {
aliasFromPreset = value;
}
@Override
public int hashCode() {
return shortId.hashCode() + 31 * declClass.hashCode();
@@ -22,6 +22,8 @@ public interface IMethodDetails extends IAttribute {
boolean isVarArg();
int getRawAccessFlags();
@Override
default AType<IMethodDetails> getType() {
return AType.METHOD_DETAILS;
@@ -522,6 +522,11 @@ public class MethodNode extends NotificationAttrNode implements IMethodDetails,
return sVars;
}
@Override
public int getRawAccessFlags() {
return accFlags.rawValue();
}
@Override
public AccessInfo getAccessFlags() {
return accFlags;
@@ -17,8 +17,7 @@ import jadx.core.utils.exceptions.JadxException;
runBefore = {
CodeShrinkVisitor.class,
TypeInferenceVisitor.class,
MethodInvokeVisitor.class,
OverrideMethodVisitor.class
MethodInvokeVisitor.class
}
)
public class AttachMethodDetails extends AbstractVisitor {
@@ -1,13 +1,19 @@
package jadx.core.dex.visitors;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import jadx.core.clsp.ClspClass;
import jadx.core.clsp.ClspMethod;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.nodes.MethodOverrideAttr;
import jadx.core.dex.info.AccessInfo;
@@ -19,13 +25,15 @@ import jadx.core.dex.nodes.RootNode;
import jadx.core.dex.visitors.typeinference.TypeCompare;
import jadx.core.dex.visitors.typeinference.TypeCompareEnum;
import jadx.core.dex.visitors.typeinference.TypeInferenceVisitor;
import jadx.core.utils.Utils;
import jadx.core.utils.exceptions.JadxException;
@JadxVisitor(
name = "OverrideMethodVisitor",
desc = "Mark override methods and revert type erasure",
runBefore = {
TypeInferenceVisitor.class
TypeInferenceVisitor.class,
RenameVisitor.class
}
)
public class OverrideMethodVisitor extends AbstractVisitor {
@@ -33,8 +41,10 @@ public class OverrideMethodVisitor extends AbstractVisitor {
@Override
public boolean visit(ClassNode cls) throws JadxException {
List<ArgType> superTypes = collectSuperTypes(cls);
for (MethodNode mth : cls.getMethods()) {
processMth(cls, superTypes, mth);
if (!superTypes.isEmpty()) {
for (MethodNode mth : cls.getMethods()) {
processMth(cls, superTypes, mth);
}
}
return true;
}
@@ -43,27 +53,36 @@ public class OverrideMethodVisitor extends AbstractVisitor {
if (mth.isConstructor() || mth.getAccessFlags().isStatic()) {
return;
}
mth.remove(AType.METHOD_OVERRIDE);
String signature = mth.getMethodInfo().makeSignature(false);
List<IMethodDetails> overrideList = collectOverrideMethods(cls, superTypes, signature);
if (!overrideList.isEmpty()) {
mth.addAttr(new MethodOverrideAttr(overrideList));
fixMethodReturnType(mth, overrideList, superTypes);
fixMethodArgTypes(mth, overrideList, superTypes);
MethodOverrideAttr attr = processOverrideMethods(cls, mth, superTypes);
if (attr != null) {
mth.addAttr(attr);
IMethodDetails baseMth = Utils.last(attr.getOverrideList());
if (baseMth != null) {
fixMethodReturnType(mth, baseMth, superTypes);
fixMethodArgTypes(mth, baseMth, superTypes);
}
}
}
private List<IMethodDetails> collectOverrideMethods(ClassNode cls, List<ArgType> superTypes, String signature) {
private MethodOverrideAttr processOverrideMethods(ClassNode cls, MethodNode mth, List<ArgType> superTypes) {
MethodOverrideAttr result = mth.get(AType.METHOD_OVERRIDE);
if (result != null) {
return result;
}
String signature = mth.getMethodInfo().makeSignature(false);
List<IMethodDetails> overrideList = new ArrayList<>();
for (ArgType superType : superTypes) {
ClassNode classNode = cls.root().resolveClass(superType);
if (classNode != null) {
for (MethodNode mth : classNode.getMethods()) {
if (!mth.getAccessFlags().isStatic()
&& isMethodVisibleInCls(mth, cls)) {
String mthShortId = mth.getMethodInfo().getShortId();
if (mthShortId.startsWith(signature)) {
overrideList.add(mth);
for (MethodNode supMth : classNode.getMethods()) {
String mthShortId = supMth.getMethodInfo().getShortId();
if (!supMth.getAccessFlags().isStatic()
&& mthShortId.startsWith(signature)
&& isMethodVisibleInCls(supMth, cls)) {
overrideList.add(supMth);
MethodOverrideAttr attr = supMth.get(AType.METHOD_OVERRIDE);
if (attr != null) {
return buildOverrideAttr(mth, overrideList, attr);
}
}
}
@@ -80,7 +99,62 @@ public class OverrideMethodVisitor extends AbstractVisitor {
}
}
}
return overrideList;
return buildOverrideAttr(mth, overrideList, null);
}
@Nullable
private MethodOverrideAttr buildOverrideAttr(MethodNode mth, List<IMethodDetails> overrideList, @Nullable MethodOverrideAttr attr) {
if (overrideList.isEmpty() && attr == null) {
return null;
}
List<IMethodDetails> cleanOverrideList = overrideList.stream().distinct().collect(Collectors.toList());
if (attr == null) {
// traced to base method
return applyOverrideAttr(mth, cleanOverrideList, false);
}
// trace stopped at already processed method -> start merging
List<IMethodDetails> mergedOverrideList = Utils.mergeLists(cleanOverrideList, attr.getOverrideList());
return applyOverrideAttr(mth, mergedOverrideList, true);
}
private MethodOverrideAttr applyOverrideAttr(MethodNode mth, List<IMethodDetails> overrideList, boolean update) {
// don't rename method if override list contains not resolved method
boolean dontRename = overrideList.stream().anyMatch(m -> !(m instanceof MethodNode));
List<MethodNode> mthNodes = getMethodNodes(mth, overrideList);
int depth = 0;
for (MethodNode mthNode : mthNodes) {
if (dontRename) {
mthNode.add(AFlag.DONT_RENAME);
}
if (depth == 0) {
// skip current (first) method
depth = 1;
continue;
}
if (update) {
MethodOverrideAttr ovrdAttr = mthNode.get(AType.METHOD_OVERRIDE);
if (ovrdAttr != null) {
ovrdAttr.setRelatedMthNodes(mthNodes);
continue;
}
}
mthNode.addAttr(new MethodOverrideAttr(Utils.listTail(overrideList, depth), mthNodes));
depth++;
}
return new MethodOverrideAttr(overrideList, mthNodes);
}
@NotNull
private List<MethodNode> getMethodNodes(MethodNode mth, List<IMethodDetails> overrideList) {
ArrayList<MethodNode> list = new ArrayList<>();
list.add(mth);
for (IMethodDetails md : overrideList) {
if (md instanceof MethodNode) {
list.add((MethodNode) md);
}
}
list.trimToSize();
return list;
}
/**
@@ -99,8 +173,11 @@ public class OverrideMethodVisitor extends AbstractVisitor {
}
private List<ArgType> collectSuperTypes(ClassNode cls) {
Map<String, ArgType> superTypes = new HashMap<>();
Map<String, ArgType> superTypes = new LinkedHashMap<>();
collectSuperTypes(cls, superTypes);
if (superTypes.isEmpty()) {
return Collections.emptyList();
}
return new ArrayList<>(superTypes.values());
}
@@ -128,24 +205,13 @@ public class OverrideMethodVisitor extends AbstractVisitor {
}
}
private void fixMethodReturnType(MethodNode mth, List<IMethodDetails> overrideList, List<ArgType> superTypes) {
private void fixMethodReturnType(MethodNode mth, IMethodDetails baseMth, List<ArgType> superTypes) {
ArgType returnType = mth.getReturnType();
if (returnType == ArgType.VOID) {
return;
}
int updateCount = 0;
for (IMethodDetails baseMth : overrideList) {
if (updateReturnType(mth, baseMth, superTypes)) {
updateCount++;
}
}
if (updateCount == 0) {
return;
}
if (updateCount == 1) {
if (updateReturnType(mth, baseMth, superTypes)) {
mth.addComment("Return type fixed from '" + returnType + "' to match base method");
} else {
mth.addWarnComment("Due to multiple override return type can be incorrect, original value: " + returnType);
}
}
@@ -174,13 +240,7 @@ public class OverrideMethodVisitor extends AbstractVisitor {
return false;
}
private void fixMethodArgTypes(MethodNode mth, List<IMethodDetails> overrideList, List<ArgType> superTypes) {
for (IMethodDetails baseMth : overrideList) {
updateArgTypes(mth, baseMth, superTypes);
}
}
private void updateArgTypes(MethodNode mth, IMethodDetails baseMth, List<ArgType> superTypes) {
private void fixMethodArgTypes(MethodNode mth, IMethodDetails baseMth, List<ArgType> superTypes) {
List<ArgType> mthArgTypes = mth.getArgTypes();
List<ArgType> baseArgTypes = baseMth.getArgTypes();
if (mthArgTypes.equals(baseArgTypes)) {
@@ -15,6 +15,7 @@ public class MutableMethodDetails implements IMethodDetails {
private List<ArgType> typeParams;
private List<ArgType> throwTypes;
private boolean varArg;
private int accFlags;
public MutableMethodDetails(IMethodDetails base) {
this.mthInfo = base.getMethodInfo();
@@ -23,6 +24,7 @@ public class MutableMethodDetails implements IMethodDetails {
this.typeParams = Collections.unmodifiableList(base.getTypeParameters());
this.throwTypes = Collections.unmodifiableList(base.getThrows());
this.varArg = base.isVarArg();
this.accFlags = base.getRawAccessFlags();
}
@Override
@@ -75,6 +77,15 @@ public class MutableMethodDetails implements IMethodDetails {
this.varArg = varArg;
}
@Override
public int getRawAccessFlags() {
return accFlags;
}
public void setRawAccessFlags(int accFlags) {
this.accFlags = accFlags;
}
@Override
public String toString() {
return "Mutable" + toAttrString();
@@ -242,6 +242,33 @@ public class Utils {
return new ImmutableList<>(list);
}
/**
* Sub list from startIndex (inclusive) to list end
*/
public static <T> List<T> listTail(List<T> list, int startIndex) {
if (startIndex == 0) {
return list;
}
int size = list.size();
if (startIndex >= size) {
return Collections.emptyList();
}
return list.subList(startIndex, size);
}
public static <T> List<T> mergeLists(List<T> first, List<T> second) {
if (isEmpty(first)) {
return second;
}
if (isEmpty(second)) {
return first;
}
List<T> result = new ArrayList<>(first.size() + second.size());
result.addAll(first);
result.addAll(second);
return result;
}
public static Map<String, String> newConstStringMap(String... parameters) {
int len = parameters.length;
if (len == 0) {
Binary file not shown.
@@ -0,0 +1,36 @@
package jadx.tests.integration.deobf;
import org.junit.jupiter.api.Test;
import jadx.tests.api.IntegrationTest;
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
public class TestDontRenameClspOverriddenMethod extends IntegrationTest {
public static class TestCls {
public static class A implements Runnable {
@Override
public void run() {
}
}
public static class B extends A {
@Override
public void run() {
}
}
}
@Test
public void test() {
noDebugInfo();
enableDeobfuscation();
args.setDeobfuscationMinLength(100); // rename everything
assertThat(getClassNode(TestCls.class))
.code()
.countString(2, "public void run() {");
}
}
@@ -0,0 +1,42 @@
package jadx.tests.integration.deobf;
import org.junit.jupiter.api.Test;
import jadx.tests.api.IntegrationTest;
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
public class TestRenameOverriddenMethod extends IntegrationTest {
public static class TestCls {
public interface I {
void m();
}
public static class A implements I {
@Override
public void m() {
}
}
public static class B extends A {
@Override
public void m() {
}
}
}
@Test
public void test() {
noDebugInfo();
enableDeobfuscation();
args.setDeobfuscationMinLength(100); // rename everything
assertThat(getClassNode(TestCls.class))
.code()
.countString(2, "@Override")
.countString(3, "/* renamed from: m */")
.containsOne("void mo0m();")
.countString(2, "public void mo0m() {");
}
}