11package uwu .narumi .deobfuscator .core .other .impl .universal .pool ;
22
3- import org .objectweb .asm .tree .AbstractInsnNode ;
4- import org .objectweb .asm .tree .FieldInsnNode ;
5- import org .objectweb .asm .tree .LdcInsnNode ;
6- import org .objectweb .asm .tree .MethodInsnNode ;
7- import org .objectweb .asm .tree .TypeInsnNode ;
3+ import org .objectweb .asm .tree .*;
84import uwu .narumi .deobfuscator .api .asm .MethodContext ;
95import uwu .narumi .deobfuscator .api .asm .matcher .Match ;
10- import uwu .narumi .deobfuscator .api .asm .matcher .MatchContext ;
11- import uwu .narumi .deobfuscator .api .asm .matcher .impl .FieldMatch ;
12- import uwu .narumi .deobfuscator .api .asm .matcher .impl .FrameMatch ;
13- import uwu .narumi .deobfuscator .api .asm .matcher .impl .NumberMatch ;
14- import uwu .narumi .deobfuscator .api .asm .matcher .impl .OpcodeMatch ;
15- import uwu .narumi .deobfuscator .api .asm .matcher .impl .StringMatch ;
6+ import uwu .narumi .deobfuscator .api .asm .matcher .impl .*;
167import uwu .narumi .deobfuscator .api .transformer .Transformer ;
178
9+ import java .util .HashSet ;
1810import java .util .Objects ;
11+ import java .util .Set ;
12+ import java .util .concurrent .atomic .AtomicBoolean ;
1913
2014/**
2115 * Replaces string pool references with actual values.
@@ -33,73 +27,79 @@ public class UniversalStringPoolTransformer extends Transformer {
3327 @ Override
3428 protected void transform () throws Exception {
3529 scopedClasses ().forEach (classWrapper -> {
36- MatchContext stringPoolMatchCtx = classWrapper .methods ().stream ()
30+ Set <MethodNode > toRemoveMn = new HashSet <>();
31+ Set <FieldNode > toRemoveFn = new HashSet <>();
32+ classWrapper .methods ().stream ()
3733 .map (methodNode -> STRING_POOL_METHOD_MATCH .findFirstMatch (MethodContext .of (classWrapper , methodNode )))
38- .filter (Objects ::nonNull )
39- .findFirst ()
40- .orElse (null );
41-
42- // No number pool method found
43- if (stringPoolMatchCtx == null ) return ;
44-
45- //System.out.println("Found string pool method in " + classWrapper.name() + "." + stringPoolMatchCtx.insnContext().methodNode().name);
46-
47- int stringPoolSize = stringPoolMatchCtx .captures ().get ("size" ).insn ().asInteger ();
48- FieldInsnNode stringPoolFieldInsn = (FieldInsnNode ) stringPoolMatchCtx .captures ().get ("stringPoolField" ).insn ();
49-
50- // Get whole number pool
51- String [] stringPool = new String [stringPoolSize ];
52- STORE_STRING_TO_ARRAY_MATCH .findAllMatches (stringPoolMatchCtx .insnContext ().methodContext ()).forEach (storeNumberMatchCtx -> {
53- int index = storeNumberMatchCtx .captures ().get ("index" ).insn ().asInteger ();
54- String value = storeNumberMatchCtx .captures ().get ("value" ).insn ().asString ();
55-
56- stringPool [index ] = value ;
57- });
58-
59- for (String string : stringPool ) {
60- if (string == null ) {
61- // String pool is not fully initialized
62- return ;
63- }
64- }
65-
66- Match stringPoolReferenceMatch = OpcodeMatch .of (AALOAD ) // AALOAD - Load array reference
67- // Index
68- .and (FrameMatch .stack (0 , NumberMatch .of ().capture ("index" )
69- // Load number pool field
70- .and (FrameMatch .stack (0 , FieldMatch .getStatic ().owner (stringPoolFieldInsn .owner ).name (stringPoolFieldInsn .name ).desc (stringPoolFieldInsn .desc )))
71- ));
72-
73- // Replace number pool references with actual values
74- classWrapper .methods ().forEach (methodNode -> {
75- MethodContext methodContext = MethodContext .of (classWrapper , methodNode );
76-
77- stringPoolReferenceMatch .findAllMatches (methodContext ).forEach (numberPoolReferenceCtx -> {
78- int index = numberPoolReferenceCtx .captures ().get ("index" ).insn ().asInteger ();
79- String value = stringPool [index ];
80-
81- // Value
82- methodNode .instructions .insert (numberPoolReferenceCtx .insn (), new LdcInsnNode (value ));
83- numberPoolReferenceCtx .removeAll ();
84- markChange ();
85- });
86- });
87-
88- // Cleanup
89- classWrapper .methods ().remove (stringPoolMatchCtx .insnContext ().methodNode ());
90- classWrapper .fields ().removeIf (fieldNode -> fieldNode .name .equals (stringPoolFieldInsn .name ) && fieldNode .desc .equals (stringPoolFieldInsn .desc ));
91- // Remove string pool initialization from clinit
92- classWrapper .findClInit ().ifPresent (clinit -> {
93- for (AbstractInsnNode insn : clinit .instructions .toArray ()) {
94- if (insn .getOpcode () == INVOKESTATIC && insn instanceof MethodInsnNode methodInsn &&
95- methodInsn .name .equals (stringPoolMatchCtx .insnContext ().methodNode ().name ) && methodInsn .desc .equals (stringPoolMatchCtx .insnContext ().methodNode ().desc ) &&
96- methodInsn .owner .equals (classWrapper .name ())
97- ) {
98- // Remove invocation
99- clinit .instructions .remove (insn );
100- }
101- }
102- });
34+ .filter (Objects ::nonNull ).forEach (stringPoolMatchCtx -> {
35+ AtomicBoolean changedForThisContext = new AtomicBoolean (false );
36+ //System.out.println("Found string pool method in " + classWrapper.name() + "." + stringPoolMatchCtx.insnContext().methodNode().name);
37+
38+ int stringPoolSize = stringPoolMatchCtx .captures ().get ("size" ).insn ().asInteger ();
39+ FieldInsnNode stringPoolFieldInsn = (FieldInsnNode ) stringPoolMatchCtx .captures ().get ("stringPoolField" ).insn ();
40+
41+ // Get whole number pool
42+ String [] stringPool = new String [stringPoolSize ];
43+ STORE_STRING_TO_ARRAY_MATCH .findAllMatches (stringPoolMatchCtx .insnContext ().methodContext ()).forEach (storeNumberMatchCtx -> {
44+ int index = storeNumberMatchCtx .captures ().get ("index" ).insn ().asInteger ();
45+ String value = storeNumberMatchCtx .captures ().get ("value" ).insn ().asString ();
46+ //System.out.println(classWrapper.name());
47+ stringPool [index ] = value ;
48+ });
49+
50+ for (String string : stringPool ) {
51+ if (string == null ) {
52+ // String pool is not fully initialized
53+ return ;
54+ }
55+ }
56+
57+ Match stringPoolReferenceMatch = OpcodeMatch .of (AALOAD ) // AALOAD - Load array reference
58+ // Index
59+ .and (FrameMatch .stack (0 , NumberMatch .of ().capture ("index" )
60+ // Load number pool field
61+ .and (FrameMatch .stack (0 , FieldMatch .getStatic ().owner (stringPoolFieldInsn .owner ).name (stringPoolFieldInsn .name ).desc (stringPoolFieldInsn .desc )))
62+ ));
63+
64+ // Replace number pool references with actual values
65+ classWrapper .methods ().forEach (methodNode -> {
66+ MethodContext methodContext = MethodContext .of (classWrapper , methodNode );
67+
68+ stringPoolReferenceMatch .findAllMatches (methodContext ).forEach (numberPoolReferenceCtx -> {
69+ int index = numberPoolReferenceCtx .captures ().get ("index" ).insn ().asInteger ();
70+ String value = stringPool [index ];
71+
72+ // Value
73+ methodNode .instructions .insert (numberPoolReferenceCtx .insn (), new LdcInsnNode (value ));
74+ numberPoolReferenceCtx .removeAll ();
75+ markChange ();
76+ changedForThisContext .set (true );
77+ });
78+ });
79+
80+ if (changedForThisContext .get ()) {
81+ toRemoveMn .add (stringPoolMatchCtx .insnContext ().methodNode ());
82+ classWrapper .fields ().forEach (fieldNode -> {
83+ if (fieldNode .name .equals (stringPoolFieldInsn .name ) && fieldNode .desc .equals (stringPoolFieldInsn .desc )) {
84+ toRemoveFn .add (fieldNode );
85+ }
86+ });
87+ // Remove string pool initialization from clinit
88+ classWrapper .findClInit ().ifPresent (clinit -> {
89+ for (AbstractInsnNode insn : clinit .instructions .toArray ()) {
90+ if (insn .getOpcode () == INVOKESTATIC && insn instanceof MethodInsnNode methodInsn &&
91+ methodInsn .name .equals (stringPoolMatchCtx .insnContext ().methodNode ().name ) && methodInsn .desc .equals (stringPoolMatchCtx .insnContext ().methodNode ().desc ) &&
92+ methodInsn .owner .equals (classWrapper .name ())
93+ ) {
94+ // Remove invocation
95+ clinit .instructions .remove (insn );
96+ }
97+ }
98+ });
99+ }
100+ });
101+ classWrapper .methods ().removeAll (toRemoveMn );
102+ classWrapper .fields ().removeAll (toRemoveFn );
103103 });
104104 }
105105}
0 commit comments