|
10 | 10 | import jadx.api.plugins.pass.types.JadxDecompilePass; |
11 | 11 | import jadx.core.dex.attributes.AType; |
12 | 12 | import jadx.core.dex.attributes.FieldInitInsnAttr; |
| 13 | +import jadx.core.dex.info.FieldInfo; |
13 | 14 | import jadx.core.dex.info.MethodInfo; |
14 | 15 | import jadx.core.dex.instructions.ConstStringNode; |
| 16 | +import jadx.core.dex.instructions.IndexInsnNode; |
| 17 | +import jadx.core.dex.instructions.InsnType; |
15 | 18 | import jadx.core.dex.instructions.InvokeNode; |
16 | 19 | import jadx.core.dex.instructions.args.InsnArg; |
17 | 20 | import jadx.core.dex.instructions.args.InsnWrapArg; |
@@ -95,13 +98,70 @@ private boolean findAndAnnotateInArgTree(FieldNode field, InsnNode insn, int dep |
95 | 98 | if (annotateField(field, str, isBase64Call)) { |
96 | 99 | return true; |
97 | 100 | } |
| 101 | + } else if (argInsn.getType() == InsnType.SGET) { |
| 102 | + // const-string may have been replaced by an SGET to a CONSTANT_VALUE field |
| 103 | + // (JADX's replaceConsts rewrites const-string to sget when a matching field exists) |
| 104 | + String str = resolveConstStringFromSget(field, (IndexInsnNode) argInsn); |
| 105 | + if (str != null) { |
| 106 | + if (annotateField(field, str, isBase64Call)) { |
| 107 | + // The string is a direct Base64.decode arg — also force-annotate the source |
| 108 | + // String field so it gets a comment even if it fails the alphanumeric check |
| 109 | + if (isBase64Call) { |
| 110 | + forceAnnotateSourceField(field, (IndexInsnNode) argInsn, str); |
| 111 | + } |
| 112 | + return true; |
| 113 | + } |
| 114 | + } |
98 | 115 | } else if (findAndAnnotateInArgTree(field, argInsn, depth + 1)) { |
99 | 116 | return true; |
100 | 117 | } |
101 | 118 | } |
102 | 119 | return false; |
103 | 120 | } |
104 | 121 |
|
| 122 | + /** |
| 123 | + * When a CONSTANT_VALUE String field is the direct arg to a Base64.decode call, |
| 124 | + * force-annotates it so the source field gets a comment regardless of the alphanumeric |
| 125 | + * threshold (the explicit decode call is sufficient evidence of intent). |
| 126 | + */ |
| 127 | + private void forceAnnotateSourceField(FieldNode contextField, IndexInsnNode sgetInsn, String str) { |
| 128 | + FieldInfo refFieldInfo = (FieldInfo) sgetInsn.getIndex(); |
| 129 | + ClassNode declCls = contextField.root().resolveClass(refFieldInfo.getDeclClass()); |
| 130 | + if (declCls == null) { |
| 131 | + return; |
| 132 | + } |
| 133 | + FieldNode srcField = declCls.searchField(refFieldInfo); |
| 134 | + if (srcField == null || srcField.get(AType.FIELD_INIT_INSN) != null) { |
| 135 | + return; |
| 136 | + } |
| 137 | + String decoded = B64Detector.decodeForced(str, options.getMaxCommentLength()); |
| 138 | + if (decoded != null) { |
| 139 | + srcField.addCodeComment("b64: " + decoded); |
| 140 | + } |
| 141 | + } |
| 142 | + |
| 143 | + /** Follows an SGET to the referenced field's CONSTANT_VALUE string, or returns null. */ |
| 144 | + private static String resolveConstStringFromSget(FieldNode contextField, IndexInsnNode sgetInsn) { |
| 145 | + FieldInfo refFieldInfo = (FieldInfo) sgetInsn.getIndex(); |
| 146 | + RootNode root = contextField.root(); |
| 147 | + ClassNode declCls = root.resolveClass(refFieldInfo.getDeclClass()); |
| 148 | + if (declCls == null) { |
| 149 | + return null; |
| 150 | + } |
| 151 | + FieldNode refField = declCls.searchField(refFieldInfo); |
| 152 | + if (refField == null) { |
| 153 | + return null; |
| 154 | + } |
| 155 | + EncodedValue constVal = refField.get(JadxAttrType.CONSTANT_VALUE); |
| 156 | + if (constVal != null && constVal.getType() == EncodedType.ENCODED_STRING) { |
| 157 | + Object val = constVal.getValue(); |
| 158 | + if (val instanceof String) { |
| 159 | + return (String) val; |
| 160 | + } |
| 161 | + } |
| 162 | + return null; |
| 163 | + } |
| 164 | + |
105 | 165 | private static InsnNode resolveArgInsn(InsnArg arg) { |
106 | 166 | if (arg instanceof InsnWrapArg) { |
107 | 167 | return ((InsnWrapArg) arg).getWrapInsn(); |
|
0 commit comments