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);
 }