feat: add the option to always use source file name as class name alias (PR #2287)

This commit is contained in:
pubiqq
2024-09-24 00:47:08 +03:00
committed by GitHub
parent 7abbc81886
commit b5e3dcf70f
20 changed files with 248 additions and 73 deletions
+25 -6
View File
@@ -21,6 +21,7 @@ import org.slf4j.LoggerFactory;
import jadx.api.args.GeneratedRenamesMappingFileMode;
import jadx.api.args.IntegerFormat;
import jadx.api.args.ResourceNameSource;
import jadx.api.args.UseSourceNameAsClassNameAlias;
import jadx.api.args.UserRenamesMappingsMode;
import jadx.api.data.ICodeData;
import jadx.api.deobf.IAliasProvider;
@@ -98,7 +99,7 @@ public class JadxArgs implements Closeable {
private UserRenamesMappingsMode userRenamesMappingsMode = UserRenamesMappingsMode.getDefault();
private boolean deobfuscationOn = false;
private boolean useSourceNameAsClassAlias = false;
private UseSourceNameAsClassNameAlias useSourceNameAsClassNameAlias = UseSourceNameAsClassNameAlias.getDefault();
private File generatedRenamesMappingFile = null;
private GeneratedRenamesMappingFileMode generatedRenamesMappingFileMode = GeneratedRenamesMappingFileMode.getDefault();
@@ -430,12 +431,29 @@ public class JadxArgs implements Closeable {
this.generatedRenamesMappingFileMode = mode;
}
public boolean isUseSourceNameAsClassAlias() {
return useSourceNameAsClassAlias;
public UseSourceNameAsClassNameAlias getUseSourceNameAsClassNameAlias() {
return useSourceNameAsClassNameAlias;
}
public void setUseSourceNameAsClassNameAlias(UseSourceNameAsClassNameAlias useSourceNameAsClassNameAlias) {
this.useSourceNameAsClassNameAlias = useSourceNameAsClassNameAlias;
}
/**
* @deprecated Use {@link #getUseSourceNameAsClassNameAlias()} instead.
*/
@Deprecated
public boolean isUseSourceNameAsClassAlias() {
return getUseSourceNameAsClassNameAlias().toBoolean();
}
/**
* @deprecated Use {@link #setUseSourceNameAsClassNameAlias(UseSourceNameAsClassNameAlias)} instead.
*/
@Deprecated
public void setUseSourceNameAsClassAlias(boolean useSourceNameAsClassAlias) {
this.useSourceNameAsClassAlias = useSourceNameAsClassAlias;
final var useSourceNameAsClassNameAlias = UseSourceNameAsClassNameAlias.create(useSourceNameAsClassAlias);
setUseSourceNameAsClassNameAlias(useSourceNameAsClassNameAlias);
}
public int getDeobfuscationMinLength() {
@@ -729,10 +747,11 @@ public class JadxArgs implements Closeable {
String argStr = "args:" + decompilationMode + useImports + showInconsistentCode
+ inlineAnonymousClasses + inlineMethods + moveInnerClasses + allowInlineKotlinLambda
+ deobfuscationOn + deobfuscationMinLength + deobfuscationMaxLength + deobfuscationWhitelist
+ useSourceNameAsClassNameAlias
+ resourceNameSource
+ useKotlinMethodsForVarNames
+ insertDebugLines + extractFinally
+ debugInfo + useSourceNameAsClassAlias + escapeUnicode + replaceConsts
+ debugInfo + escapeUnicode + replaceConsts
+ respectBytecodeAccModifiers + fsCaseSensitive + renameFlags
+ commentsLevel + useDxInput + integerFormat
+ "|" + buildPluginsHash(decompiler);
@@ -768,7 +787,7 @@ public class JadxArgs implements Closeable {
+ ", generatedRenamesMappingFile=" + generatedRenamesMappingFile
+ ", generatedRenamesMappingFileMode=" + generatedRenamesMappingFileMode
+ ", resourceNameSource=" + resourceNameSource
+ ", useSourceNameAsClassAlias=" + useSourceNameAsClassAlias
+ ", useSourceNameAsClassNameAlias=" + useSourceNameAsClassNameAlias
+ ", useKotlinMethodsForVarNames=" + useKotlinMethodsForVarNames
+ ", insertDebugLines=" + insertDebugLines
+ ", extractFinally=" + extractFinally
@@ -0,0 +1,38 @@
package jadx.api.args;
import jadx.core.utils.exceptions.JadxRuntimeException;
public enum UseSourceNameAsClassNameAlias {
ALWAYS,
IF_BETTER,
NEVER;
public static UseSourceNameAsClassNameAlias getDefault() {
return NEVER;
}
/**
* @deprecated Use {@link UseSourceNameAsClassNameAlias} directly.
*/
@Deprecated
public boolean toBoolean() {
switch (this) {
case IF_BETTER:
return true;
case NEVER:
return false;
case ALWAYS:
throw new JadxRuntimeException("No match between " + this + " and boolean");
default:
throw new JadxRuntimeException("Unhandled strategy: " + this);
}
}
/**
* @deprecated Use {@link UseSourceNameAsClassNameAlias} directly.
*/
@Deprecated
public static UseSourceNameAsClassNameAlias create(boolean useSourceNameAsAlias) {
return useSourceNameAsAlias ? IF_BETTER : NEVER;
}
}
@@ -7,6 +7,7 @@ import java.util.Map;
import org.jetbrains.annotations.Nullable;
import jadx.api.args.UseSourceNameAsClassNameAlias;
import jadx.api.plugins.input.data.attributes.JadxAttrType;
import jadx.api.plugins.input.data.attributes.types.SourceFileAttr;
import jadx.core.deobf.NameMapper;
@@ -18,6 +19,7 @@ import jadx.core.dex.visitors.AbstractVisitor;
import jadx.core.utils.BetterName;
import jadx.core.utils.StringUtils;
import jadx.core.utils.exceptions.JadxException;
import jadx.core.utils.exceptions.JadxRuntimeException;
public class SourceFileRename extends AbstractVisitor {
@@ -28,9 +30,11 @@ public class SourceFileRename extends AbstractVisitor {
@Override
public void init(RootNode root) throws JadxException {
if (!root.getArgs().isUseSourceNameAsClassAlias()) {
final var useSourceName = root.getArgs().getUseSourceNameAsClassNameAlias();
if (useSourceName == UseSourceNameAsClassNameAlias.NEVER) {
return;
}
List<ClassNode> classes = root.getClasses();
Map<String, Boolean> canUseAlias = new HashMap<>();
for (ClassNode cls : classes) {
@@ -55,16 +59,15 @@ public class SourceFileRename extends AbstractVisitor {
for (ClsRename clsRename : renames) {
String alias = clsRename.getAlias();
if (canUseAlias.get(alias) == Boolean.TRUE) {
applyRename(clsRename.getCls(), alias);
applyRename(clsRename.getCls(), alias, useSourceName);
}
}
}
private static void applyRename(ClassNode cls, String alias) {
private static void applyRename(ClassNode cls, String alias, UseSourceNameAsClassNameAlias useSourceName) {
if (cls.getClassInfo().hasAlias()) {
// ignore source name if current alias is "better"
String currentAlias = cls.getAlias();
String betterName = BetterName.compareAndGet(alias, currentAlias);
String betterName = getBetterName(currentAlias, alias, useSourceName);
if (betterName.equals(currentAlias)) {
return;
}
@@ -73,6 +76,19 @@ public class SourceFileRename extends AbstractVisitor {
cls.addAttr(new RenameReasonAttr(cls).append("use source file name"));
}
private static String getBetterName(String currentName, String sourceName, UseSourceNameAsClassNameAlias useSourceName) {
switch (useSourceName) {
case ALWAYS:
return sourceName;
case IF_BETTER:
return BetterName.compareAndGet(sourceName, currentName);
case NEVER:
return currentName;
default:
throw new JadxRuntimeException("Unhandled strategy: " + useSourceName);
}
}
private static @Nullable String getAliasFromSourceFile(ClassNode cls) {
SourceFileAttr sourceFileAttr = cls.get(JadxAttrType.SOURCE_FILE);
if (sourceFileAttr == null) {
@@ -1,40 +0,0 @@
package jadx.tests.integration.deobf;
import org.junit.jupiter.api.Test;
import jadx.tests.api.SmaliTest;
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
public class TestBadSourceFile extends SmaliTest {
@Test
public void test() {
// use source name disabled by default
enableDeobfuscation();
args.setDeobfuscationMinLength(100); // rename everything
assertThat(searchCls(loadFromSmaliFiles(), "b"))
.code()
.containsOne("class C0000b {");
}
@Test
public void testWithUseSourceName() {
args.setUseSourceNameAsClassAlias(true);
// deobfuscation disabled
assertThat(searchCls(loadFromSmaliFiles(), "b"))
.code()
.containsOne("class a {");
}
@Test
public void testWithUseSourceNameAndDeobf() {
args.setUseSourceNameAsClassAlias(true);
enableDeobfuscation();
args.setDeobfuscationMinLength(100); // rename everything
assertThat(searchCls(loadFromSmaliFiles(), "b"))
.code()
.containsOne("class C0000b {")
.containsOne("/* compiled from: a.java */");
}
}
@@ -0,0 +1,79 @@
package jadx.tests.integration.rename;
import org.junit.jupiter.api.Test;
import jadx.api.args.UseSourceNameAsClassNameAlias;
import jadx.tests.api.SmaliTest;
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
public class TestUsingSourceFileName extends SmaliTest {
@Test
public void testNeverUseSourceName() {
args.setUseSourceNameAsClassNameAlias(UseSourceNameAsClassNameAlias.NEVER);
assertThat(searchCls(loadFromSmaliFiles(), "b"))
.code()
.containsOne("class b {");
}
@Test
public void testAlwaysUseSourceName() {
args.setUseSourceNameAsClassNameAlias(UseSourceNameAsClassNameAlias.ALWAYS);
assertThat(searchCls(loadFromSmaliFiles(), "b"))
.code()
.containsOne("class a {");
}
@Test
public void testNeverUseSourceNameWithDeobf() {
args.setUseSourceNameAsClassNameAlias(UseSourceNameAsClassNameAlias.NEVER);
enableDeobfuscation();
args.setDeobfuscationMinLength(100); // rename everything
assertThat(searchCls(loadFromSmaliFiles(), "b"))
.code()
.containsOne("class C0000b {")
.containsOne("/* compiled from: a.java */");
}
@Test
public void testAlwaysUseSourceNameWithDeobf() {
args.setUseSourceNameAsClassNameAlias(UseSourceNameAsClassNameAlias.ALWAYS);
enableDeobfuscation();
args.setDeobfuscationMinLength(100); // rename everything
assertThat(searchCls(loadFromSmaliFiles(), "b"))
.code()
.containsOne("class a {")
.containsOne("/* compiled from: a.java */");
}
@Test
public void testDeprecatedDontUseSourceName() {
// noinspection deprecation
args.setUseSourceNameAsClassAlias(false);
assertThat(searchCls(loadFromSmaliFiles(), "b"))
.code()
.containsOne("class b {");
}
@Test
public void testDeprecatedUseSourceName() {
// noinspection deprecation
args.setUseSourceNameAsClassAlias(true);
assertThat(searchCls(loadFromSmaliFiles(), "b"))
.code()
.containsOne("class a {");
}
@Test
public void testDeprecatedUseSourceNameWithDeobf() {
// noinspection deprecation
args.setUseSourceNameAsClassAlias(true);
enableDeobfuscation();
args.setDeobfuscationMinLength(100); // rename everything
assertThat(searchCls(loadFromSmaliFiles(), "b"))
.code()
.containsOne("class C0000b {")
.containsOne("/* compiled from: a.java */");
}
}