diff --git a/.gitignore b/.gitignore index 4627d8fad..d8906bd22 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ out/ *.iml *.ipr *.iws +.attach_pid* **/.DS_Store diff --git a/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java b/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java index bf1e15e93..3c189154f 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java @@ -78,7 +78,7 @@ public class MethodGen { ai = ai.remove(AccessFlags.ACC_PUBLIC); } - if (mth.getMethodInfo().isRenamed()) { + if (mth.getMethodInfo().isRenamed() && !ai.isConstructor()) { code.startLine("/* renamed from: ").add(mth.getName()).add(" */"); } code.startLineWithNum(mth.getSourceLine()); @@ -87,7 +87,7 @@ public class MethodGen { if (classGen.addGenericMap(code, mth.getGenericMap())) { code.add(' '); } - if (mth.getAccessFlags().isConstructor()) { + if (ai.isConstructor()) { code.attachDefinition(mth); code.add(classGen.getClassNode().getShortName()); // constructor } else { diff --git a/jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java b/jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java index 9480ee147..ed0d45f91 100644 --- a/jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java +++ b/jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java @@ -129,7 +129,7 @@ public class Deobfuscator { for (MethodInfo mth : o.getMethods()) { if (aliasToUse == null) { if (mth.isRenamed() && !mth.isAliasFromPreset()) { - mth.setAlias(String.format("mo%d%s", id, makeName(mth.getName()))); + mth.setAlias(String.format("mo%d%s", id, prepareNamePart(mth.getName()))); } aliasToUse = mth.getAlias(); } @@ -350,7 +350,7 @@ public class Deobfuscator { if (alias == null) { String clsName = classInfo.getShortName(); - alias = String.format("C%04d%s", clsIndex++, makeName(clsName)); + alias = String.format("C%04d%s", clsIndex++, prepareNamePart(clsName)); } PackageNode pkg = getPackageNode(classInfo.getPackage(), true); clsMap.put(classInfo, new DeobfClsInfo(this, cls, pkg, alias)); @@ -426,13 +426,13 @@ public class Deobfuscator { } public String makeFieldAlias(FieldNode field) { - String alias = String.format("f%d%s", fldIndex++, makeName(field.getName())); + String alias = String.format("f%d%s", fldIndex++, prepareNamePart(field.getName())); fldMap.put(field.getFieldInfo(), alias); return alias; } public String makeMethodAlias(MethodNode mth) { - String alias = String.format("m%d%s", mthIndex++, makeName(mth.getName())); + String alias = String.format("m%d%s", mthIndex++, prepareNamePart(mth.getName())); mthMap.put(mth.getMethodInfo(), alias); return alias; } @@ -454,25 +454,21 @@ public class Deobfuscator { String pkgName = pkg.getName(); if (!pkg.hasAlias() && shouldRename(pkgName)) { - String pkgAlias = String.format("p%03d%s", pkgIndex++, makeName(pkgName)); + String pkgAlias = String.format("p%03d%s", pkgIndex++, prepareNamePart(pkgName)); pkg.setAlias(pkgAlias); } } private boolean shouldRename(String s) { - return s.length() > maxLength - || s.length() < minLength - || NameMapper.isReserved(s) - || !NameMapper.isAllCharsPrintable(s); + int len = s.length(); + return len < minLength || len > maxLength + || !NameMapper.isValidIdentifier(s); } - private String makeName(String name) { + private String prepareNamePart(String name) { if (name.length() > maxLength) { return "x" + Integer.toHexString(name.hashCode()); } - if (NameMapper.isReserved(name)) { - return name; - } if (!NameMapper.isAllCharsPrintable(name)) { return removeInvalidChars(name); } diff --git a/jadx-core/src/main/java/jadx/core/deobf/NameMapper.java b/jadx-core/src/main/java/jadx/core/deobf/NameMapper.java index f73f11882..4e8e75686 100644 --- a/jadx-core/src/main/java/jadx/core/deobf/NameMapper.java +++ b/jadx-core/src/main/java/jadx/core/deobf/NameMapper.java @@ -79,12 +79,14 @@ public class NameMapper { public static boolean isValidIdentifier(String str) { return notEmpty(str) + && !isReserved(str) && VALID_JAVA_IDENTIFIER.matcher(str).matches() && isAllCharsPrintable(str); } public static boolean isValidFullIdentifier(String str) { return notEmpty(str) + && !isReserved(str) && VALID_JAVA_FULL_IDENTIFIER.matcher(str).matches() && isAllCharsPrintable(str); } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/RenameVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/RenameVisitor.java index cd32cedea..a3ca4fa91 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/RenameVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/RenameVisitor.java @@ -10,6 +10,7 @@ import org.apache.commons.io.FilenameUtils; import jadx.api.JadxArgs; import jadx.core.Consts; import jadx.core.deobf.Deobfuscator; +import jadx.core.deobf.NameMapper; import jadx.core.dex.attributes.AFlag; import jadx.core.dex.info.ClassInfo; import jadx.core.dex.info.FieldInfo; @@ -100,7 +101,8 @@ public class RenameVisitor extends AbstractVisitor { Set names = new HashSet<>(); for (FieldNode field : cls.getFields()) { FieldInfo fieldInfo = field.getFieldInfo(); - if (!names.add(fieldInfo.getAlias())) { + String fieldName = fieldInfo.getAlias(); + if (!names.add(fieldName) || !NameMapper.isValidIdentifier(fieldName)) { deobfuscator.renameField(field); } } @@ -109,11 +111,11 @@ public class RenameVisitor extends AbstractVisitor { private void checkMethods(ClassNode cls) { Set names = new HashSet<>(); for (MethodNode mth : cls.getMethods()) { - if (mth.contains(AFlag.DONT_GENERATE)) { + if (mth.contains(AFlag.DONT_GENERATE) || mth.getAccessFlags().isConstructor()) { continue; } String signature = mth.getMethodInfo().makeSignature(false); - if (!names.add(signature)) { + if (!names.add(signature) || !NameMapper.isValidIdentifier(mth.getAlias())) { deobfuscator.renameMethod(mth); } } diff --git a/jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java b/jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java index 3e621c8b9..48e718200 100644 --- a/jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java +++ b/jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java @@ -27,6 +27,7 @@ import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.nodes.RootNode; import jadx.core.dex.visitors.DepthTraversal; import jadx.core.dex.visitors.IDexTreeVisitor; +import jadx.core.utils.exceptions.JadxException; import jadx.core.xmlgen.ResourceStorage; import jadx.core.xmlgen.entry.ResourceEntry; import jadx.tests.api.compiler.DynamicCompiler; @@ -137,13 +138,13 @@ public abstract class IntegrationTest extends TestUtils { } protected void decompile(JadxDecompiler jadx, ClassNode cls) { - List passes = Jadx.getPassesList(jadx.getArgs()); + List passes = getPassesList(jadx); ProcessClass.process(cls, passes, new CodeGen()); } - protected void decompileWithoutUnload(JadxDecompiler d, ClassNode cls) { + protected void decompileWithoutUnload(JadxDecompiler jadx, ClassNode cls) { cls.load(); - List passes = Jadx.getPassesList(d.getArgs()); + List passes = getPassesList(jadx); for (IDexTreeVisitor visitor : passes) { DepthTraversal.visit(visitor, cls); } @@ -151,6 +152,20 @@ public abstract class IntegrationTest extends TestUtils { // don't unload class } + private List getPassesList(JadxDecompiler jadx) { + RootNode root = JadxInternalAccess.getRoot(jadx); + List passesList = Jadx.getPassesList(jadx.getArgs()); + passesList.forEach(pass -> { + try { + pass.init(root); + } catch (JadxException e) { + e.printStackTrace(); + fail(e.getMessage()); + } + }); + return passesList; + } + protected void generateClsCode(ClassNode cls) { try { new CodeGen().visit(cls); diff --git a/jadx-core/src/test/java/jadx/tests/integration/names/TestReservedNames.java b/jadx-core/src/test/java/jadx/tests/integration/names/TestReservedNames.java new file mode 100644 index 000000000..70cbb57c6 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/names/TestReservedNames.java @@ -0,0 +1,32 @@ +package jadx.tests.integration.names; + +import org.junit.Test; + +import jadx.core.dex.nodes.ClassNode; +import jadx.tests.api.SmaliTest; + +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertThat; + +public class TestReservedNames extends SmaliTest { + /* + public static class TestCls { + + public String do; // reserved name + public String 0f; // invalid identifier + + public String try() { + return this.do; + } + } + */ + + @Test + public void test() { + ClassNode cls = getClassNodeFromSmaliWithPath("names", "TestReservedNames"); + String code = cls.getCode().toString(); + + assertThat(code, not(containsString("public String do;"))); + } +} diff --git a/jadx-core/src/test/smali/names/TestReservedNames.smali b/jadx-core/src/test/smali/names/TestReservedNames.smali new file mode 100644 index 000000000..30bcbe08b --- /dev/null +++ b/jadx-core/src/test/smali/names/TestReservedNames.smali @@ -0,0 +1,32 @@ +.class public LTestReservedNames; +.super Ljava/lang/Object; +.source "TestReservedNames.java" + + +# instance fields +.field public do:Ljava/lang/String; # reserved name +.field public 0f:Ljava/lang/String; # invalid identifier + + +# direct methods +.method public constructor ()V + .registers 1 + + .prologue + .line 3 + invoke-direct {p0}, Ljava/lang/Object;->()V + + return-void +.end method + + +# virtual methods +.method public try()Ljava/lang/String; + .registers 2 + + .prologue + .line 8 + iget-object v0, p0, LTestReservedNames;->do:Ljava/lang/String; + + return-object v0 +.end method