|
1 | 1 | package com.javadeobfuscator.deobfuscator.transformers.special;
|
2 | 2 |
|
| 3 | +import java.io.ByteArrayInputStream; |
| 4 | +import java.io.ByteArrayOutputStream; |
| 5 | +import java.io.IOException; |
| 6 | +import java.io.InputStream; |
3 | 7 | import java.lang.reflect.Modifier;
|
4 | 8 | import java.util.*;
|
5 | 9 | import java.util.Map.Entry;
|
6 | 10 | import java.util.concurrent.atomic.AtomicInteger;
|
| 11 | +import java.util.zip.ZipFile; |
7 | 12 |
|
8 | 13 | import org.assertj.core.internal.asm.Opcodes;
|
9 | 14 | import org.objectweb.asm.Handle;
|
|
28 | 33 | import com.javadeobfuscator.deobfuscator.executor.defined.MappedFieldProvider;
|
29 | 34 | import com.javadeobfuscator.deobfuscator.executor.defined.MappedMethodProvider;
|
30 | 35 | import com.javadeobfuscator.deobfuscator.executor.defined.PrimitiveFieldProvider;
|
| 36 | +import com.javadeobfuscator.deobfuscator.executor.defined.types.JavaClass; |
31 | 37 | import com.javadeobfuscator.deobfuscator.executor.defined.types.JavaMethodHandle;
|
32 | 38 | import com.javadeobfuscator.deobfuscator.executor.providers.ComparisonProvider;
|
33 | 39 | import com.javadeobfuscator.deobfuscator.executor.providers.DelegatingProvider;
|
|
41 | 47 |
|
42 | 48 | public class RadonTransformerV2 extends Transformer<TransformerConfig>
|
43 | 49 | {
|
| 50 | + public static boolean ANTITAMPER = true; |
44 | 51 | public static boolean EJECTOR = true;
|
45 | 52 | public static boolean ANTI_DEBUG = true;
|
46 | 53 | public static boolean FLOW_OBF = true;
|
@@ -110,6 +117,87 @@ public boolean canCheckEquality(JavaValue first, JavaValue second, Context conte
|
110 | 117 | AtomicInteger number = new AtomicInteger();
|
111 | 118 | AtomicInteger indy = new AtomicInteger();
|
112 | 119 | AtomicInteger str = new AtomicInteger();
|
| 120 | + AtomicInteger antiTamper = new AtomicInteger(); |
| 121 | + if(ANTITAMPER) |
| 122 | + { |
| 123 | + MethodNode atDecr = null; |
| 124 | + ClassNode atOwner = null; |
| 125 | + finder: |
| 126 | + for(ClassNode classNode : classNodes()) |
| 127 | + if(classNode.methods.size() == 1) |
| 128 | + { |
| 129 | + MethodNode method = classNode.methods.get(0); |
| 130 | + if(!method.desc.equals("(Ljava/lang/String;)Ljava/lang/String;")) |
| 131 | + continue; |
| 132 | + Map<Integer, AtomicInteger> insnCount = new HashMap<>(); |
| 133 | + Map<String, AtomicInteger> invokeCount = new HashMap<>(); |
| 134 | + for(AbstractInsnNode i = method.instructions.getFirst(); i != null; i = i.getNext()) { |
| 135 | + int opcode = i.getOpcode(); |
| 136 | + insnCount.putIfAbsent(opcode, new AtomicInteger(0)); |
| 137 | + insnCount.get(opcode).getAndIncrement(); |
| 138 | + if(i instanceof MethodInsnNode) { |
| 139 | + invokeCount.putIfAbsent(((MethodInsnNode) i).name, new AtomicInteger(0)); |
| 140 | + invokeCount.get(((MethodInsnNode) i).name).getAndIncrement(); |
| 141 | + } |
| 142 | + } |
| 143 | + if(insnCount.get(Opcodes.NEWARRAY) == null || insnCount.get(Opcodes.ISTORE) == null |
| 144 | + || insnCount.get(Opcodes.BALOAD) == null || insnCount.get(Opcodes.IOR) == null |
| 145 | + || invokeCount.get("toCharArray") == null || invokeCount.get("getResourceAsStream") == null |
| 146 | + || invokeCount.get("getMethodName") == null) |
| 147 | + continue; |
| 148 | + atDecr = method; |
| 149 | + atOwner = classNode; |
| 150 | + break finder; |
| 151 | + } |
| 152 | + if(atDecr != null) |
| 153 | + { |
| 154 | + ZipFile zipIn = new ZipFile(getDeobfuscator().getConfig().getInput()); |
| 155 | + for(AbstractInsnNode ain : atDecr.instructions.toArray()) |
| 156 | + if (ain.getOpcode() == Opcodes.INVOKEVIRTUAL |
| 157 | + && ((MethodInsnNode)ain).owner.equals("java/lang/Class") |
| 158 | + && ((MethodInsnNode)ain).name.equals("getResourceAsStream") |
| 159 | + && ((MethodInsnNode)ain).desc.equals("(Ljava/lang/String;)Ljava/io/InputStream;")) |
| 160 | + MethodExecutor.customMethodFunc.put(ain, (list, ctx) -> { |
| 161 | + try { |
| 162 | + String clazzName = list.remove(0).as(String.class); |
| 163 | + list.remove(0).as(JavaClass.class); |
| 164 | + clazzName = clazzName.substring(1); |
| 165 | + InputStream in = zipIn.getInputStream(zipIn.getEntry(clazzName)); |
| 166 | + ByteArrayOutputStream baos = cloneInputStream(in); |
| 167 | + InputStream input = new ByteArrayInputStream(baos.toByteArray()); |
| 168 | + return JavaValue.valueOf(input); |
| 169 | + } catch (IOException e) { |
| 170 | + e.printStackTrace(); |
| 171 | + } |
| 172 | + return null; |
| 173 | + }); |
| 174 | + for(ClassNode classNode : classNodes()) |
| 175 | + for(MethodNode m : classNode.methods) { |
| 176 | + InstructionModifier modifier = new InstructionModifier(); |
| 177 | + for(AbstractInsnNode ain : TransformerHelper.instructionIterator(m)) |
| 178 | + if (ain instanceof MethodInsnNode) { |
| 179 | + MethodInsnNode insn = (MethodInsnNode)ain; |
| 180 | + if (insn.owner.equals(atOwner.name) && insn.name.equals(atDecr.name) |
| 181 | + && insn.desc.equals(atDecr.desc)) { |
| 182 | + AbstractInsnNode ldc = ain.getPrevious(); |
| 183 | + if (!(ldc instanceof LdcInsnNode) || !(((LdcInsnNode)ldc).cst instanceof String)) |
| 184 | + continue; |
| 185 | + Context context = new Context(provider); |
| 186 | + context.push(classNode.name, m.name, |
| 187 | + getDeobfuscator().getConstantPool(classNode).getSize()); |
| 188 | + context.dictionary = classpath; |
| 189 | + ((LdcInsnNode)ldc).cst = MethodExecutor.execute(atOwner, atDecr, |
| 190 | + Arrays.asList(JavaValue.valueOf(((LdcInsnNode) ldc).cst)), null, context); |
| 191 | + modifier.remove(ain); |
| 192 | + antiTamper.getAndIncrement(); |
| 193 | + } |
| 194 | + } |
| 195 | + modifier.apply(m); |
| 196 | + } |
| 197 | + } |
| 198 | + classes.remove(atOwner.name); |
| 199 | + classpath.remove(atOwner.name); |
| 200 | + } |
113 | 201 | //Bad Annotations
|
114 | 202 | for(ClassNode classNode : classNodes())
|
115 | 203 | for(MethodNode method : classNode.methods)
|
@@ -187,7 +275,7 @@ public boolean canCheckEquality(JavaValue first, JavaValue second, Context conte
|
187 | 275 | }else if(last.getOpcode() == Opcodes.PUTFIELD)
|
188 | 276 | {
|
189 | 277 | if(list.size() != 3)
|
190 |
| - throw new RuntimeException("Unexpected Ejector pattern (PS)"); |
| 278 | + throw new RuntimeException("Unexpected Ejector pattern (PF)"); |
191 | 279 | AbstractInsnNode prev = list.get(1);
|
192 | 280 | method.instructions.remove(ain.getPrevious());//number
|
193 | 281 | method.instructions.insertBefore(ain, prev.clone(null));
|
@@ -1050,6 +1138,7 @@ && getNextFollowGoto(ain, 2) != null && getNextFollowGoto(ain, 2).getOpcode() ==
|
1050 | 1138 | classes.remove(e.name);
|
1051 | 1139 | classpath.remove(e.name);
|
1052 | 1140 | });
|
| 1141 | + System.out.println("[Special] [RadonTransformerV2] Decrypted " + antiTamper + " strings with anti-tamper"); |
1053 | 1142 | System.out.println("[Special] [RadonTransformerV2] Unejected " + eject + " methods");
|
1054 | 1143 | System.out.println("[Special] [RadonTransformerV2] Removed " + antiDebug + " anti-debug injections");
|
1055 | 1144 | System.out.println("[Special] [RadonTransformerV2] Removed " + tryCatch + " try-catch blocks");
|
@@ -1460,4 +1549,22 @@ private AbstractInsnNode doMath(AbstractInsnNode value1, AbstractInsnNode value2
|
1460 | 1549 | }
|
1461 | 1550 | throw new RuntimeException();
|
1462 | 1551 | }
|
| 1552 | + |
| 1553 | + private static ByteArrayOutputStream cloneInputStream(InputStream input) |
| 1554 | + { |
| 1555 | + try |
| 1556 | + { |
| 1557 | + ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| 1558 | + byte[] buffer = new byte[1024]; |
| 1559 | + int len; |
| 1560 | + while((len = input.read(buffer)) > -1) |
| 1561 | + baos.write(buffer, 0, len); |
| 1562 | + baos.flush(); |
| 1563 | + return baos; |
| 1564 | + }catch(Exception e) |
| 1565 | + { |
| 1566 | + e.printStackTrace(); |
| 1567 | + return null; |
| 1568 | + } |
| 1569 | + } |
1463 | 1570 | }
|
0 commit comments