From 2921c668349986ea802bd9e2c1c4a0cdc5b54a25 Mon Sep 17 00:00:00 2001 From: Skylot Date: Tue, 14 Jan 2020 19:21:31 +0000 Subject: [PATCH] fix: replace constants inside annotations (#831) --- .../main/java/jadx/core/codegen/ClassGen.java | 11 +++ .../main/java/jadx/core/codegen/InsnGen.java | 2 +- .../jadx/core/dex/visitors/ModVisitor.java | 37 ++++++++++ .../integration/inner/TestRFieldRestore3.java | 70 +++++++++++++++++++ .../inner/TestReplaceConstsInAnnotations.java | 37 ++++++++++ 5 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 jadx-core/src/test/java/jadx/tests/integration/inner/TestRFieldRestore3.java create mode 100644 jadx-core/src/test/java/jadx/tests/integration/inner/TestReplaceConstsInAnnotations.java diff --git a/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java b/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java index ba8f9ce50..4a83d5c35 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java @@ -52,6 +52,8 @@ public class ClassGen { private final Set imports = new HashSet<>(); private int clsDeclLine; + private boolean bodyGenStarted; + public ClassGen(ClassNode cls, JadxArgs jadxArgs) { this(cls, null, jadxArgs.isUseImports(), jadxArgs.isFallbackMode(), jadxArgs.isShowInconsistentCode()); } @@ -222,6 +224,7 @@ public class ClassGen { public void addClassBody(CodeWriter clsCode) throws CodegenException { clsCode.add('{'); + setBodyGenStarted(true); clsDeclLine = clsCode.getLine(); clsCode.incIndent(); addFields(clsCode); @@ -656,4 +659,12 @@ public class ClassGen { public boolean isFallbackMode() { return fallback; } + + public boolean isBodyGenStarted() { + return bodyGenStarted; + } + + public void setBodyGenStarted(boolean bodyGenStarted) { + this.bodyGenStarted = bodyGenStarted; + } } diff --git a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java index f3def9baf..92219107a 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java @@ -183,7 +183,7 @@ public class InsnGen { ClassInfo declClass = field.getDeclClass(); // TODO boolean fieldFromThisClass = clsGen.getClassNode().getClassInfo().equals(declClass); - if (!fieldFromThisClass) { + if (!fieldFromThisClass || !clsGen.isBodyGenStarted()) { // Android specific resources class handler if (!handleAppResField(code, clsGen, declClass)) { clsGen.useClass(code, declClass); diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ModVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ModVisitor.java index 1a36d71bc..a7e854060 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ModVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ModVisitor.java @@ -11,6 +11,9 @@ import org.slf4j.LoggerFactory; import jadx.core.dex.attributes.AFlag; import jadx.core.dex.attributes.AType; +import jadx.core.dex.attributes.AttrNode; +import jadx.core.dex.attributes.annotations.Annotation; +import jadx.core.dex.attributes.annotations.AnnotationsList; import jadx.core.dex.attributes.nodes.FieldReplaceAttr; import jadx.core.dex.info.FieldInfo; import jadx.core.dex.info.MethodInfo; @@ -46,6 +49,7 @@ import jadx.core.dex.visitors.shrink.CodeShrinkVisitor; import jadx.core.utils.ErrorsCounter; import jadx.core.utils.InsnRemover; import jadx.core.utils.InsnUtils; +import jadx.core.utils.exceptions.JadxException; import jadx.core.utils.exceptions.JadxRuntimeException; import static jadx.core.utils.BlockUtils.replaceInsn; @@ -68,6 +72,12 @@ public class ModVisitor extends AbstractVisitor { private static final long DOUBLE_TO_BITS = Double.doubleToLongBits(1); private static final long FLOAT_TO_BITS = Float.floatToIntBits(1); + @Override + public boolean visit(ClassNode cls) throws JadxException { + replaceConstInAnnotations(cls); + return true; + } + @Override public void visit(MethodNode mth) { if (mth.isNoCode()) { @@ -171,6 +181,33 @@ public class ModVisitor extends AbstractVisitor { } } + private void replaceConstInAnnotations(ClassNode cls) { + if (cls.root().getArgs().isReplaceConsts()) { + replaceConstsInAnnotationForAttrNode(cls, cls); + cls.getFields().forEach(f -> replaceConstsInAnnotationForAttrNode(cls, f)); + cls.getMethods().forEach(m -> replaceConstsInAnnotationForAttrNode(cls, m)); + } + } + + private void replaceConstsInAnnotationForAttrNode(ClassNode parentCls, AttrNode attrNode) { + AnnotationsList annotationsList = attrNode.get(AType.ANNOTATION_LIST); + if (annotationsList == null) { + return; + } + for (Annotation annotation : annotationsList.getAll()) { + if (annotation.getVisibility() == Annotation.Visibility.SYSTEM) { + continue; + } + for (Map.Entry entry : annotation.getValues().entrySet()) { + Object value = entry.getValue(); + FieldNode constField = parentCls.getConstField(value); + if (constField != null) { + entry.setValue(constField.getFieldInfo()); + } + } + } + } + private static void replaceConst(MethodNode mth, ClassNode parentClass, BlockNode block, int i, InsnNode insn) { FieldNode f; if (insn.getType() == InsnType.CONST_STR) { diff --git a/jadx-core/src/test/java/jadx/tests/integration/inner/TestRFieldRestore3.java b/jadx-core/src/test/java/jadx/tests/integration/inner/TestRFieldRestore3.java new file mode 100644 index 000000000..7fbca415b --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/inner/TestRFieldRestore3.java @@ -0,0 +1,70 @@ +package jadx.tests.integration.inner; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.HashMap; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import jadx.tests.api.IntegrationTest; + +import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat; + +public class TestRFieldRestore3 extends IntegrationTest { + + public static class TestCls { + + @T(2131230730) + public static class A { + @F(2131230730) + private int f; + + @M(bind = 2137373737) + private void mth() { + } + + @T(2137373737) + private class D { + } + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.TYPE) + @interface T { + int value(); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.FIELD }) + @interface F { + int value(); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.METHOD }) + @interface M { + int bind(); + } + + public static class R { + } + } + + @Test + public void test() { + Map map = new HashMap<>(); + map.put(2131230730, "id.Button"); + map.put(2137373737, "id.MyId"); + setResMap(map); + + assertThat(getClassNode(TestCls.class)) + .code() + .containsOnlyOnce("@T(R.id.Button)") + .containsOnlyOnce("@T(R.id.MyId)") + .containsOnlyOnce("@F(R.id.Button)") + .containsOnlyOnce("@M(bind = R.id.MyId)"); + } +} diff --git a/jadx-core/src/test/java/jadx/tests/integration/inner/TestReplaceConstsInAnnotations.java b/jadx-core/src/test/java/jadx/tests/integration/inner/TestReplaceConstsInAnnotations.java new file mode 100644 index 000000000..3009283fd --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/inner/TestReplaceConstsInAnnotations.java @@ -0,0 +1,37 @@ +package jadx.tests.integration.inner; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.junit.jupiter.api.Test; + +import jadx.tests.api.IntegrationTest; + +import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat; + +public class TestReplaceConstsInAnnotations extends IntegrationTest { + + public static class TestCls { + @Target(ElementType.TYPE) + @Retention(RetentionPolicy.RUNTIME) + public @interface A { + int i(); + + float f(); + } + + @A(i = -1, f = C.FLOAT_CONST) + public static class C { + public static final float FLOAT_CONST = 3.14f; + } + } + + @Test + public void test() { + assertThat(getClassNode(TestCls.class)) + .code() + .containsOnlyOnce("f = C.FLOAT_CONST"); + } +}