public void scanMethodNode(ClassNode classNode, MethodNode methodNode) throws Exception { InsnList iList = methodNode.instructions; log("Scanning method " + methodNode.name + " of " + classNode.name); LdcInsnNode laststringldconstack = null; for (AbstractInsnNode i : iList.toArray()) { if (i instanceof LdcInsnNode) { LdcInsnNode ldci = (LdcInsnNode) i; if (ldci.cst instanceof String) { laststringldconstack = ldci; } continue; } else if (i instanceof MethodInsnNode) { MethodInsnNode methodi = (MethodInsnNode) i; if (laststringldconstack != null && methodi.opcode() == 0xb8) { // Decryption is always a static call - 0xb8 - invokestatic String decrypterclassname = methodi.owner; String decryptermethodname = methodi.name; if (decrypterclassname.contains( "$")) { // Decrypter is always a static method of other class's inner class byte[] decrypterFileContents = BytecodeViewer.getFileContents(decrypterclassname + ".class"); // We have to create new node for editing // Also, one decrypter method could be used for multiple methods in code, what gives us // only part of string decrypted ClassNode decrypterclassnode = JarUtils.getNode(decrypterFileContents); if (decrypterclassnode != null) { MethodNode decryptermethodnode = null; for (Object uncasted : decrypterclassnode.methods) { if (((MethodNode) uncasted).name.equals(decryptermethodname)) { decryptermethodnode = (MethodNode) uncasted; } } if (decryptermethodnode != null) { String keyString = (getConstantPoolSize(classNode.name) + classNode.name + methodNode.name + getConstantPoolSize(classNode.name)); int newHashCode = keyString.hashCode(); scanDecrypter(decryptermethodnode, newHashCode); try { System.out.println("loading " + decrypterclassname); List<Class<?>> decrypterclasslist = the.bytecode.club.bytecodeviewer.api.BytecodeViewer .loadClassesIntoClassLoader( new ArrayList<ClassNode>( Arrays.asList(new ClassNode[] {decrypterclassnode}))); String decrypted = invokeDecrypter( decrypterclasslist.get(0), decryptermethodname, (String) laststringldconstack.cst); if (decrypted != null) { log("Succesfully invoked decrypter method: " + decrypted); laststringldconstack.cst = decrypted; iList.remove(methodi); } } catch (IndexOutOfBoundsException | ClassNotFoundException | IOException e) { e.printStackTrace(); log("Could not load decrypter class: " + decrypterclassname); } } else { log( "Could not find decrypter method (" + decryptermethodname + ") of class " + decrypterclassname); } } else { log("Could not find decrypter ClassNode of class " + decrypterclassname); } } } } else if (i instanceof InvokeDynamicInsnNode) { InvokeDynamicInsnNode methodi = (InvokeDynamicInsnNode) i; if (methodi.opcode() == 0xba) { // TODO: Safe-reflection deobfuscator here // Allatori replaces invokeinterface and invokestatic with invokedynamic // log(methodi.bsm.getOwner()+" dot "+methodi.bsm.getName()); // iList.set(methodi, new MethodInsnNode(0xb8, methodi.bsm.getOwner(), // methodi.bsm.getName(), methodi.bsm.getDesc(), false)); } } laststringldconstack = null; } }
protected String printLdcInsnNode(LdcInsnNode ldc, ListIterator<?> it) { if (ldc.cst instanceof String) return nameOpcode(ldc.opcode()) + " \"" + StringEscapeUtils.escapeJava(ldc.cst.toString()) + "\" (" + ldc.cst.getClass().getCanonicalName() + ")"; return nameOpcode(ldc.opcode()) + " " + StringEscapeUtils.escapeJava(ldc.cst.toString()) + " (" + ldc.cst.getClass().getCanonicalName() + ")"; }
@SuppressWarnings("unchecked") private static void processReflection(Remapper remapper, ClassNode node) { for (MethodNode mn : (List<MethodNode>) node.methods) { InsnList insns = mn.instructions; ListIterator<AbstractInsnNode> iterator = insns.iterator(); while (iterator.hasNext()) { AbstractInsnNode insn = iterator.next(); if (insn.getOpcode() == Opcodes.INVOKESTATIC) { MethodInsnNode min = (MethodInsnNode) insn; if (min.desc.equals("(Ljava/lang/String;)Ljava/lang/Class;")) { AbstractInsnNode push = insn.getPrevious(); if (push.getOpcode() == Opcodes.LDC) { LdcInsnNode lin = (LdcInsnNode) push; lin.cst = remapper.map(((String) lin.cst).replace('.', '/')).replace('/', '.'); } } } } } }