fix: replace constants inside annotations (#831)

This commit is contained in:
Skylot
2020-01-14 19:21:31 +00:00
parent 7bbb083c36
commit 2921c66834
5 changed files with 156 additions and 1 deletions
@@ -52,6 +52,8 @@ public class ClassGen {
private final Set<ClassInfo> 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;
}
}
@@ -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);
@@ -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<String, Object> 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) {
@@ -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<Integer, String> 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)");
}
}
@@ -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");
}
}