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; } }
private int getConstantPoolSize(String className) { byte[] fileContents = BytecodeViewer.getFileContents(className + ".class"); return readUnsignedShort(fileContents, 8); }