11package jadx .plugins .stringdecoder ;
22
33import java .util .Locale ;
4+ import java .util .TreeMap ;
45
56import jadx .api .plugins .input .data .annotations .EncodedType ;
67import jadx .api .plugins .input .data .annotations .EncodedValue ;
1314import jadx .core .dex .info .FieldInfo ;
1415import jadx .core .dex .info .MethodInfo ;
1516import jadx .core .dex .instructions .ConstStringNode ;
17+ import jadx .core .dex .instructions .FilledNewArrayNode ;
1618import jadx .core .dex .instructions .IndexInsnNode ;
1719import jadx .core .dex .instructions .InsnType ;
1820import jadx .core .dex .instructions .InvokeNode ;
@@ -60,6 +62,12 @@ private void processField(FieldNode field) {
6062 InsnNode initInsn = initAttr .getInsn ();
6163 if (initInsn instanceof ConstStringNode ) {
6264 annotateField (field , ((ConstStringNode ) initInsn ).getString (), false );
65+ } else if (initInsn instanceof FilledNewArrayNode ) {
66+ // B64DeobfuscatePass already handled small filled-new-array instructions
67+ // (original Dalvik opcode); skip re-processing them.
68+ if (!initInsn .contains (AType .CODE_COMMENTS )) {
69+ findAndAnnotateFilledArray (field , (FilledNewArrayNode ) initInsn );
70+ }
6371 } else {
6472 findAndAnnotateInArgTree (field , initInsn , 0 );
6573 }
@@ -76,6 +84,45 @@ private void processField(FieldNode field) {
7684 }
7785 }
7886
87+ /**
88+ * Processes a FilledNewArrayNode field init with indexed, anchor-based detection.
89+ * Mirrors B64DeobfuscatePass array logic: only emits a comment when at least one element
90+ * passes full detection (the anchor), then includes all valid-Base64+UTF-8 elements
91+ * with their array indices so the caller can identify which element was decoded.
92+ */
93+ private void findAndAnnotateFilledArray (FieldNode field , FilledNewArrayNode filledArray ) {
94+ TreeMap <Integer , B64Result > candidates = null ;
95+ boolean hasAnchor = false ;
96+ for (int idx = 0 ; idx < filledArray .getArgsCount (); idx ++) {
97+ InsnArg arg = filledArray .getArg (idx );
98+ InsnNode argInsn = resolveArgInsn (arg );
99+ String str = null ;
100+ if (argInsn instanceof ConstStringNode ) {
101+ str = ((ConstStringNode ) argInsn ).getString ();
102+ } else if (argInsn != null && argInsn .getType () == InsnType .SGET ) {
103+ str = resolveConstStringFromSget (field , (IndexInsnNode ) argInsn );
104+ }
105+ if (str == null || B64FalsePositives .contains (str )) {
106+ continue ;
107+ }
108+ B64Result full = B64Detector .detect (str , options );
109+ B64Result candidate = full != null ? full : B64Detector .decodeIfValid (str , options .getMaxCommentLength ());
110+ if (candidate == null ) {
111+ continue ;
112+ }
113+ if (candidates == null ) {
114+ candidates = new TreeMap <>();
115+ }
116+ candidates .put (idx , candidate );
117+ if (full != null ) {
118+ hasAnchor = true ;
119+ }
120+ }
121+ if (hasAnchor && candidates != null ) {
122+ field .addCodeComment (B64Result .buildIndexedComment (candidates ));
123+ }
124+ }
125+
79126 /**
80127 * Recursively walks the instruction arg tree looking for a ConstStringNode.
81128 * When the ConstStringNode is a direct arg of a Base64.decode-like call, decodes
0 commit comments