refactor(deobf): split deobfuscation conditions (#2040)
This commit is contained in:
@@ -120,6 +120,7 @@ options:
|
||||
--deobf - activate deobfuscation
|
||||
--deobf-min - min length of name, renamed if shorter, default: 3
|
||||
--deobf-max - max length of name, renamed if longer, default: 64
|
||||
--deobf-whitelist - space separated list of classes (full name) and packages (ends with '.*') to exclude from deobfuscation, default: android.support.v4.* android.support.v7.* android.support.v4.os.* android.support.annotation.Px androidx.core.os.* androidx.annotation.Px
|
||||
--deobf-cfg-file - deobfuscation mappings file used for JADX auto-generated names (in the JOBF file format), default: same dir and name as input file with '.jobf' extension
|
||||
--deobf-cfg-file-mode - set mode for handling the JADX auto-generated names' deobfuscation map file:
|
||||
'read' - read if found, don't save (default)
|
||||
@@ -131,7 +132,6 @@ options:
|
||||
'auto' - automatically select best name (default)
|
||||
'resources' - use resources names
|
||||
'code' - use R class fields names
|
||||
--deobf-whitelist - list of ':' separated packages (suffix '.*') and class names that will not be deobfuscated
|
||||
--use-kotlin-methods-for-var-names - use kotlin intrinsic methods to rename variables, values: disable, apply, apply-and-hide, default: apply
|
||||
--rename-flags - fix options (comma-separated list of):
|
||||
'case' - fix case sensitivity issues (according to --fs-case-sensitive option),
|
||||
|
||||
@@ -2,6 +2,7 @@ package jadx.cli;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -27,6 +28,7 @@ import jadx.api.args.GeneratedRenamesMappingFileMode;
|
||||
import jadx.api.args.IntegerFormat;
|
||||
import jadx.api.args.ResourceNameSource;
|
||||
import jadx.api.args.UserRenamesMappingsMode;
|
||||
import jadx.core.deobf.conditions.DeobfWhitelist;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
import jadx.core.utils.files.FileUtils;
|
||||
|
||||
@@ -137,9 +139,11 @@ public class JadxCLIArgs {
|
||||
@Parameter(names = { "--deobf-max" }, description = "max length of name, renamed if longer")
|
||||
protected int deobfuscationMaxLength = 64;
|
||||
|
||||
@Parameter(names = { "--deobf-whitelist}" }, description = "debfucation whitelist")
|
||||
protected String deobfuscationWhitelist =
|
||||
"android.support.v4.*:android.support.v7.*:android.support.v4.os.*:android.support.annotation.Px:androidx.core.os.*:androidx.annotation.Px";
|
||||
@Parameter(
|
||||
names = { "--deobf-whitelist" },
|
||||
description = "space separated list of classes (full name) and packages (ends with '.*') to exclude from deobfuscation"
|
||||
)
|
||||
protected String deobfuscationWhitelistStr = DeobfWhitelist.DEFAULT_STR;
|
||||
|
||||
@Parameter(
|
||||
names = { "--deobf-cfg-file" },
|
||||
@@ -320,7 +324,7 @@ public class JadxCLIArgs {
|
||||
args.setGeneratedRenamesMappingFileMode(generatedRenamesMappingFileMode);
|
||||
args.setDeobfuscationMinLength(deobfuscationMinLength);
|
||||
args.setDeobfuscationMaxLength(deobfuscationMaxLength);
|
||||
args.setDeobfuscationWhitelist(deobfuscationWhitelist);
|
||||
args.setDeobfuscationWhitelist(Arrays.asList(deobfuscationWhitelistStr.split(" ")));
|
||||
args.setUseSourceNameAsClassAlias(deobfuscationUseSourceNameAsAlias);
|
||||
args.setUseKotlinMethodsForVarNames(useKotlinMethodsForVarNames);
|
||||
args.setResourceNameSource(resourceNameSource);
|
||||
@@ -448,8 +452,8 @@ public class JadxCLIArgs {
|
||||
return deobfuscationMaxLength;
|
||||
}
|
||||
|
||||
public String getDeobfuscationWhitelist() {
|
||||
return deobfuscationWhitelist;
|
||||
public String getDeobfuscationWhitelistStr() {
|
||||
return deobfuscationWhitelistStr;
|
||||
}
|
||||
|
||||
public String getGeneratedRenamesMappingFile() {
|
||||
|
||||
@@ -32,7 +32,8 @@ import jadx.api.plugins.loader.JadxPluginLoader;
|
||||
import jadx.api.usage.IUsageInfoCache;
|
||||
import jadx.api.usage.impl.InMemoryUsageInfoCache;
|
||||
import jadx.core.deobf.DeobfAliasProvider;
|
||||
import jadx.core.deobf.DeobfCondition;
|
||||
import jadx.core.deobf.conditions.DeobfWhitelist;
|
||||
import jadx.core.deobf.conditions.JadxRenameConditions;
|
||||
import jadx.core.plugins.PluginContext;
|
||||
import jadx.core.utils.files.FileUtils;
|
||||
|
||||
@@ -103,7 +104,10 @@ public class JadxArgs implements Closeable {
|
||||
private int deobfuscationMinLength = 0;
|
||||
private int deobfuscationMaxLength = Integer.MAX_VALUE;
|
||||
|
||||
private String deobfuscationWhitelist = "";
|
||||
/**
|
||||
* List of classes and packages (ends with '.*') to exclude from deobfuscation
|
||||
*/
|
||||
private List<String> deobfuscationWhitelist = DeobfWhitelist.DEFAULT_LIST;
|
||||
|
||||
/**
|
||||
* Nodes alias provider for deobfuscator and rename visitor
|
||||
@@ -113,7 +117,7 @@ public class JadxArgs implements Closeable {
|
||||
/**
|
||||
* Condition to rename node in deobfuscator
|
||||
*/
|
||||
private IRenameCondition renameCondition = new DeobfCondition();
|
||||
private IRenameCondition renameCondition = JadxRenameConditions.buildDefault();
|
||||
|
||||
private boolean escapeUnicode = false;
|
||||
private boolean replaceConsts = true;
|
||||
@@ -436,11 +440,11 @@ public class JadxArgs implements Closeable {
|
||||
this.deobfuscationMaxLength = deobfuscationMaxLength;
|
||||
}
|
||||
|
||||
public String getDeobfuscationWhitelist() {
|
||||
public List<String> getDeobfuscationWhitelist() {
|
||||
return this.deobfuscationWhitelist;
|
||||
}
|
||||
|
||||
public void setDeobfuscationWhitelist(String deobfuscationWhitelist) {
|
||||
public void setDeobfuscationWhitelist(List<String> deobfuscationWhitelist) {
|
||||
this.deobfuscationWhitelist = deobfuscationWhitelist;
|
||||
}
|
||||
|
||||
@@ -678,7 +682,7 @@ public class JadxArgs implements Closeable {
|
||||
public String makeCodeArgsHash(@Nullable JadxDecompiler decompiler) {
|
||||
String argStr = "args:" + decompilationMode + useImports + showInconsistentCode
|
||||
+ inlineAnonymousClasses + inlineMethods + moveInnerClasses + allowInlineKotlinLambda
|
||||
+ deobfuscationOn + deobfuscationMinLength + deobfuscationMaxLength
|
||||
+ deobfuscationOn + deobfuscationMinLength + deobfuscationMaxLength + deobfuscationWhitelist
|
||||
+ resourceNameSource
|
||||
+ useKotlinMethodsForVarNames
|
||||
+ insertDebugLines + extractFinally
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package jadx.api.deobf;
|
||||
|
||||
import jadx.api.deobf.impl.CombineDeobfConditions;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.nodes.PackageNode;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
|
||||
/**
|
||||
* Utility interface to simplify merging several rename conditions to build {@link IRenameCondition}
|
||||
* instance with {@link CombineDeobfConditions#combine(IDeobfCondition...)}.
|
||||
*/
|
||||
public interface IDeobfCondition {
|
||||
|
||||
enum Action {
|
||||
NO_ACTION,
|
||||
FORCE_RENAME,
|
||||
FORBID_RENAME,
|
||||
}
|
||||
|
||||
void init(RootNode root);
|
||||
|
||||
Action check(PackageNode pkg);
|
||||
|
||||
Action check(ClassNode cls);
|
||||
|
||||
Action check(FieldNode fld);
|
||||
|
||||
Action check(MethodNode mth);
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package jadx.api.deobf.impl;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import jadx.api.deobf.IDeobfCondition;
|
||||
import jadx.api.deobf.IRenameCondition;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.nodes.PackageNode;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
|
||||
public class CombineDeobfConditions implements IRenameCondition {
|
||||
|
||||
public static IRenameCondition combine(List<IDeobfCondition> conditions) {
|
||||
return new CombineDeobfConditions(conditions);
|
||||
}
|
||||
|
||||
public static IRenameCondition combine(IDeobfCondition... conditions) {
|
||||
return new CombineDeobfConditions(Arrays.asList(conditions));
|
||||
}
|
||||
|
||||
private final List<IDeobfCondition> conditions;
|
||||
|
||||
private CombineDeobfConditions(List<IDeobfCondition> conditions) {
|
||||
if (conditions == null || conditions.isEmpty()) {
|
||||
throw new IllegalArgumentException("Conditions list can't be empty");
|
||||
}
|
||||
this.conditions = conditions;
|
||||
}
|
||||
|
||||
private boolean combineFunc(Function<IDeobfCondition, IDeobfCondition.Action> check) {
|
||||
for (IDeobfCondition c : conditions) {
|
||||
switch (check.apply(c)) {
|
||||
case NO_ACTION:
|
||||
// ignore
|
||||
break;
|
||||
case FORCE_RENAME:
|
||||
return true;
|
||||
case FORBID_RENAME:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(RootNode root) {
|
||||
conditions.forEach(c -> c.init(root));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRename(PackageNode pkg) {
|
||||
return combineFunc(c -> c.check(pkg));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRename(ClassNode cls) {
|
||||
return combineFunc(c -> c.check(cls));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRename(FieldNode fld) {
|
||||
return combineFunc(c -> c.check(fld));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRename(MethodNode mth) {
|
||||
return combineFunc(c -> c.check(mth));
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
package jadx.core.deobf;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import jadx.api.JadxArgs;
|
||||
import jadx.api.deobf.IRenameCondition;
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.nodes.PackageNode;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
|
||||
public class DeobfCondition implements IRenameCondition {
|
||||
|
||||
private int minLength;
|
||||
private int maxLength;
|
||||
|
||||
private final Set<String> avoidClsNames = new HashSet<>();
|
||||
|
||||
@Override
|
||||
public void init(RootNode root) {
|
||||
JadxArgs args = root.getArgs();
|
||||
this.minLength = args.getDeobfuscationMinLength();
|
||||
this.maxLength = args.getDeobfuscationMaxLength();
|
||||
|
||||
for (PackageNode pkg : root.getPackages()) {
|
||||
avoidClsNames.add(pkg.getPkgInfo().getName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRename(PackageNode pkg) {
|
||||
String name = pkg.getAliasPkgInfo().getName();
|
||||
return shouldRename(name)
|
||||
&& !pkg.hasAlias()
|
||||
&& !TldHelper.contains(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRename(ClassNode cls) {
|
||||
if (cls.contains(AFlag.DONT_RENAME)
|
||||
|| cls.getClassInfo().hasAlias()
|
||||
|| isR(cls.getTopParentClass())) {
|
||||
return false;
|
||||
}
|
||||
String name = cls.getAlias();
|
||||
if (avoidClsNames.contains(name)) {
|
||||
return true;
|
||||
}
|
||||
return shouldRename(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRename(FieldNode fld) {
|
||||
return shouldRename(fld.getAlias())
|
||||
&& !fld.contains(AFlag.DONT_RENAME)
|
||||
&& !fld.getFieldInfo().hasAlias()
|
||||
&& !isR(fld.getTopParentClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRename(MethodNode mth) {
|
||||
return shouldRename(mth.getAlias())
|
||||
&& !mth.contains(AFlag.DONT_RENAME)
|
||||
&& !mth.getMethodInfo().hasAlias()
|
||||
&& !mth.isConstructor();
|
||||
}
|
||||
|
||||
private boolean shouldRename(String s) {
|
||||
int len = s.length();
|
||||
return len < minLength || len > maxLength;
|
||||
}
|
||||
|
||||
private static boolean isR(ClassNode cls) {
|
||||
if (cls.contains(AFlag.ANDROID_R_CLASS)) {
|
||||
return true;
|
||||
}
|
||||
if (!cls.getClassInfo().getShortName().equals("R")) {
|
||||
return false;
|
||||
}
|
||||
if (!cls.getMethods().isEmpty() || !cls.getFields().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
for (ClassNode inner : cls.getInnerClasses()) {
|
||||
for (MethodNode m : inner.getMethods()) {
|
||||
if (!m.getMethodInfo().isConstructor() && !m.getMethodInfo().isClassInit()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (FieldNode field : cls.getFields()) {
|
||||
ArgType type = field.getType();
|
||||
if (type != ArgType.INT && (!type.isArray() || type.getArrayElement() != ArgType.INT)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
cls.add(AFlag.ANDROID_R_CLASS);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
package jadx.core.deobf;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import jadx.api.JadxArgs;
|
||||
import jadx.api.deobf.IRenameCondition;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.nodes.PackageNode;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
|
||||
public class DeobfWhitelist implements IRenameCondition {
|
||||
|
||||
private static DeobfWhitelist whitelist = null;
|
||||
|
||||
private final List<String> packages = new ArrayList<>();
|
||||
|
||||
private final List<String> classes = new ArrayList<>();
|
||||
|
||||
public static DeobfWhitelist getWhitelist() {
|
||||
if (whitelist == null) {
|
||||
whitelist = new DeobfWhitelist();
|
||||
}
|
||||
return whitelist;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(RootNode root) {
|
||||
packages.clear();
|
||||
classes.clear();
|
||||
JadxArgs args = root.getArgs();
|
||||
String whitelistStr = args.getDeobfuscationWhitelist();
|
||||
String[] whitelisteItems = whitelistStr.split(":");
|
||||
for (String whitelistItem : whitelisteItems) {
|
||||
if (!whitelistItem.isEmpty()) {
|
||||
if (whitelistItem.endsWith(".*")) {
|
||||
packages.add(whitelistItem.substring(0, whitelistItem.length() - 2));
|
||||
} else {
|
||||
classes.add(whitelistItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRename(PackageNode pkg) {
|
||||
String fullname = pkg.getPkgInfo().getFullName();
|
||||
for (String p : packages) {
|
||||
if (fullname.equals(p)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRename(ClassNode cls) {
|
||||
String fullname = cls.getFullName();
|
||||
for (String c : classes) {
|
||||
if (fullname.equals(c)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRename(FieldNode fld) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRename(MethodNode mth) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -19,8 +19,6 @@ public class DeobfuscatorVisitor extends AbstractVisitor {
|
||||
if (!args.isDeobfuscationOn()) {
|
||||
return;
|
||||
}
|
||||
DeobfWhitelist whitelist = DeobfWhitelist.getWhitelist();
|
||||
whitelist.init(root);
|
||||
DeobfPresets mapping = DeobfPresets.build(root);
|
||||
if (args.getGeneratedRenamesMappingFileMode().shouldRead()) {
|
||||
if (mapping.load()) {
|
||||
@@ -34,11 +32,9 @@ public class DeobfuscatorVisitor extends AbstractVisitor {
|
||||
}
|
||||
|
||||
public static void process(RootNode root, IRenameCondition renameCondition, IAliasProvider aliasProvider) {
|
||||
DeobfWhitelist whitelist = DeobfWhitelist.getWhitelist();
|
||||
|
||||
boolean pkgUpdated = false;
|
||||
for (PackageNode pkg : root.getPackages()) {
|
||||
if (whitelist.shouldRename(pkg) && renameCondition.shouldRename(pkg)) {
|
||||
if (renameCondition.shouldRename(pkg)) {
|
||||
String alias = aliasProvider.forPackage(pkg);
|
||||
if (alias != null) {
|
||||
pkg.rename(alias, false);
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
package jadx.core.deobf;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
/**
|
||||
* Provides a list of all top level domains with 3 characters and less,
|
||||
* so we can exclude them from deobfuscation.
|
||||
*/
|
||||
public class TldHelper {
|
||||
|
||||
private static final Set<String> TLD_SET = loadTldFile();
|
||||
|
||||
private static Set<String> loadTldFile() {
|
||||
Set<String> tldNames = new HashSet<>();
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(TldHelper.class.getResourceAsStream("tld_3.txt")))) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
line = line.trim();
|
||||
if (!line.startsWith("#") && !line.isEmpty()) {
|
||||
tldNames.add(line);
|
||||
}
|
||||
}
|
||||
return tldNames;
|
||||
} catch (Exception e) {
|
||||
throw new JadxRuntimeException("Failed to load top level domain list tld_3.txt", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean contains(String name) {
|
||||
return TLD_SET.contains(name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package jadx.core.deobf.conditions;
|
||||
|
||||
import jadx.api.deobf.IDeobfCondition;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.nodes.PackageNode;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
|
||||
public abstract class AbstractDeobfCondition implements IDeobfCondition {
|
||||
|
||||
@Override
|
||||
public void init(RootNode root) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action check(PackageNode pkg) {
|
||||
return Action.NO_ACTION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action check(ClassNode cls) {
|
||||
return Action.NO_ACTION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action check(FieldNode fld) {
|
||||
return Action.NO_ACTION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action check(MethodNode mth) {
|
||||
return Action.NO_ACTION;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package jadx.core.deobf.conditions;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.PackageNode;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
|
||||
public class AvoidClsAndPkgNamesCollision extends AbstractDeobfCondition {
|
||||
|
||||
private final Set<String> avoidClsNames = new HashSet<>();
|
||||
|
||||
@Override
|
||||
public void init(RootNode root) {
|
||||
avoidClsNames.clear();
|
||||
for (PackageNode pkg : root.getPackages()) {
|
||||
avoidClsNames.add(pkg.getName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action check(ClassNode cls) {
|
||||
if (avoidClsNames.contains(cls.getAlias())) {
|
||||
return Action.FORCE_RENAME;
|
||||
}
|
||||
return Action.NO_ACTION;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package jadx.core.deobf.conditions;
|
||||
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.nodes.PackageNode;
|
||||
|
||||
/**
|
||||
* Disable deobfuscation for nodes:
|
||||
* - with 'DONT_RENAME' flag
|
||||
* - already renamed
|
||||
*/
|
||||
public class BaseDeobfCondition extends AbstractDeobfCondition {
|
||||
|
||||
@Override
|
||||
public Action check(PackageNode pkg) {
|
||||
if (pkg.contains(AFlag.DONT_RENAME) || pkg.hasAlias()) {
|
||||
return Action.FORBID_RENAME;
|
||||
}
|
||||
return Action.NO_ACTION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action check(ClassNode cls) {
|
||||
if (cls.contains(AFlag.DONT_RENAME) || cls.getClassInfo().hasAlias()) {
|
||||
return Action.FORBID_RENAME;
|
||||
}
|
||||
return Action.NO_ACTION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action check(MethodNode mth) {
|
||||
if (mth.contains(AFlag.DONT_RENAME)
|
||||
|| mth.getMethodInfo().hasAlias()
|
||||
|| mth.isConstructor()) {
|
||||
return Action.FORBID_RENAME;
|
||||
}
|
||||
return Action.NO_ACTION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action check(FieldNode fld) {
|
||||
if (fld.contains(AFlag.DONT_RENAME) || fld.getFieldInfo().hasAlias()) {
|
||||
return Action.FORBID_RENAME;
|
||||
}
|
||||
return Action.NO_ACTION;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package jadx.core.deobf.conditions;
|
||||
|
||||
import jadx.api.JadxArgs;
|
||||
import jadx.api.deobf.IDeobfCondition;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.nodes.PackageNode;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
|
||||
public class DeobfLengthCondition implements IDeobfCondition {
|
||||
|
||||
private int minLength;
|
||||
private int maxLength;
|
||||
|
||||
@Override
|
||||
public void init(RootNode root) {
|
||||
JadxArgs args = root.getArgs();
|
||||
this.minLength = args.getDeobfuscationMinLength();
|
||||
this.maxLength = args.getDeobfuscationMaxLength();
|
||||
}
|
||||
|
||||
private Action checkName(String s) {
|
||||
int len = s.length();
|
||||
if (len < minLength || len > maxLength) {
|
||||
return Action.FORCE_RENAME;
|
||||
}
|
||||
return Action.NO_ACTION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action check(PackageNode pkg) {
|
||||
return checkName(pkg.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action check(ClassNode cls) {
|
||||
return checkName(cls.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action check(FieldNode fld) {
|
||||
return checkName(fld.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action check(MethodNode mth) {
|
||||
return checkName(mth.getName());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package jadx.core.deobf.conditions;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.PackageNode;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
import jadx.core.utils.Utils;
|
||||
|
||||
public class DeobfWhitelist extends AbstractDeobfCondition {
|
||||
|
||||
public static final List<String> DEFAULT_LIST = Arrays.asList(
|
||||
"android.support.v4.*",
|
||||
"android.support.v7.*",
|
||||
"android.support.v4.os.*",
|
||||
"android.support.annotation.Px",
|
||||
"androidx.core.os.*",
|
||||
"androidx.annotation.Px");
|
||||
|
||||
public static final String DEFAULT_STR = Utils.listToString(DEFAULT_LIST, " ");
|
||||
|
||||
private final Set<String> packages = new HashSet<>();
|
||||
private final Set<String> classes = new HashSet<>();
|
||||
|
||||
@Override
|
||||
public void init(RootNode root) {
|
||||
packages.clear();
|
||||
classes.clear();
|
||||
for (String whitelistItem : root.getArgs().getDeobfuscationWhitelist()) {
|
||||
if (!whitelistItem.isEmpty()) {
|
||||
if (whitelistItem.endsWith(".*")) {
|
||||
packages.add(whitelistItem.substring(0, whitelistItem.length() - 2));
|
||||
} else {
|
||||
classes.add(whitelistItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action check(PackageNode pkg) {
|
||||
if (packages.contains(pkg.getPkgInfo().getFullName())) {
|
||||
return Action.FORBID_RENAME;
|
||||
}
|
||||
return Action.NO_ACTION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action check(ClassNode cls) {
|
||||
if (classes.contains(cls.getClassInfo().getFullName())) {
|
||||
return Action.FORBID_RENAME;
|
||||
}
|
||||
return Action.NO_ACTION;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package jadx.core.deobf.conditions;
|
||||
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
|
||||
public class ExcludeAndroidRClass extends AbstractDeobfCondition {
|
||||
|
||||
@Override
|
||||
public Action check(ClassNode cls) {
|
||||
if (isR(cls.getTopParentClass())) {
|
||||
return Action.FORBID_RENAME;
|
||||
}
|
||||
return Action.NO_ACTION;
|
||||
}
|
||||
|
||||
private static boolean isR(ClassNode cls) {
|
||||
if (cls.contains(AFlag.ANDROID_R_CLASS)) {
|
||||
return true;
|
||||
}
|
||||
if (!cls.getClassInfo().getShortName().equals("R")) {
|
||||
return false;
|
||||
}
|
||||
if (!cls.getMethods().isEmpty() || !cls.getFields().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
for (ClassNode inner : cls.getInnerClasses()) {
|
||||
for (MethodNode m : inner.getMethods()) {
|
||||
if (!m.getMethodInfo().isConstructor() && !m.getMethodInfo().isClassInit()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (FieldNode field : cls.getFields()) {
|
||||
ArgType type = field.getType();
|
||||
if (type != ArgType.INT && (!type.isArray() || type.getArrayElement() != ArgType.INT)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
cls.add(AFlag.ANDROID_R_CLASS);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package jadx.core.deobf.conditions;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import jadx.core.dex.nodes.PackageNode;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
/**
|
||||
* Provides a list of all top level domains with 3 characters and less,
|
||||
* so we can exclude them from deobfuscation.
|
||||
*/
|
||||
public class ExcludePackageWithTLDNames extends AbstractDeobfCondition {
|
||||
|
||||
/**
|
||||
* Lazy load TLD set
|
||||
*/
|
||||
private static class TldHolder {
|
||||
private static final Set<String> TLD_SET = loadTldFile();
|
||||
}
|
||||
|
||||
private static Set<String> loadTldFile() {
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(TldHolder.class.getResourceAsStream("tld_3.txt")))) {
|
||||
return reader.lines()
|
||||
.map(String::trim)
|
||||
.filter(line -> !line.startsWith("#") && !line.isEmpty())
|
||||
.collect(Collectors.toSet());
|
||||
} catch (Exception e) {
|
||||
throw new JadxRuntimeException("Failed to load top level domain list file: tld_3.txt", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action check(PackageNode pkg) {
|
||||
if (TldHolder.TLD_SET.contains(pkg.getName())) {
|
||||
return Action.FORBID_RENAME;
|
||||
}
|
||||
return Action.NO_ACTION;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package jadx.core.deobf.conditions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import jadx.api.deobf.IDeobfCondition;
|
||||
import jadx.api.deobf.IRenameCondition;
|
||||
import jadx.api.deobf.impl.CombineDeobfConditions;
|
||||
|
||||
public class JadxRenameConditions {
|
||||
|
||||
/**
|
||||
* This method provides a mutable list of default deobfuscation conditions used by jadx.
|
||||
* To build {@link IRenameCondition} use {@link CombineDeobfConditions#combine(List)} method.
|
||||
*/
|
||||
public static List<IDeobfCondition> buildDefaultDeobfConditions() {
|
||||
List<IDeobfCondition> list = new ArrayList<>();
|
||||
list.add(new BaseDeobfCondition());
|
||||
list.add(new DeobfWhitelist());
|
||||
list.add(new ExcludePackageWithTLDNames());
|
||||
list.add(new ExcludeAndroidRClass());
|
||||
list.add(new AvoidClsAndPkgNamesCollision());
|
||||
list.add(new DeobfLengthCondition());
|
||||
return list;
|
||||
}
|
||||
|
||||
public static IRenameCondition buildDefault() {
|
||||
return CombineDeobfConditions.combine(buildDefaultDeobfConditions());
|
||||
}
|
||||
}
|
||||
@@ -130,6 +130,14 @@ public class PackageNode extends LineAttrNode
|
||||
}
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return pkgInfo.getName();
|
||||
}
|
||||
|
||||
public String getFullName() {
|
||||
return pkgInfo.getFullName();
|
||||
}
|
||||
|
||||
public PackageInfo getPkgInfo() {
|
||||
return pkgInfo;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.core.deobf.NameMapper;
|
||||
import jadx.core.deobf.TldHelper;
|
||||
|
||||
public class BetterName {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(BetterName.class);
|
||||
@@ -43,9 +42,6 @@ public class BetterName {
|
||||
if (NameMapper.isValidIdentifier(str)) {
|
||||
rating += 50;
|
||||
}
|
||||
if (TldHelper.contains(str)) {
|
||||
rating += 20;
|
||||
}
|
||||
if (str.contains("_")) {
|
||||
// rare in obfuscated names
|
||||
rating += 100;
|
||||
|
||||
@@ -369,6 +369,10 @@ public class JadxSettings extends JadxCLIArgs {
|
||||
this.deobfuscationMaxLength = deobfuscationMaxLength;
|
||||
}
|
||||
|
||||
public void setDeobfuscationWhitelistStr(String value) {
|
||||
this.deobfuscationWhitelistStr = value;
|
||||
}
|
||||
|
||||
public void setGeneratedRenamesMappingFileMode(GeneratedRenamesMappingFileMode mode) {
|
||||
this.generatedRenamesMappingFileMode = mode;
|
||||
}
|
||||
|
||||
@@ -238,13 +238,13 @@ public class JadxSettingsWindow extends JDialog {
|
||||
|
||||
JButton editWhitelistedEntities = new JButton(NLS.str("preferences.excludedPackages.button"));
|
||||
editWhitelistedEntities.addActionListener(event -> {
|
||||
|
||||
String oldEWhitelistedEntities = settings.getDeobfuscationWhitelist();
|
||||
String result = JOptionPane.showInputDialog(this, NLS.str("preferences.deobfuscation_whitelist.editDialog"),
|
||||
settings.getDeobfuscationWhitelist());
|
||||
String prevWhitelistedEntities = settings.getDeobfuscationWhitelistStr();
|
||||
String result = JOptionPane.showInputDialog(this,
|
||||
NLS.str("preferences.deobfuscation_whitelist.editDialog"),
|
||||
prevWhitelistedEntities);
|
||||
if (result != null) {
|
||||
settings.setExcludedPackages(result);
|
||||
if (!oldEWhitelistedEntities.equals(result)) {
|
||||
settings.setDeobfuscationWhitelistStr(result);
|
||||
if (!prevWhitelistedEntities.equals(result)) {
|
||||
needReload();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user