|
1 | 1 | package jadx.plugins.stringdecoder; |
2 | 2 |
|
| 3 | +import java.util.ArrayList; |
3 | 4 | import java.util.Collections; |
4 | 5 | import java.util.HashSet; |
5 | 6 | import java.util.LinkedHashMap; |
@@ -68,6 +69,8 @@ public void visit(MethodNode mth) { |
68 | 69 | } |
69 | 70 | // Lazily built on the first B64 hit — avoids field scan for methods with no B64 strings |
70 | 71 | Set<String> fieldConstants = null; |
| 72 | + // Decoded strings grouped by their top-level statement instruction (insertion = source order) |
| 73 | + Map<InsnNode, List<B64Result>> stmtCandidates = null; |
71 | 74 | // Arrays where ≥1 string passed normal detect() — required anchor for contextual decoding |
72 | 75 | Set<InsnNode> arrayAnchors = null; |
73 | 76 | // All valid-B64+UTF-8 strings in arrays (superset of anchors; used for the final comment) |
@@ -136,13 +139,20 @@ public void visit(MethodNode mth) { |
136 | 139 | if (fieldConstants.contains(str)) { |
137 | 140 | continue; |
138 | 141 | } |
139 | | - // Attach to the top-level statement rather than the ConstStringNode so that |
140 | | - // comments from multiple decoded strings in one expression all survive. |
141 | | - // inheritMetadata uses the untyped addAttr (map.put = replace), so comments |
142 | | - // riding up through inlining chains overwrite each other; attaching directly |
143 | | - // to the statement uses the typed addAttr (list.add = merge) and bypasses that. |
144 | | - InsnNode stmtInsn = findStatementInsn(csn); |
145 | | - stmtInsn.addAttr(AType.CODE_COMMENTS, new CodeComment(decoded.commentText(), CommentStyle.LINE)); |
| 142 | + if (stmtCandidates == null) { |
| 143 | + stmtCandidates = new LinkedHashMap<>(); |
| 144 | + } |
| 145 | + stmtCandidates.computeIfAbsent(findStatementInsn(csn), k -> new ArrayList<>()).add(decoded); |
| 146 | + } |
| 147 | + } |
| 148 | + |
| 149 | + if (stmtCandidates != null) { |
| 150 | + for (Map.Entry<InsnNode, List<B64Result>> e : stmtCandidates.entrySet()) { |
| 151 | + List<B64Result> results = e.getValue(); |
| 152 | + String comment = results.size() == 1 |
| 153 | + ? results.get(0).commentText() |
| 154 | + : buildChainComment(results); |
| 155 | + e.getKey().addAttr(AType.CODE_COMMENTS, new CodeComment(comment, CommentStyle.LINE)); |
146 | 156 | } |
147 | 157 | } |
148 | 158 |
|
@@ -266,6 +276,15 @@ private static int argIndexOf(InsnNode insn, SSAVar ssaVar) { |
266 | 276 | return -1; |
267 | 277 | } |
268 | 278 |
|
| 279 | + private static String buildChainComment(List<B64Result> results) { |
| 280 | + StringBuilder sb = new StringBuilder(); |
| 281 | + for (int i = 0; i < results.size(); i++) { |
| 282 | + sb.append('\n'); |
| 283 | + sb.append(results.get(i).indexedCommentText(i)); |
| 284 | + } |
| 285 | + return sb.toString(); |
| 286 | + } |
| 287 | + |
269 | 288 | private static String buildArrayComment(TreeMap<Integer, B64Result> decodings) { |
270 | 289 | StringBuilder sb = new StringBuilder(); |
271 | 290 | for (Map.Entry<Integer, B64Result> e : decodings.entrySet()) { |
|
0 commit comments