From 4e982722a57604f97a83eaf2d1c6ee54bad62a7f Mon Sep 17 00:00:00 2001 From: Skylot Date: Sat, 19 Mar 2016 22:55:57 +0300 Subject: [PATCH] core: fix incorrect package for R class (#99) --- .../main/java/jadx/core/codegen/ClassGen.java | 4 ++ .../main/java/jadx/core/codegen/InsnGen.java | 9 +-- .../java/jadx/core/dex/info/ClassInfo.java | 4 ++ .../java/jadx/core/dex/nodes/FieldNode.java | 6 +- .../java/jadx/core/dex/nodes/RootNode.java | 32 +++++------ .../utils/android/AndroidResourcesUtils.java | 55 +++++++++++++++++++ .../integration/inner/TestRFieldRestore.java | 3 + 7 files changed, 85 insertions(+), 28 deletions(-) create mode 100644 jadx-core/src/main/java/jadx/core/utils/android/AndroidResourcesUtils.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 ac8718fa..60f76086 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java @@ -501,6 +501,10 @@ public class ClassGen { if (searchCollision(cls.dex(), useCls, extClsInfo)) { return fullName; } + // ignore classes from default package + if (extClsInfo.isDefaultPackage()) { + return shortName; + } if (extClsInfo.getPackage().equals(useCls.getPackage())) { fullName = extClsInfo.getNameWithoutPackage(); } 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 bbd972f4..471834ee 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java @@ -52,6 +52,8 @@ import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static jadx.core.utils.android.AndroidResourcesUtils.handleAppResField; + public class InsnGen { private static final Logger LOG = LoggerFactory.getLogger(InsnGen.class); @@ -169,12 +171,7 @@ public class InsnGen { boolean fieldFromThisClass = clsGen.getClassNode().getClassInfo().equals(declClass); if (!fieldFromThisClass) { // Android specific resources class handler - ClassInfo parentClass = declClass.getParentClass(); - if (parentClass != null && parentClass.getShortName().equals("R")) { - clsGen.useClass(code, parentClass); - code.add('.'); - code.add(declClass.getAlias().getShortName()); - } else { + if (!handleAppResField(code, clsGen, declClass)) { clsGen.useClass(code, declClass); } code.add('.'); diff --git a/jadx-core/src/main/java/jadx/core/dex/info/ClassInfo.java b/jadx-core/src/main/java/jadx/core/dex/info/ClassInfo.java index 53b04c03..2550e082 100644 --- a/jadx-core/src/main/java/jadx/core/dex/info/ClassInfo.java +++ b/jadx-core/src/main/java/jadx/core/dex/info/ClassInfo.java @@ -125,6 +125,10 @@ public final class ClassInfo { return pkg; } + public boolean isDefaultPackage() { + return pkg.isEmpty(); + } + public String getRawName() { return type.getObject(); } diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/FieldNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/FieldNode.java index a19f49ff..486f0eac 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/FieldNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/FieldNode.java @@ -17,10 +17,8 @@ public class FieldNode extends LineAttrNode { private ArgType type; // store signature public FieldNode(ClassNode cls, Field field) { - this.parent = cls; - this.fieldInfo = FieldInfo.fromDex(cls.dex(), field.getFieldIndex()); - this.type = fieldInfo.getType(); - this.accFlags = new AccessInfo(field.getAccessFlags(), AFType.FIELD); + this(cls, FieldInfo.fromDex(cls.dex(), field.getFieldIndex()), + field.getAccessFlags()); } public FieldNode(ClassNode cls, FieldInfo fieldInfo, int accessFlags) { diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java index bcab6654..d121c88d 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java @@ -9,6 +9,7 @@ import jadx.core.dex.info.ClassInfo; import jadx.core.dex.info.ConstStorage; import jadx.core.utils.ErrorsCounter; import jadx.core.utils.StringUtils; +import jadx.core.utils.android.AndroidResourcesUtils; import jadx.core.utils.exceptions.DecodeException; import jadx.core.utils.exceptions.JadxException; import jadx.core.utils.files.DexFile; @@ -97,24 +98,7 @@ public class RootNode { } public void initAppResClass() { - ClassNode resCls; - if (appPackage == null) { - appResClass = makeClass("R"); - return; - } - String fullName = appPackage + ".R"; - resCls = searchClassByName(fullName); - if (resCls != null) { - appResClass = resCls; - } else { - appResClass = makeClass(fullName); - } - } - - private ClassNode makeClass(String clsName) { - DexNode firstDex = dexNodes.get(0); - ClassInfo r = ClassInfo.fromName(firstDex, clsName); - return new ClassNode(firstDex, r); + appResClass = AndroidResourcesUtils.searchAppResClass(this); } public void initClassPath() throws DecodeException { @@ -169,6 +153,18 @@ public class RootNode { return null; } + public List searchClassByShortName(String shortName) { + List list = new ArrayList(); + for (DexNode dexNode : dexNodes) { + for (ClassNode cls : dexNode.getClasses()) { + if (cls.getClassInfo().getShortName().equals(shortName)) { + list.add(cls); + } + } + } + return list; + } + public List getDexNodes() { return dexNodes; } diff --git a/jadx-core/src/main/java/jadx/core/utils/android/AndroidResourcesUtils.java b/jadx-core/src/main/java/jadx/core/utils/android/AndroidResourcesUtils.java new file mode 100644 index 00000000..ee444153 --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/utils/android/AndroidResourcesUtils.java @@ -0,0 +1,55 @@ +package jadx.core.utils.android; + +import jadx.core.codegen.ClassGen; +import jadx.core.codegen.CodeWriter; +import jadx.core.dex.info.ClassInfo; +import jadx.core.dex.nodes.ClassNode; +import jadx.core.dex.nodes.DexNode; +import jadx.core.dex.nodes.RootNode; + +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Android resources specific handlers + */ +public class AndroidResourcesUtils { + private static final Logger LOG = LoggerFactory.getLogger(AndroidResourcesUtils.class); + + public static ClassNode searchAppResClass(RootNode root) { + String appPackage = root.getAppPackage(); + String fullName = appPackage != null ? appPackage + ".R" : "R"; + ClassNode resCls = root.searchClassByName(fullName); + if (resCls != null) { + return resCls; + } + List candidates = root.searchClassByShortName("R"); + if (candidates.size() == 1) { + return candidates.get(0); + } + if (!candidates.isEmpty()) { + LOG.info("Found several 'R' class candidates: {}", candidates); + } + LOG.warn("Unknown 'R' class, create references to '{}'", fullName); + return makeClass(root, fullName); + } + + public static boolean handleAppResField(CodeWriter code, ClassGen clsGen, ClassInfo declClass) { + ClassInfo parentClass = declClass.getParentClass(); + if (parentClass != null && parentClass.getShortName().equals("R")) { + clsGen.useClass(code, parentClass); + code.add('.'); + code.add(declClass.getAlias().getShortName()); + return true; + } + return false; + } + + private static ClassNode makeClass(RootNode root, String clsName) { + DexNode firstDex = root.getDexNodes().get(0); + ClassInfo r = ClassInfo.fromName(firstDex, clsName); + return new ClassNode(firstDex, r); + } +} diff --git a/jadx-core/src/test/java/jadx/tests/integration/inner/TestRFieldRestore.java b/jadx-core/src/test/java/jadx/tests/integration/inner/TestRFieldRestore.java index 4f763ddb..03508b8d 100644 --- a/jadx-core/src/test/java/jadx/tests/integration/inner/TestRFieldRestore.java +++ b/jadx-core/src/test/java/jadx/tests/integration/inner/TestRFieldRestore.java @@ -9,6 +9,8 @@ import java.util.Map; import org.junit.Test; import static jadx.tests.api.utils.JadxMatchers.containsOne; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.not; import static org.junit.Assert.assertThat; public class TestRFieldRestore extends IntegrationTest { @@ -31,5 +33,6 @@ public class TestRFieldRestore extends IntegrationTest { ClassNode cls = getClassNode(TestCls.class); String code = cls.getCode().toString(); assertThat(code, containsOne("return R.id.Button;")); + assertThat(code, not(containsString("import R;"))); } } -- GitLab