1616
1717package com .javadeobfuscator .deobfuscator .transformers .zelix .string ;
1818
19+ import com .fasterxml .jackson .annotation .*;
20+ import com .google .common .base .*;
1921import com .javadeobfuscator .deobfuscator .config .*;
2022import com .javadeobfuscator .deobfuscator .exceptions .*;
21- import com .javadeobfuscator .deobfuscator .executor .values .*;
2223import com .javadeobfuscator .deobfuscator .matcher .*;
2324import com .javadeobfuscator .deobfuscator .transformers .*;
2425import com .javadeobfuscator .deobfuscator .utils .*;
2526import com .javadeobfuscator .javavm .*;
2627import com .javadeobfuscator .javavm .exceptions .*;
2728import com .javadeobfuscator .javavm .mirrors .*;
2829import com .javadeobfuscator .javavm .values .*;
29- import com .javadeobfuscator .javavm .values .JavaArray ;
3030import org .objectweb .asm .*;
3131import org .objectweb .asm .tree .*;
3232import org .objectweb .asm .tree .analysis .*;
3333import org .objectweb .asm .tree .analysis .Frame ;
3434
3535import java .util .*;
36- import java .util .stream .* ;
36+ import java .util .function . Supplier ;
3737
3838/**
3939 * This is a transformer for the enhanced version of Zelix string encryption
4040 */
41- public class EnhancedStringEncryptionTransformer extends Transformer <TransformerConfig > implements Opcodes {
41+ @ TransformerConfig .ConfigOptions (configClass = EnhancedStringEncryptionTransformer .Config .class )
42+ public class EnhancedStringEncryptionTransformer extends Transformer <EnhancedStringEncryptionTransformer .Config > implements Opcodes {
4243 private static final InstructionPattern DECRYPT_PATTERN = new InstructionPattern (
4344 new LoadIntStep (),
4445 new LoadIntStep (),
45- new InvocationStep (INVOKESTATIC , null , null , "(II)Ljava/lang/String;" , false )
46+ new CapturingStep ( new InvocationStep (INVOKESTATIC , null , null , "(II)Ljava/lang/String;" , false ), "invoke" )
4647 );
4748
4849 @ Override
@@ -54,25 +55,74 @@ public boolean transform() throws Throwable, WrongTransformerException {
5455 try {
5556 vm .initialize (JavaClass .forName (vm , classNode .name ));
5657 } catch (VMException e ) {
58+ JavaClass .forName (vm , classNode .name ).setInitializationState (JavaClass .InitializationState .INITIALIZED , null ); // of course we initialized it
5759 logger .debug ("Exception while initializing {}, should be fine" , classNode .name );
5860 logger .debug (vm .exceptionToString (e ));
5961 } catch (Throwable e ) {
62+ JavaClass .forName (vm , classNode .name ).setInitializationState (JavaClass .InitializationState .INITIALIZED , null ); // of course we initialized it
6063 logger .debug ("(Severe) Exception while initializing {}, should be fine" , classNode .name , e );
6164 }
65+
6266 for (MethodNode methodNode : new ArrayList <>(classNode .methods )) {
6367 InstructionModifier modifier = new InstructionModifier ();
6468
69+ // If we want the slow version, memoize the analysis
70+ Supplier <Frame <SourceValue >[]> framesSupplier = Suppliers .memoize (() -> {
71+ try {
72+ return new Analyzer <>(new SourceInterpreter ()).analyze (classNode .name , methodNode );
73+ } catch (AnalyzerException e ) {
74+ oops ("unexpected analyzer exception" , e );
75+ return null ;
76+ }
77+ })::get ;
78+
6579 for (AbstractInsnNode insnNode : TransformerHelper .instructionIterator (methodNode )) {
66- InstructionMatcher matcher = DECRYPT_PATTERN .matcher (insnNode );
67- if (!matcher .find ()) continue ;
80+ AbstractInsnNode invocation ;
81+ MethodNode decryptNode ;
82+
83+ if (!getConfig ().isSlowlyDetermineMagicNumbers ()) {
84+ InstructionMatcher matcher = DECRYPT_PATTERN .matcher (insnNode );
85+ if (!matcher .find ()) continue ;
86+
87+ decryptNode = new MethodNode (ASM6 , ACC_PUBLIC | ACC_STATIC , "Decryptor" , "()Ljava/lang/String;" , null , null );
88+ InsnList decryptInsns = new InsnList ();
89+ for (AbstractInsnNode matched : matcher .getCapturedInstructions ("all" )) {
90+ decryptInsns .add (matched .clone (null ));
91+ }
92+ decryptInsns .add (new InsnNode (ARETURN ));
93+ decryptNode .instructions = decryptInsns ;
94+ invocation = matcher .getCapturedInstruction ("invoke" );
95+ } else {
96+ if (insnNode .getOpcode () != INVOKESTATIC ) continue ;
97+
98+ MethodInsnNode methodInsnNode = (MethodInsnNode ) insnNode ;
99+ if (!methodInsnNode .desc .equals ("(II)Ljava/lang/String;" )) continue ;
100+
101+ Frame <SourceValue >[] frames = framesSupplier .get ();
102+ if (frames == null ) continue ;
103+
104+ Frame <SourceValue > frame = frames [methodNode .instructions .indexOf (insnNode )];
105+ if (frame == null ) continue ;
106+
107+ SourceValue cst1 = frame .getStack (frame .getStackSize () - 2 );
108+ SourceValue cst2 = frame .getStack (frame .getStackSize () - 1 );
109+ if (cst1 .insns .size () != 1 ) continue ;
110+ if (cst2 .insns .size () != 1 ) continue ;
68111
69- MethodNode decryptNode = new MethodNode (ASM6 , ACC_PUBLIC | ACC_STATIC , "Decryptor" , "()Ljava/lang/String;" , null , null );
70- InsnList decryptInsns = new InsnList ();
71- for (AbstractInsnNode matched : matcher .getCapturedInstructions ("all" )) {
72- decryptInsns .add (matched .clone (null ));
112+ AbstractInsnNode insn1 = cst1 .insns .iterator ().next ();
113+ AbstractInsnNode insn2 = cst2 .insns .iterator ().next ();
114+ if (insn1 .getOpcode () != SIPUSH ) continue ;
115+ if (insn2 .getOpcode () != SIPUSH ) continue ;
116+
117+ decryptNode = new MethodNode (ASM6 , ACC_PUBLIC | ACC_STATIC , "Decryptor" , "()Ljava/lang/String;" , null , null );
118+ InsnList decryptInsns = new InsnList ();
119+ decryptInsns .add (insn1 .clone (null ));
120+ decryptInsns .add (insn2 .clone (null ));
121+ decryptInsns .add (methodInsnNode .clone (null ));
122+ decryptInsns .add (new InsnNode (ARETURN ));
123+ decryptNode .instructions = decryptInsns ;
124+ invocation = methodInsnNode ;
73125 }
74- decryptInsns .add (new InsnNode (ARETURN ));
75- decryptNode .instructions = decryptInsns ;
76126
77127 JavaWrapper result ;
78128
@@ -95,8 +145,7 @@ public boolean transform() throws Throwable, WrongTransformerException {
95145 String decrypted = vm .convertJavaObjectToString (result );
96146 logger .info ("Decrypted string in {} {}{}: {}" , classNode .name , methodNode .name , methodNode .desc , decrypted );
97147
98- modifier .removeAll (matcher .getCapturedInstructions ("all" ));
99- modifier .replace (matcher .getCapturedInstructions ("all" ).get (0 ), new LdcInsnNode (decrypted ));
148+ modifier .replace (invocation , new InsnNode (POP2 ), new LdcInsnNode (decrypted ));
100149 }
101150
102151 modifier .apply (methodNode );
@@ -106,4 +155,22 @@ public boolean transform() throws Throwable, WrongTransformerException {
106155 vm .shutdown ();
107156 return false ;
108157 }
158+
159+ public static class Config extends TransformerConfig {
160+
161+ @ JsonProperty ("slowly-determine-magic-numbers" )
162+ private boolean slowlyDetermineMagicNumbers ;
163+
164+ public Config () {
165+ super (EnhancedStringEncryptionTransformer .class );
166+ }
167+
168+ public boolean isSlowlyDetermineMagicNumbers () {
169+ return slowlyDetermineMagicNumbers ;
170+ }
171+
172+ public void setSlowlyDetermineMagicNumbers (boolean slowlyDetermineMagicNumbers ) {
173+ this .slowlyDetermineMagicNumbers = slowlyDetermineMagicNumbers ;
174+ }
175+ }
109176}
0 commit comments