Commit 19c57258 authored by Skylot's avatar Skylot

fix: improve rename checks and show rename reason (#584)

parent fef3e55c
package jadx.cli; package jadx.cli;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Set; import java.util.Set;
...@@ -106,7 +106,7 @@ public class JadxCLIArgs { ...@@ -106,7 +106,7 @@ public class JadxCLIArgs {
+ " 'case' for system case sensitivity," + " 'case' for system case sensitivity,"
+ " 'valid' for java identifiers," + " 'valid' for java identifiers,"
+ " 'printable' characters," + " 'printable' characters,"
+ " 'none' or 'all'", + " 'none' or 'all' (default)",
converter = RenameConverter.class converter = RenameConverter.class
) )
protected Set<RenameEnum> renameFlags = EnumSet.allOf(RenameEnum.class); protected Set<RenameEnum> renameFlags = EnumSet.allOf(RenameEnum.class);
...@@ -181,7 +181,7 @@ public class JadxCLIArgs { ...@@ -181,7 +181,7 @@ public class JadxCLIArgs {
args.setThreadsCount(threadsCount); args.setThreadsCount(threadsCount);
args.setSkipSources(skipSources); args.setSkipSources(skipSources);
if (singleClass != null) { if (singleClass != null) {
args.setClassFilter((className) -> singleClass.equals(className)); args.setClassFilter(className -> singleClass.equals(className));
} }
args.setSkipResources(skipResources); args.setSkipResources(skipResources);
args.setFallbackMode(fallbackMode); args.setFallbackMode(fallbackMode);
...@@ -303,38 +303,14 @@ public class JadxCLIArgs { ...@@ -303,38 +303,14 @@ public class JadxCLIArgs {
return renameFlags.contains(RenameEnum.CASE); return renameFlags.contains(RenameEnum.CASE);
} }
public void setRenameCaseSensitive(boolean renameCase) {
if (renameCase && !isRenameCaseSensitive()) {
renameFlags.add(RenameEnum.CASE);
} else if (!renameCase && isRenameCaseSensitive()) {
renameFlags.remove(RenameEnum.CASE);
}
}
public boolean isRenameValid() { public boolean isRenameValid() {
return renameFlags.contains(RenameEnum.VALID); return renameFlags.contains(RenameEnum.VALID);
} }
public void setRenameValid(boolean renameValid) {
if (renameValid && !isRenameValid()) {
renameFlags.add(RenameEnum.VALID);
} else if (!renameValid && isRenameValid()) {
renameFlags.remove(RenameEnum.VALID);
}
}
public boolean isRenamePrintable() { public boolean isRenamePrintable() {
return renameFlags.contains(RenameEnum.PRINTABLE); return renameFlags.contains(RenameEnum.PRINTABLE);
} }
public void setRenamePrintable(boolean renamePrintable) {
if (renamePrintable && !isRenamePrintable()) {
renameFlags.add(RenameEnum.PRINTABLE);
} else if (!renamePrintable && isRenamePrintable()) {
renameFlags.remove(RenameEnum.PRINTABLE);
}
}
public boolean isFsCaseSensitive() { public boolean isFsCaseSensitive() {
return fsCaseSensitive; return fsCaseSensitive;
} }
...@@ -348,23 +324,23 @@ public class JadxCLIArgs { ...@@ -348,23 +324,23 @@ public class JadxCLIArgs {
@Override @Override
public Set<RenameEnum> convert(String value) { public Set<RenameEnum> convert(String value) {
Set<RenameEnum> set = new HashSet<>(); if (value.equalsIgnoreCase("NONE")) {
return EnumSet.noneOf(RenameEnum.class);
}
if (value.equalsIgnoreCase("ALL")) { if (value.equalsIgnoreCase("ALL")) {
set.add(RenameEnum.CASE); return EnumSet.allOf(RenameEnum.class);
set.add(RenameEnum.VALID); }
set.add(RenameEnum.PRINTABLE); Set<RenameEnum> set = EnumSet.noneOf(RenameEnum.class);
} else if (!value.equalsIgnoreCase("NONE")) {
for (String s : value.split(",")) { for (String s : value.split(",")) {
try { try {
set.add(RenameEnum.valueOf(s.toUpperCase(Locale.ROOT))); set.add(RenameEnum.valueOf(s.toUpperCase(Locale.ROOT)));
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
String values = "'" + RenameEnum.CASE String values = Arrays.stream(RenameEnum.values())
+ "', '" + RenameEnum.VALID .map(v -> '\'' + v.name().toLowerCase(Locale.ROOT) + '\'')
+ "' and '" + RenameEnum.PRINTABLE + '\''; .collect(Collectors.joining(", "));
throw new IllegalArgumentException( throw new IllegalArgumentException(
s + " is unknown for parameter " + paramName '\'' + s + "' is unknown for parameter " + paramName
+ ", possible values are " + values.toLowerCase(Locale.ROOT)); + ", possible values are " + values);
}
} }
} }
return set; return set;
......
...@@ -42,8 +42,8 @@ public class RenameConverterTest { ...@@ -42,8 +42,8 @@ public class RenameConverterTest {
() -> converter.convert("wrong"), () -> converter.convert("wrong"),
"Expected convert() to throw, but it didn't"); "Expected convert() to throw, but it didn't");
assertEquals("wrong is unknown for parameter someParam, " assertEquals("'wrong' is unknown for parameter someParam, "
+ "possible values are 'case', 'valid' and 'printable'", + "possible values are 'case', 'valid', 'printable'",
thrown.getMessage()); thrown.getMessage());
} }
} }
...@@ -98,7 +98,7 @@ public final class ResourcesLoader { ...@@ -98,7 +98,7 @@ public final class ResourcesLoader {
return ResContainer.textResource(rf.getName(), content); return ResContainer.textResource(rf.getName(), content);
case ARSC: case ARSC:
return new ResTableParser().decodeFiles(inputStream); return new ResTableParser(jadxRef.getRoot()).decodeFiles(inputStream);
case IMG: case IMG:
return decodeImage(rf, inputStream); return decodeImage(rf, inputStream);
......
...@@ -32,7 +32,7 @@ import jadx.core.dex.nodes.InsnNode; ...@@ -32,7 +32,7 @@ import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.parser.FieldInitAttr; import jadx.core.dex.nodes.parser.FieldInitAttr;
import jadx.core.dex.nodes.parser.FieldInitAttr.InitType; import jadx.core.dex.nodes.parser.FieldInitAttr.InitType;
import jadx.core.utils.CodegenUtils; 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;
...@@ -104,7 +104,7 @@ public class ClassGen { ...@@ -104,7 +104,7 @@ public class ClassGen {
if (cls.contains(AFlag.DONT_GENERATE)) { if (cls.contains(AFlag.DONT_GENERATE)) {
return; return;
} }
CodegenUtils.addComments(code, cls); CodeGenUtils.addComments(code, cls);
insertDecompilationProblems(code, cls); insertDecompilationProblems(code, cls);
addClassDeclaration(code); addClassDeclaration(code);
addClassBody(code); addClassBody(code);
...@@ -299,7 +299,7 @@ public class ClassGen { ...@@ -299,7 +299,7 @@ public class ClassGen {
} }
code.add(';'); code.add(';');
} else { } else {
CodegenUtils.addComments(code, mth); CodeGenUtils.addComments(code, mth);
insertDecompilationProblems(code, mth); insertDecompilationProblems(code, mth);
boolean badCode = mth.contains(AFlag.INCONSISTENT_CODE); boolean badCode = mth.contains(AFlag.INCONSISTENT_CODE);
if (badCode && showInconsistentCode) { if (badCode && showInconsistentCode) {
...@@ -356,11 +356,12 @@ public class ClassGen { ...@@ -356,11 +356,12 @@ public class ClassGen {
if (f.contains(AFlag.DONT_GENERATE)) { if (f.contains(AFlag.DONT_GENERATE)) {
continue; continue;
} }
CodegenUtils.addComments(code, f); CodeGenUtils.addComments(code, f);
annotationGen.addForField(code, f); annotationGen.addForField(code, f);
if (f.getFieldInfo().isRenamed()) { if (f.getFieldInfo().isRenamed()) {
code.startLine("/* renamed from: ").add(f.getName()).add(" */"); code.newLine();
CodeGenUtils.addRenamedComment(code, f, f.getName());
} }
code.startLine(f.getAccessFlags().makeString()); code.startLine(f.getAccessFlags().makeString());
useType(code, f.getType()); useType(code, f.getType());
...@@ -620,7 +621,7 @@ public class ClassGen { ...@@ -620,7 +621,7 @@ public class ClassGen {
private void insertRenameInfo(CodeWriter code, ClassNode cls) { private void insertRenameInfo(CodeWriter code, ClassNode cls) {
ClassInfo classInfo = cls.getClassInfo(); ClassInfo classInfo = cls.getClassInfo();
if (classInfo.hasAlias()) { if (classInfo.hasAlias()) {
code.startLine("/* renamed from: ").add(classInfo.getType().getObject()).add(" */"); CodeGenUtils.addRenamedComment(code, cls, classInfo.getType().getObject());
} }
} }
......
...@@ -25,6 +25,7 @@ import jadx.core.dex.nodes.MethodNode; ...@@ -25,6 +25,7 @@ import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.trycatch.CatchAttr; import jadx.core.dex.trycatch.CatchAttr;
import jadx.core.dex.visitors.DepthTraversal; import jadx.core.dex.visitors.DepthTraversal;
import jadx.core.dex.visitors.FallbackModeVisitor; import jadx.core.dex.visitors.FallbackModeVisitor;
import jadx.core.utils.CodeGenUtils;
import jadx.core.utils.InsnUtils; import jadx.core.utils.InsnUtils;
import jadx.core.utils.Utils; import jadx.core.utils.Utils;
import jadx.core.utils.exceptions.CodegenException; import jadx.core.utils.exceptions.CodegenException;
...@@ -84,7 +85,7 @@ public class MethodGen { ...@@ -84,7 +85,7 @@ public class MethodGen {
} }
if (mth.getMethodInfo().isRenamed() && !ai.isConstructor()) { if (mth.getMethodInfo().isRenamed() && !ai.isConstructor()) {
code.startLine("/* renamed from: ").add(mth.getName()).add(" */"); CodeGenUtils.addRenamedComment(code, mth, mth.getName());
} }
code.startLineWithNum(mth.getSourceLine()); code.startLineWithNum(mth.getSourceLine());
code.add(ai.makeString()); code.add(ai.makeString());
......
...@@ -129,7 +129,7 @@ public class NameGen { ...@@ -129,7 +129,7 @@ public class NameGen {
if (NameMapper.isReserved(varName)) { if (NameMapper.isReserved(varName)) {
varName = varName + 'R'; varName = varName + 'R';
} }
if (!NameMapper.isValidIdentifier(varName)) { if (!NameMapper.isValidAndPrintable(varName)) {
varName = getFallbackName(var); varName = getFallbackName(var);
} }
return varName; return varName;
......
package jadx.core.deobf; package jadx.core.deobf;
import java.io.File;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
...@@ -64,11 +63,6 @@ public class Deobfuscator { ...@@ -64,11 +63,6 @@ public class Deobfuscator {
private int fldIndex = 0; private int fldIndex = 0;
private int mthIndex = 0; private int mthIndex = 0;
@Deprecated
public Deobfuscator(JadxArgs args, @NotNull List<DexNode> dexNodes, File deobfMapFile) {
this(args, dexNodes, deobfMapFile.toPath());
}
public Deobfuscator(JadxArgs args, @NotNull List<DexNode> dexNodes, Path deobfMapFile) { public Deobfuscator(JadxArgs args, @NotNull List<DexNode> dexNodes, Path deobfMapFile) {
this.args = args; this.args = args;
this.dexNodes = dexNodes; this.dexNodes = dexNodes;
...@@ -86,8 +80,20 @@ public class Deobfuscator { ...@@ -86,8 +80,20 @@ public class Deobfuscator {
initIndexes(); initIndexes();
} }
process(); process();
}
public void savePresets() {
deobfPresets.save(args.isDeobfuscationForceSave()); deobfPresets.save(args.isDeobfuscationForceSave());
clear(); }
public void clear() {
deobfPresets.clear();
clsMap.clear();
fldMap.clear();
mthMap.clear();
ovrd.clear();
ovrdMap.clear();
} }
private void initIndexes() { private void initIndexes() {
...@@ -148,16 +154,6 @@ public class Deobfuscator { ...@@ -148,16 +154,6 @@ public class Deobfuscator {
} }
} }
void clear() {
deobfPresets.clear();
clsMap.clear();
fldMap.clear();
mthMap.clear();
ovrd.clear();
ovrdMap.clear();
}
private void resolveOverriding(MethodNode mth) { private void resolveOverriding(MethodNode mth) {
Set<ClassNode> clsParents = new LinkedHashSet<>(); Set<ClassNode> clsParents = new LinkedHashSet<>();
collectClassHierarchy(mth.getParentClass(), clsParents); collectClassHierarchy(mth.getParentClass(), clsParents);
...@@ -241,7 +237,7 @@ public class Deobfuscator { ...@@ -241,7 +237,7 @@ public class Deobfuscator {
} else if (!clsInfo.isInner()) { } else if (!clsInfo.isInner()) {
// check if package renamed // check if package renamed
PackageNode pkgNode = getPackageNode(clsInfo.getPackage(), false); PackageNode pkgNode = getPackageNode(clsInfo.getPackage(), false);
if (pkgNode.hasAnyAlias()) { if (pkgNode != null && pkgNode.hasAnyAlias()) {
clsInfo.changePkg(pkgNode.getFullAlias()); clsInfo.changePkg(pkgNode.getFullAlias());
} }
} }
...@@ -259,7 +255,7 @@ public class Deobfuscator { ...@@ -259,7 +255,7 @@ public class Deobfuscator {
} }
} }
public void renameField(FieldNode field) { private void renameField(FieldNode field) {
FieldInfo fieldInfo = field.getFieldInfo(); FieldInfo fieldInfo = field.getFieldInfo();
String alias = getFieldAlias(field); String alias = getFieldAlias(field);
if (alias != null) { if (alias != null) {
...@@ -271,7 +267,7 @@ public class Deobfuscator { ...@@ -271,7 +267,7 @@ public class Deobfuscator {
field.getFieldInfo().setAlias(makeFieldAlias(field)); field.getFieldInfo().setAlias(makeFieldAlias(field));
} }
public void renameMethod(MethodNode mth) { private void renameMethod(MethodNode mth) {
String alias = getMethodAlias(mth); String alias = getMethodAlias(mth);
if (alias != null) { if (alias != null) {
mth.getMethodInfo().setAlias(alias); mth.getMethodInfo().setAlias(alias);
...@@ -408,7 +404,7 @@ public class Deobfuscator { ...@@ -408,7 +404,7 @@ public class Deobfuscator {
} else if (name.endsWith(".kt")) { } else if (name.endsWith(".kt")) {
name = name.substring(0, name.length() - ".kt".length()); name = name.substring(0, name.length() - ".kt".length());
} }
if (!NameMapper.isValidIdentifier(name) || !NameMapper.isAllCharsPrintable(name)) { if (!NameMapper.isValidAndPrintable(name)) {
return null; return null;
} }
for (DeobfClsInfo deobfClsInfo : clsMap.values()) { for (DeobfClsInfo deobfClsInfo : clsMap.values()) {
...@@ -500,8 +496,7 @@ public class Deobfuscator { ...@@ -500,8 +496,7 @@ public class Deobfuscator {
private boolean shouldRename(String s) { private boolean shouldRename(String s) {
int len = s.length(); int len = s.length();
return len < minLength || len > maxLength return len < minLength || len > maxLength;
|| !NameMapper.isValidIdentifier(s);
} }
private String prepareNamePart(String name) { private String prepareNamePart(String name) {
......
...@@ -87,6 +87,10 @@ public class NameMapper { ...@@ -87,6 +87,10 @@ public class NameMapper {
&& VALID_JAVA_FULL_IDENTIFIER.matcher(str).matches(); && VALID_JAVA_FULL_IDENTIFIER.matcher(str).matches();
} }
public static boolean isValidAndPrintable(String str) {
return isValidIdentifier(str) && isAllCharsPrintable(str);
}
public static boolean isValidIdentifierStart(int codePoint) { public static boolean isValidIdentifierStart(int codePoint) {
return Character.isJavaIdentifierStart(codePoint); return Character.isJavaIdentifierStart(codePoint);
} }
......
...@@ -17,6 +17,7 @@ import jadx.core.dex.attributes.nodes.LoopLabelAttr; ...@@ -17,6 +17,7 @@ import jadx.core.dex.attributes.nodes.LoopLabelAttr;
import jadx.core.dex.attributes.nodes.MethodInlineAttr; import jadx.core.dex.attributes.nodes.MethodInlineAttr;
import jadx.core.dex.attributes.nodes.PhiListAttr; import jadx.core.dex.attributes.nodes.PhiListAttr;
import jadx.core.dex.attributes.nodes.RegDebugInfoAttr; import jadx.core.dex.attributes.nodes.RegDebugInfoAttr;
import jadx.core.dex.attributes.nodes.RenameReasonAttr;
import jadx.core.dex.attributes.nodes.SourceFileAttr; import jadx.core.dex.attributes.nodes.SourceFileAttr;
import jadx.core.dex.nodes.parser.FieldInitAttr; import jadx.core.dex.nodes.parser.FieldInitAttr;
import jadx.core.dex.trycatch.CatchAttr; import jadx.core.dex.trycatch.CatchAttr;
...@@ -55,6 +56,7 @@ public class AType<T extends IAttribute> { ...@@ -55,6 +56,7 @@ public class AType<T extends IAttribute> {
public static final AType<DeclareVariablesAttr> DECLARE_VARIABLES = new AType<>(); public static final AType<DeclareVariablesAttr> DECLARE_VARIABLES = new AType<>();
public static final AType<LoopLabelAttr> LOOP_LABEL = new AType<>(); public static final AType<LoopLabelAttr> LOOP_LABEL = new AType<>();
public static final AType<IgnoreEdgeAttr> IGNORE_EDGE = new AType<>(); public static final AType<IgnoreEdgeAttr> IGNORE_EDGE = new AType<>();
public static final AType<RenameReasonAttr> RENAME_REASON = new AType<>();
// method // method
public static final AType<LocalVarsDebugInfoAttr> LOCAL_VARS_DEBUG_INFO = new AType<>(); public static final AType<LocalVarsDebugInfoAttr> LOCAL_VARS_DEBUG_INFO = new AType<>();
......
package jadx.core.dex.attributes.nodes;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.AttrNode;
import jadx.core.dex.attributes.IAttribute;
public class RenameReasonAttr implements IAttribute {
private String description;
public RenameReasonAttr() {
this.description = "";
}
public RenameReasonAttr(String description) {
this.description = description;
}
public RenameReasonAttr(AttrNode node) {
RenameReasonAttr renameReasonAttr = node.get(AType.RENAME_REASON);
if (renameReasonAttr != null) {
this.description = renameReasonAttr.description;
} else {
this.description = "";
}
}
public RenameReasonAttr(AttrNode node, boolean notValid, boolean notPrintable) {
this(node);
if (notValid) {
notValid();
}
if (notPrintable) {
notPrintable();
}
}
private RenameReasonAttr notValid() {
return append("not valid java name");
}
private RenameReasonAttr notPrintable() {
return append("contains not printable characters");
}
public RenameReasonAttr append(String reason) {
if (description.isEmpty()) {
description += reason;
} else {
description += " and " + reason;
}
return this;
}
public String getDescription() {
return description;
}
@Override
public AType<RenameReasonAttr> getType() {
return AType.RENAME_REASON;
}
@Override
public String toString() {
return "RENAME_REASON:" + description;
}
}
...@@ -87,7 +87,7 @@ public class RootNode { ...@@ -87,7 +87,7 @@ public class RootNode {
} }
try { try {
ResourceStorage resStorage = ResourcesLoader.decodeStream(arsc, (size, is) -> { ResourceStorage resStorage = ResourcesLoader.decodeStream(arsc, (size, is) -> {
ResTableParser parser = new ResTableParser(); ResTableParser parser = new ResTableParser(this);
parser.decode(is); parser.decode(is);
return parser.getResStorage(); return parser.getResStorage();
}); });
......
...@@ -212,6 +212,7 @@ public class ClassModifier extends AbstractVisitor { ...@@ -212,6 +212,7 @@ public class ClassModifier extends AbstractVisitor {
} }
private static boolean removeBridgeMethod(ClassNode cls, MethodNode mth) { private static boolean removeBridgeMethod(ClassNode cls, MethodNode mth) {
if (cls.root().getArgs().isRenameValid()) {
List<InsnNode> allInsns = BlockUtils.collectAllInsns(mth.getBasicBlocks()); List<InsnNode> allInsns = BlockUtils.collectAllInsns(mth.getBasicBlocks());
if (allInsns.size() == 1) { if (allInsns.size() == 1) {
InsnNode wrappedInsn = allInsns.get(0); InsnNode wrappedInsn = allInsns.get(0);
...@@ -225,6 +226,7 @@ public class ClassModifier extends AbstractVisitor { ...@@ -225,6 +226,7 @@ public class ClassModifier extends AbstractVisitor {
return true; return true;
} }
} }
}
return !isMethodUnique(cls, mth); return !isMethodUnique(cls, mth);
} }
......
...@@ -152,8 +152,8 @@ public class EnumVisitor extends AbstractVisitor { ...@@ -152,8 +152,8 @@ public class EnumVisitor extends AbstractVisitor {
String name = getConstString(cls.dex(), co.getArg(0)); String name = getConstString(cls.dex(), co.getArg(0));
if (name != null if (name != null
&& !fieldInfo.getAlias().equals(name) && !fieldInfo.getAlias().equals(name)
&& NameMapper.isValidIdentifier(name)) { && NameMapper.isValidAndPrintable(name)
// LOG.debug("Rename enum field: '{}' to '{}' in {}", fieldInfo.getName(), name, cls); && cls.root().getArgs().isRenameValid()) {
fieldInfo.setAlias(name); fieldInfo.setAlias(name);
} }
......
...@@ -12,6 +12,7 @@ import jadx.core.Consts; ...@@ -12,6 +12,7 @@ import jadx.core.Consts;
import jadx.core.deobf.Deobfuscator; import jadx.core.deobf.Deobfuscator;
import jadx.core.deobf.NameMapper; import jadx.core.deobf.NameMapper;
import jadx.core.dex.attributes.AFlag; import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.nodes.RenameReasonAttr;
import jadx.core.dex.info.AccessInfo; import jadx.core.dex.info.AccessInfo;
import jadx.core.dex.info.ClassInfo; import jadx.core.dex.info.ClassInfo;
import jadx.core.dex.info.FieldInfo; import jadx.core.dex.info.FieldInfo;
...@@ -24,8 +25,6 @@ import jadx.core.utils.files.InputFile; ...@@ -24,8 +25,6 @@ import jadx.core.utils.files.InputFile;
public class RenameVisitor extends AbstractVisitor { public class RenameVisitor extends AbstractVisitor {
private Deobfuscator deobfuscator;
@Override @Override
public void init(RootNode root) { public void init(RootNode root) {
List<DexNode> dexNodes = root.getDexNodes(); List<DexNode> dexNodes = root.getDexNodes();
...@@ -36,24 +35,29 @@ public class RenameVisitor extends AbstractVisitor { ...@@ -36,24 +35,29 @@ public class RenameVisitor extends AbstractVisitor {
Path inputFilePath = firstInputFile.getFile().toPath(); Path inputFilePath = firstInputFile.getFile().toPath();
String inputName = inputFilePath.getFileName().toString(); String inputName = inputFilePath.getFileName().toString();
inputName = inputName.substring(0, inputName.lastIndexOf('.')); String baseName = inputName.substring(0, inputName.lastIndexOf('.'));
Path deobfMapPath = inputFilePath.getParent().resolve(baseName + ".jobf");
Path deobfMapPath = inputFilePath.getParent().resolve(inputName + ".jobf");
JadxArgs args = root.getArgs(); JadxArgs args = root.getArgs();
deobfuscator = new Deobfuscator(args, dexNodes, deobfMapPath); Deobfuscator deobfuscator = new Deobfuscator(args, dexNodes, deobfMapPath);
boolean deobfuscationOn = args.isDeobfuscationOn(); if (args.isDeobfuscationOn()) {
if (deobfuscationOn) {
deobfuscator.execute(); deobfuscator.execute();
} }
checkClasses(root, args);
checkClasses(deobfuscator, root, args);
if (args.isDeobfuscationOn()) {
deobfuscator.savePresets();
deobfuscator.clear();
}
} }
private void checkClasses(RootNode root, JadxArgs args) { private static void checkClasses(Deobfuscator deobfuscator, RootNode root, JadxArgs args) {
List<ClassNode> classes = root.getClasses(true); List<ClassNode> classes = root.getClasses(true);
for (ClassNode cls : classes) { for (ClassNode cls : classes) {
checkClassName(cls, args); checkClassName(deobfuscator, cls, args);
checkFields(cls, args); checkFields(deobfuscator, cls, args);
checkMethods(cls, args); checkMethods(deobfuscator, cls, args);
} }
if (!args.isFsCaseSensitive() && args.isRenameCaseSensitive()) { if (!args.isFsCaseSensitive() && args.isRenameCaseSensitive()) {
Set<String> clsFullPaths = new HashSet<>(classes.size()); Set<String> clsFullPaths = new HashSet<>(classes.size());
...@@ -62,20 +66,22 @@ public class RenameVisitor extends AbstractVisitor { ...@@ -62,20 +66,22 @@ public class RenameVisitor extends AbstractVisitor {
if (!clsFullPaths.add(clsInfo.getAliasFullPath().toLowerCase())) { if (!clsFullPaths.add(clsInfo.getAliasFullPath().toLowerCase())) {
String newShortName = deobfuscator.getClsAlias(cls); String newShortName = deobfuscator.getClsAlias(cls);
clsInfo.changeShortName(newShortName); clsInfo.changeShortName(newShortName);
cls.addAttr(new RenameReasonAttr(cls).append("case insensitive filesystem"));
clsFullPaths.add(clsInfo.getAliasFullPath().toLowerCase()); clsFullPaths.add(clsInfo.getAliasFullPath().toLowerCase());
} }
} }
} }
processRootPackages(root, classes); processRootPackages(deobfuscator, root, classes);
} }
private void checkClassName(ClassNode cls, JadxArgs args) { private static void checkClassName(Deobfuscator deobfuscator, ClassNode cls, JadxArgs args) {
ClassInfo classInfo = cls.getClassInfo(); ClassInfo classInfo = cls.getClassInfo();
String clsName = classInfo.getAliasShortName(); String clsName = classInfo.getAliasShortName();
String newShortName = fixClsShortName(args, clsName); String newShortName = fixClsShortName(args, clsName);
if (!newShortName.equals(clsName)) { if (!newShortName.equals(clsName)) {
classInfo.changeShortName(newShortName); classInfo.changeShortName(newShortName);
cls.addAttr(new RenameReasonAttr(cls).append("invalid class name"));
} }
if (args.isRenameValid()) { if (args.isRenameValid()) {
if (classInfo.isInner()) { if (classInfo.isInner()) {
...@@ -84,6 +90,7 @@ public class RenameVisitor extends AbstractVisitor { ...@@ -84,6 +90,7 @@ public class RenameVisitor extends AbstractVisitor {
if (parentClass.getAliasShortName().equals(clsName)) { if (parentClass.getAliasShortName().equals(clsName)) {
String clsAlias = deobfuscator.getClsAlias(cls); String clsAlias = deobfuscator.getClsAlias(cls);
classInfo.changeShortName(clsAlias); classInfo.changeShortName(clsAlias);
cls.addAttr(new RenameReasonAttr(cls).append("collision with other inner class name"));
break; break;
} }
parentClass = parentClass.getParentClass(); parentClass = parentClass.getParentClass();
...@@ -91,12 +98,13 @@ public class RenameVisitor extends AbstractVisitor { ...@@ -91,12 +98,13 @@ public class RenameVisitor extends AbstractVisitor {
} else { } else {
if (classInfo.getAliasPkg().isEmpty()) { if (classInfo.getAliasPkg().isEmpty()) {
classInfo.changePkg(Consts.DEFAULT_PACKAGE_NAME); classInfo.changePkg(Consts.DEFAULT_PACKAGE_NAME);
cls.addAttr(new RenameReasonAttr(cls).append("default package"));
} }
} }
} }
} }
private String fixClsShortName(JadxArgs args, String clsName) { private static String fixClsShortName(JadxArgs args, String clsName) {
char firstChar = clsName.charAt(0); char firstChar = clsName.charAt(0);
boolean renameValid = args.isRenameValid(); boolean renameValid = args.isRenameValid();
if (Character.isDigit(firstChar) && renameValid) { if (Character.isDigit(firstChar) && renameValid) {
...@@ -114,25 +122,33 @@ public class RenameVisitor extends AbstractVisitor { ...@@ -114,25 +122,33 @@ public class RenameVisitor extends AbstractVisitor {
return cleanClsName; return cleanClsName;
} }
private void checkFields(ClassNode cls, JadxArgs args) { private static void checkFields(Deobfuscator deobfuscator, ClassNode cls, JadxArgs args) {
Set<String> names = new HashSet<>(); Set<String> names = new HashSet<>();
for (FieldNode field : cls.getFields()) { for (FieldNode field : cls.getFields()) {
FieldInfo fieldInfo = field.getFieldInfo(); FieldInfo fieldInfo = field.getFieldInfo();
String fieldName = fieldInfo.getAlias(); String fieldName = fieldInfo.getAlias();
if (!names.add(fieldName) boolean notUnique = !names.add(fieldName);
|| (args.isRenameValid() && !NameMapper.isValidIdentifier(fieldName)) boolean notValid = args.isRenameValid() && !NameMapper.isValidIdentifier(fieldName);
|| (args.isRenamePrintable() && !NameMapper.isAllCharsPrintable(fieldName))) { boolean notPrintable = args.isRenamePrintable() && !NameMapper.isAllCharsPrintable(fieldName);
if (notUnique || notValid || notPrintable) {
deobfuscator.forceRenameField(field); deobfuscator.forceRenameField(field);
field.addAttr(new RenameReasonAttr(field, notValid, notPrintable));
if (notUnique) {
field.addAttr(new RenameReasonAttr(field).append("collision with other field name"));
}
} }
} }
} }
private void checkMethods(ClassNode cls, JadxArgs args) { private static void checkMethods(Deobfuscator deobfuscator, ClassNode cls, JadxArgs args) {
for (MethodNode mth : cls.getMethods()) { for (MethodNode mth : cls.getMethods()) {
String alias = mth.getAlias(); String alias = mth.getAlias();
if (args.isRenameValid() && !NameMapper.isValidIdentifier(alias)
|| (args.isRenamePrintable() && !NameMapper.isAllCharsPrintable(alias))) { boolean notValid = args.isRenameValid() && !NameMapper.isValidIdentifier(alias);
boolean notPrintable = args.isRenamePrintable() && !NameMapper.isAllCharsPrintable(alias);
if (notValid || notPrintable) {
deobfuscator.forceRenameMethod(mth); deobfuscator.forceRenameMethod(mth);
mth.addAttr(new RenameReasonAttr(mth, notValid, notPrintable));
} }
} }
Set<String> names = new HashSet<>(); Set<String> names = new HashSet<>();
...@@ -147,11 +163,12 @@ public class RenameVisitor extends AbstractVisitor { ...@@ -147,11 +163,12 @@ public class RenameVisitor extends AbstractVisitor {
String signature = mth.getMethodInfo().makeSignature(true, false); String signature = mth.getMethodInfo().makeSignature(true, false);
if (!names.add(signature)) { if (!names.add(signature)) {
deobfuscator.forceRenameMethod(mth); deobfuscator.forceRenameMethod(mth);
mth.addAttr(new RenameReasonAttr("collision with other method in class"));
} }
} }
} }
private void processRootPackages(RootNode root, List<ClassNode> classes) { private static void processRootPackages(Deobfuscator deobfuscator, RootNode root, List<ClassNode> classes) {
Set<String> rootPkgs = collectRootPkgs(classes); Set<String> rootPkgs = collectRootPkgs(classes);
root.getCacheStorage().setRootPkgs(rootPkgs); root.getCacheStorage().setRootPkgs(rootPkgs);
...@@ -161,6 +178,7 @@ public class RenameVisitor extends AbstractVisitor { ...@@ -161,6 +178,7 @@ public class RenameVisitor extends AbstractVisitor {
for (FieldNode field : cls.getFields()) { for (FieldNode field : cls.getFields()) {
if (rootPkgs.contains(field.getAlias())) { if (rootPkgs.contains(field.getAlias())) {
deobfuscator.forceRenameField(field); deobfuscator.forceRenameField(field);
field.addAttr(new RenameReasonAttr("collision with root package name"));
} }
} }
} }
......
...@@ -147,7 +147,7 @@ public class DebugInfoApplyVisitor extends AbstractVisitor { ...@@ -147,7 +147,7 @@ public class DebugInfoApplyVisitor extends AbstractVisitor {
LOG.debug("Reject debug info of type: {} and name: '{}' for {}, mth: {}", type, varName, ssaVar, mth); LOG.debug("Reject debug info of type: {} and name: '{}' for {}, mth: {}", type, varName, ssaVar, mth);
} }
} else { } else {
if (NameMapper.isValidIdentifier(varName)) { if (NameMapper.isValidAndPrintable(varName)) {
ssaVar.setName(varName); ssaVar.setName(varName);
} }
detachDebugInfo(ssaVar.getAssign()); detachDebugInfo(ssaVar.getAssign());
......
...@@ -5,8 +5,9 @@ import java.util.List; ...@@ -5,8 +5,9 @@ import java.util.List;
import jadx.core.codegen.CodeWriter; import jadx.core.codegen.CodeWriter;
import jadx.core.dex.attributes.AType; import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.AttrNode; import jadx.core.dex.attributes.AttrNode;
import jadx.core.dex.attributes.nodes.RenameReasonAttr;
public class CodegenUtils { public class CodeGenUtils {
public static void addComments(CodeWriter code, AttrNode node) { public static void addComments(CodeWriter code, AttrNode node) {
List<String> comments = node.getAll(AType.COMMENTS); List<String> comments = node.getAll(AType.COMMENTS);
...@@ -15,4 +16,17 @@ public class CodegenUtils { ...@@ -15,4 +16,17 @@ public class CodegenUtils {
.forEach(comment -> code.startLine("/* ").addMultiLine(comment).add(" */")); .forEach(comment -> code.startLine("/* ").addMultiLine(comment).add(" */"));
} }
} }
public static void addRenamedComment(CodeWriter code, AttrNode node, String origName) {
code.startLine("/* renamed from: ").add(origName);
RenameReasonAttr renameReasonAttr = node.get(AType.RENAME_REASON);
if (renameReasonAttr != null) {
code.add(" reason: ");
code.add(renameReasonAttr.getDescription());
}
code.add(" */");
}
private CodeGenUtils() {
}
} }
...@@ -124,7 +124,8 @@ public class AndroidResourcesUtils { ...@@ -124,7 +124,8 @@ public class AndroidResourcesUtils {
FieldNode fieldNode = resFieldsMap.get(resource.getId()); FieldNode fieldNode = resFieldsMap.get(resource.getId());
if (fieldNode != null if (fieldNode != null
&& !fieldNode.getName().equals(resName) && !fieldNode.getName().equals(resName)
&& NameMapper.isValidIdentifier(resName)) { && NameMapper.isValidAndPrintable(resName)
&& resCls.root().getArgs().isRenameValid()) {
fieldNode.add(AFlag.DONT_RENAME); fieldNode.add(AFlag.DONT_RENAME);
fieldNode.getFieldInfo().setAlias(resName); fieldNode.getFieldInfo().setAlias(resName);
} }
......
...@@ -115,7 +115,7 @@ public class BinaryXMLParser extends CommonBinaryParser { ...@@ -115,7 +115,7 @@ public class BinaryXMLParser extends CommonBinaryParser {
break; break;
case RES_STRING_POOL_TYPE: case RES_STRING_POOL_TYPE:
strings = parseStringPoolNoType(); strings = parseStringPoolNoType();
valuesParser = new ValuesParser(strings, resNames); valuesParser = new ValuesParser(rootNode, strings, resNames);
break; break;
case RES_XML_RESOURCE_MAP_TYPE: case RES_XML_RESOURCE_MAP_TYPE:
parseResourceMap(); parseResourceMap();
......
...@@ -11,6 +11,9 @@ import org.slf4j.Logger; ...@@ -11,6 +11,9 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import jadx.core.codegen.CodeWriter; import jadx.core.codegen.CodeWriter;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.nodes.FieldNode;
import jadx.core.dex.nodes.RootNode;
import jadx.core.xmlgen.entry.EntryConfig; import jadx.core.xmlgen.entry.EntryConfig;
import jadx.core.xmlgen.entry.RawNamedValue; import jadx.core.xmlgen.entry.RawNamedValue;
import jadx.core.xmlgen.entry.RawValue; import jadx.core.xmlgen.entry.RawValue;
...@@ -50,8 +53,13 @@ public class ResTableParser extends CommonBinaryParser { ...@@ -50,8 +53,13 @@ public class ResTableParser extends CommonBinaryParser {
} }
} }
private String[] strings; private final RootNode root;
private final ResourceStorage resStorage = new ResourceStorage(); private final ResourceStorage resStorage = new ResourceStorage();
private String[] strings;
public ResTableParser(RootNode root) {
this.root = root;
}
public void decode(InputStream inputStream) throws IOException { public void decode(InputStream inputStream) throws IOException {
is = new ParserStream(inputStream); is = new ParserStream(inputStream);
...@@ -62,7 +70,7 @@ public class ResTableParser extends CommonBinaryParser { ...@@ -62,7 +70,7 @@ public class ResTableParser extends CommonBinaryParser {
public ResContainer decodeFiles(InputStream inputStream) throws IOException { public ResContainer decodeFiles(InputStream inputStream) throws IOException {
decode(inputStream); decode(inputStream);
ValuesParser vp = new ValuesParser(strings, resStorage.getResourcesNames()); ValuesParser vp = new ValuesParser(root, strings, resStorage.getResourcesNames());
ResXmlGen resGen = new ResXmlGen(resStorage, vp); ResXmlGen resGen = new ResXmlGen(resStorage, vp);
CodeWriter content = makeXmlDump(); CodeWriter content = makeXmlDump();
...@@ -222,8 +230,14 @@ public class ResTableParser extends CommonBinaryParser { ...@@ -222,8 +230,14 @@ public class ResTableParser extends CommonBinaryParser {
String typeName = pkg.getTypeStrings()[typeId - 1]; String typeName = pkg.getTypeStrings()[typeId - 1];
String keyName = pkg.getKeyStrings()[key]; String keyName = pkg.getKeyStrings()[key];
if (keyName.isEmpty()) { if (keyName.isEmpty()) {
FieldNode constField = root.getConstValues().getGlobalConstFields().get(resRef);
if (constField != null) {
keyName = constField.getName();
constField.add(AFlag.DONT_RENAME);
} else {
keyName = "RES_" + resRef; // autogenerate key name keyName = "RES_" + resRef; // autogenerate key name
} }
}
ResourceEntry ri = new ResourceEntry(resRef, pkg.getName(), typeName, keyName); ResourceEntry ri = new ResourceEntry(resRef, pkg.getName(), typeName, keyName);
ri.setConfig(config); ri.setConfig(config);
......
...@@ -13,6 +13,7 @@ import org.jetbrains.annotations.Nullable; ...@@ -13,6 +13,7 @@ import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import jadx.core.dex.nodes.RootNode;
import jadx.core.xmlgen.ParserConstants; import jadx.core.xmlgen.ParserConstants;
import jadx.core.xmlgen.ResTableParser; import jadx.core.xmlgen.ResTableParser;
...@@ -25,22 +26,22 @@ public class ValuesParser extends ParserConstants { ...@@ -25,22 +26,22 @@ public class ValuesParser extends ParserConstants {
private final String[] strings; private final String[] strings;
private final Map<Integer, String> resMap; private final Map<Integer, String> resMap;
public ValuesParser(String[] strings, Map<Integer, String> resMap) { public ValuesParser(RootNode root, String[] strings, Map<Integer, String> resMap) {
this.strings = strings; this.strings = strings;
this.resMap = resMap; this.resMap = resMap;
if (androidStrings == null && androidResMap == null) { if (androidStrings == null && androidResMap == null) {
try { try {
decodeAndroid(); decodeAndroid(root);
} catch (Exception e) { } catch (Exception e) {
LOG.error("Failed to decode Android Resource file", e); LOG.error("Failed to decode Android Resource file", e);
} }
} }
} }
private static void decodeAndroid() throws IOException { private static void decodeAndroid(RootNode root) throws IOException {
InputStream inputStream = new BufferedInputStream(ValuesParser.class.getResourceAsStream("/resources.arsc")); InputStream inputStream = new BufferedInputStream(ValuesParser.class.getResourceAsStream("/resources.arsc"));
ResTableParser androidParser = new ResTableParser(); ResTableParser androidParser = new ResTableParser(root);
androidParser.decode(inputStream); androidParser.decode(inputStream);
androidStrings = androidParser.getStrings(); androidStrings = androidParser.getStrings();
androidResMap = androidParser.getResStorage().getResourcesNames(); androidResMap = androidParser.getResStorage().getResourcesNames();
......
...@@ -278,6 +278,14 @@ public class JadxSettings extends JadxCLIArgs { ...@@ -278,6 +278,14 @@ public class JadxSettings extends JadxCLIArgs {
this.deobfuscationUseSourceNameAsAlias = deobfuscationUseSourceNameAsAlias; this.deobfuscationUseSourceNameAsAlias = deobfuscationUseSourceNameAsAlias;
} }
public void updateRenameFlag(JadxArgs.RenameEnum flag, boolean enabled) {
if (enabled) {
renameFlags.add(flag);
} else {
renameFlags.remove(flag);
}
}
public void setEscapeUnicode(boolean escapeUnicode) { public void setEscapeUnicode(boolean escapeUnicode) {
this.escapeUnicode = escapeUnicode; this.escapeUnicode = escapeUnicode;
} }
......
...@@ -14,6 +14,7 @@ import org.slf4j.LoggerFactory; ...@@ -14,6 +14,7 @@ import org.slf4j.LoggerFactory;
import say.swing.JFontChooser; import say.swing.JFontChooser;
import jadx.api.JadxArgs;
import jadx.gui.ui.MainWindow; import jadx.gui.ui.MainWindow;
import jadx.gui.ui.codearea.EditorTheme; import jadx.gui.ui.codearea.EditorTheme;
import jadx.gui.utils.FontUtils; import jadx.gui.utils.FontUtils;
...@@ -184,21 +185,21 @@ public class JadxSettingsWindow extends JDialog { ...@@ -184,21 +185,21 @@ public class JadxSettingsWindow extends JDialog {
JCheckBox renameCaseSensitive = new JCheckBox(); JCheckBox renameCaseSensitive = new JCheckBox();
renameCaseSensitive.setSelected(settings.isRenameCaseSensitive()); renameCaseSensitive.setSelected(settings.isRenameCaseSensitive());
renameCaseSensitive.addItemListener(e -> { renameCaseSensitive.addItemListener(e -> {
settings.setRenameCaseSensitive(e.getStateChange() == ItemEvent.SELECTED); settings.updateRenameFlag(JadxArgs.RenameEnum.CASE, e.getStateChange() == ItemEvent.SELECTED);
needReload(); needReload();
}); });
JCheckBox renameValid = new JCheckBox(); JCheckBox renameValid = new JCheckBox();
renameValid.setSelected(settings.isRenameValid()); renameValid.setSelected(settings.isRenameValid());
renameValid.addItemListener(e -> { renameValid.addItemListener(e -> {
settings.setRenameValid(e.getStateChange() == ItemEvent.SELECTED); settings.updateRenameFlag(JadxArgs.RenameEnum.VALID, e.getStateChange() == ItemEvent.SELECTED);
needReload(); needReload();
}); });
JCheckBox renamePrintable = new JCheckBox(); JCheckBox renamePrintable = new JCheckBox();
renamePrintable.setSelected(settings.isRenamePrintable()); renamePrintable.setSelected(settings.isRenamePrintable());
renamePrintable.addItemListener(e -> { renamePrintable.addItemListener(e -> {
settings.setRenamePrintable(e.getStateChange() == ItemEvent.SELECTED); settings.updateRenameFlag(JadxArgs.RenameEnum.PRINTABLE, e.getStateChange() == ItemEvent.SELECTED);
needReload(); needReload();
}); });
......
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