From 91f42e91d0675afe7f5d69d0b24cdbedfb72c9d3 Mon Sep 17 00:00:00 2001 From: Szperak Date: Mon, 31 Aug 2015 16:32:25 +0200 Subject: [PATCH 1/5] modified loadClassesIntoClassLoader loadClassesIntoClassLoader is now using specified list of ClassNode --- .../bytecodeviewer/api/BytecodeViewer.java | 89 +++++++++++-------- .../preinstalled/ZStringArrayDecrypter.java | 46 +++++----- 2 files changed, 75 insertions(+), 60 deletions(-) diff --git a/src/the/bytecode/club/bytecodeviewer/api/BytecodeViewer.java b/src/the/bytecode/club/bytecodeviewer/api/BytecodeViewer.java index 2cbc345c..dc5d8296 100644 --- a/src/the/bytecode/club/bytecodeviewer/api/BytecodeViewer.java +++ b/src/the/bytecode/club/bytecodeviewer/api/BytecodeViewer.java @@ -1,6 +1,7 @@ package the.bytecode.club.bytecodeviewer.api; import java.io.File; +import java.io.IOException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; @@ -62,50 +63,60 @@ public class BytecodeViewer { return cl; } + + /** * Re-instances the URLClassLoader and loads a jar to it. - * @param path + * + * @param nodeList + * The list of ClassNodes to be loaded * @return The loaded classes into the new URLClassLoader instance * @author Cafebabe + * @throws IOException + * @throws ClassNotFoundException */ - public static List> loadClassesIntoClassLoader() { - try { - File f = new File( - the.bytecode.club.bytecodeviewer.BytecodeViewer.tempDirectory + - the.bytecode.club.bytecodeviewer.BytecodeViewer.fs + - "loaded_temp.jar"); - JarUtils.saveAsJar(BytecodeViewer.getLoadedClasses(), f.getAbsolutePath()); - JarFile jarFile = new JarFile(""+f.getAbsolutePath()); - Enumeration e = jarFile.entries(); - URL[] urls = { new URL("jar:file:" + ""+f.getAbsolutePath()+"!/") }; - cl = URLClassLoader.newInstance(urls); - List> ret = new ArrayList>(); - - while (e.hasMoreElements()) - { - JarEntry je = (JarEntry) e.nextElement(); - if(je.isDirectory() || !je.getName().endsWith(".class")) - continue; - String className = je.getName().replace("/", ".").replace(".class", ""); - className = className.replace('/', '.'); - try{ - ret.add(cl.loadClass(className)); - } - catch(Exception e2) - { - new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e2); - } - } - jarFile.close(); - - return ret; - } - catch(Exception e) - { - new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e); - } - return null; - } + @SuppressWarnings("deprecation") + public static List> loadClassesIntoClassLoader( + ArrayList nodeList) throws IOException, + ClassNotFoundException { + + File f = new File( + the.bytecode.club.bytecodeviewer.BytecodeViewer.tempDirectory + + the.bytecode.club.bytecodeviewer.BytecodeViewer.fs + + "loaded_temp.jar"); + JarUtils.saveAsJarClassesOnly(nodeList, f.getAbsolutePath()); + + JarFile jarFile = new JarFile("" + f.getAbsolutePath()); + Enumeration e = jarFile.entries(); + cl = URLClassLoader.newInstance(new URL[]{ f.toURL() }); + List> ret = new ArrayList>(); + + while (e.hasMoreElements()) { + JarEntry je = (JarEntry) e.nextElement(); + if (je.isDirectory() || !je.getName().endsWith(".class")) + continue; + String className = je.getName().replace("/", ".").replace(".class", ""); + className = className.replace('/', '.'); + ret.add(cl.loadClass(className)); + + } + jarFile.close(); + + return ret; + + } + + + /** + * Re-instances the URLClassLoader and loads a jar to it. + * @return The loaded classes into the new URLClassLoader instance + * @author Cafebabe + * @throws IOException + * @throws ClassNotFoundException + */ + public static List> loadAllClassesIntoClassLoader() throws ClassNotFoundException, IOException { + return loadClassesIntoClassLoader(getLoadedClasses()); + } /** * Creates a new instance of the ClassNode loader. diff --git a/src/the/bytecode/club/bytecodeviewer/plugin/preinstalled/ZStringArrayDecrypter.java b/src/the/bytecode/club/bytecodeviewer/plugin/preinstalled/ZStringArrayDecrypter.java index e30cd6ed..a986f4ff 100644 --- a/src/the/bytecode/club/bytecodeviewer/plugin/preinstalled/ZStringArrayDecrypter.java +++ b/src/the/bytecode/club/bytecodeviewer/plugin/preinstalled/ZStringArrayDecrypter.java @@ -3,6 +3,7 @@ package the.bytecode.club.bytecodeviewer.plugin.preinstalled; import org.objectweb.asm.tree.ClassNode; import the.bytecode.club.bytecodeviewer.BytecodeViewer; +import the.bytecode.club.bytecodeviewer.api.ExceptionUI; import the.bytecode.club.bytecodeviewer.api.Plugin; import the.bytecode.club.bytecodeviewer.api.PluginConsole; @@ -63,28 +64,31 @@ public class ZStringArrayDecrypter extends Plugin { if (result == 0) { boolean needsWarning = false; - for (Class debug : the.bytecode.club.bytecodeviewer.api.BytecodeViewer.loadClassesIntoClassLoader()) { - try { - Field[] fields = debug.getDeclaredFields(); - for ( Field field : fields ) { - if ( field.getName().equals("z") ) { - out.append(debug.getName() + ":" + BytecodeViewer.nl); - field.setAccessible(true); - if(field.get(null) != null && field.get(null) instanceof String[] && Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers())) { - String[] fieldVal = (String[]) field.get(null); - for ( int i = 0; i < fieldVal.length; i++ ) { - out.append(" z[" + i + "] = " + fieldVal[i] + BytecodeViewer.nl); + try { + for (Class debug : the.bytecode.club.bytecodeviewer.api.BytecodeViewer.loadAllClassesIntoClassLoader()) { + try { + Field[] fields = debug.getDeclaredFields(); + for ( Field field : fields ) { + if ( field.getName().equals("z") ) { + out.append(debug.getName() + ":" + BytecodeViewer.nl); + field.setAccessible(true); + if(field.get(null) != null && field.get(null) instanceof String[] && Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers())) { + String[] fieldVal = (String[]) field.get(null); + for ( int i = 0; i < fieldVal.length; i++ ) { + out.append(" z[" + i + "] = " + fieldVal[i] + BytecodeViewer.nl); + } } - } - } - } - } catch(NoClassDefFoundError | Exception e) { - System.err.println("Failed loading class " + debug.getName()); - e.printStackTrace(); - needsWarning = true; - } - } - + } + } + } catch(NoClassDefFoundError | Exception e) { + System.err.println("Failed loading class " + debug.getName()); + e.printStackTrace(); + needsWarning = true; + } + } + } catch (Exception e){ + new ExceptionUI(e); + } if(needsWarning) { BytecodeViewer.showMessage("Some classes failed to decrypt, if you'd like to decrypt all of them"+BytecodeViewer.nl+"makes sure you include ALL the libraries it requires."); } From a0d552e573fdc65b8cd24c29371fd8e424e3abdc Mon Sep 17 00:00:00 2001 From: Szperak Date: Mon, 31 Aug 2015 16:35:39 +0200 Subject: [PATCH 2/5] Class files are now added to the list file contents --- .../club/bytecodeviewer/JarUtils.java | 36 ++++++++++--------- .../gui/FileNavigationPane.java | 30 ---------------- 2 files changed, 19 insertions(+), 47 deletions(-) diff --git a/src/the/bytecode/club/bytecodeviewer/JarUtils.java b/src/the/bytecode/club/bytecodeviewer/JarUtils.java index 8e148d00..98240b66 100644 --- a/src/the/bytecode/club/bytecodeviewer/JarUtils.java +++ b/src/the/bytecode/club/bytecodeviewer/JarUtils.java @@ -63,26 +63,28 @@ public class JarUtils { try { final String name = entry.getName(); final byte[] bytes = getBytes(jis); - if (!name.endsWith(".class")) { - if(!entry.isDirectory()) - files.put(name, bytes); - } else { - String cafebabe = String.format("%02X", bytes[0]) - + String.format("%02X", bytes[1]) - + String.format("%02X", bytes[2]) - + String.format("%02X", bytes[3]); - if(cafebabe.toLowerCase().equals("cafebabe")) { - try { - final ClassNode cn = getNode(bytes); - container.classes.add(cn); - } catch(Exception e) { - e.printStackTrace(); - } + if(!files.containsKey(name)){ + if (!name.endsWith(".class")) { + if(!entry.isDirectory()) + files.put(name, bytes); } else { - System.out.println(jarFile+">"+name+": Header does not start with CAFEBABE, ignoring."); + String cafebabe = String.format("%02X", bytes[0]) + + String.format("%02X", bytes[1]) + + String.format("%02X", bytes[2]) + + String.format("%02X", bytes[3]); + if(cafebabe.toLowerCase().equals("cafebabe")) { + try { + final ClassNode cn = getNode(bytes); + container.classes.add(cn); + } catch(Exception e) { + e.printStackTrace(); + } + } else { + System.out.println(jarFile+">"+name+": Header does not start with CAFEBABE, ignoring."); + } + files.put(name, bytes); } } - } catch(Exception e) { new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e); } finally { diff --git a/src/the/bytecode/club/bytecodeviewer/gui/FileNavigationPane.java b/src/the/bytecode/club/bytecodeviewer/gui/FileNavigationPane.java index 70a37c1c..149a121b 100644 --- a/src/the/bytecode/club/bytecodeviewer/gui/FileNavigationPane.java +++ b/src/the/bytecode/club/bytecodeviewer/gui/FileNavigationPane.java @@ -311,36 +311,6 @@ public class FileNavigationPane extends VisibleComponent implements ImageRenderer renderer = new ImageRenderer(); tree.setCellRenderer(renderer); - if(!container.classes.isEmpty()) { - for(ClassNode c : container.classes) { - String name = c.name; - final String[] spl = name.split("/"); - if (spl.length < 2) { - root.add(new MyTreeNode(name+".class")); - } else { - MyTreeNode parent = root; - for (int i1 = 0; i1 < spl.length; i1++) { - String s = spl[i1]; - MyTreeNode child = null; - for (int i = 0; i < parent.getChildCount(); i++) { - if (((MyTreeNode) parent.getChildAt(i)).getUserObject() - .equals(s)) { - child = (MyTreeNode) parent.getChildAt(i); - break; - } - } - if (child == null) { - if(i1 == spl.length-1) - child = new MyTreeNode(s+".class"); - else - child = new MyTreeNode(s); - parent.add(child); - } - parent = child; - } - } - } - } if(!container.files.isEmpty()) { for (final Entry entry : container.files.entrySet()) { From a98ff88ba4b6e749287b29d4d8adc4fcaefe2e25 Mon Sep 17 00:00:00 2001 From: Szperak Date: Mon, 31 Aug 2015 16:38:33 +0200 Subject: [PATCH 3/5] Bugfix: invokedynamic shows wrong method name Obfuscators exploits this bug to make deobfuscator see method other than JVM actually calls. --- .../bytecodeviewer/decompilers/bytecode/InstructionPrinter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/the/bytecode/club/bytecodeviewer/decompilers/bytecode/InstructionPrinter.java b/src/the/bytecode/club/bytecodeviewer/decompilers/bytecode/InstructionPrinter.java index b607152e..baa5d2a3 100644 --- a/src/the/bytecode/club/bytecodeviewer/decompilers/bytecode/InstructionPrinter.java +++ b/src/the/bytecode/club/bytecodeviewer/decompilers/bytecode/InstructionPrinter.java @@ -304,7 +304,7 @@ public class InstructionPrinter { protected String printInvokeDynamicInsNode(InvokeDynamicInsnNode idin) { StringBuilder sb = new StringBuilder(); - sb.append(nameOpcode(idin.opcode()) + " " + idin.name + "("); + sb.append(nameOpcode(idin.opcode()) + " " + idin.bsm.getName() + "("); String desc = idin.desc; String partedDesc = idin.desc.substring(2); From 5685681f5c6776cc3a5ee9f6e54ae8f044dedb5c Mon Sep 17 00:00:00 2001 From: Szperak Date: Mon, 31 Aug 2015 16:42:27 +0200 Subject: [PATCH 4/5] Bugfix: super.loadClass calls loadClass in superclass ... superclass's loadClass calls right loadClass, right loadClass calls super.loadClass, super.loadClass calls loadClass in superclass, superclass's loadClass calls right loadClass, right loadClass calls super.loadClass, super.loadClass calls loadClass in superclass, superclass's loadClass calls right loadClass, right loadClass calls super.loadClass, super.loadClass calls loadClass in superclass, superclass's loadClass calls right loadClass, right loadClass calls super.loadClass, super.loadClass calls loadClass in superclass, superclass's loadClass calls right loadClass, right loadClass calls super.loadClass, super.loadClass calls loadClass in superclass, superclass's loadClass calls right loadClass, right loadClass calls super.loadClass, super.loadClass calls loadClass in superclass... recursion :smile: --- src/the/bytecode/club/bytecodeviewer/api/ClassNodeLoader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/the/bytecode/club/bytecodeviewer/api/ClassNodeLoader.java b/src/the/bytecode/club/bytecodeviewer/api/ClassNodeLoader.java index 1a06011f..9c05d2d6 100644 --- a/src/the/bytecode/club/bytecodeviewer/api/ClassNodeLoader.java +++ b/src/the/bytecode/club/bytecodeviewer/api/ClassNodeLoader.java @@ -110,7 +110,7 @@ public final class ClassNodeLoader extends ClassLoader { if (classes.containsKey(name)) { return nodeToClass(classes.get(name)); } else { - return super.loadClass(name); + return super.findClass(name); } } From b75cb67df33821722ad47724d6afa5c2cc342f0b Mon Sep 17 00:00:00 2001 From: Szperak Date: Mon, 31 Aug 2015 16:42:54 +0200 Subject: [PATCH 5/5] Added Allatori string decrypter --- .../gui/AllatoriStringDecrypterOptions.java | 77 ++++++ .../bytecodeviewer/gui/MainViewerGUI.java | 8 +- .../preinstalled/AllatoriStringDecrypter.java | 222 +++++++++++++++++- 3 files changed, 304 insertions(+), 3 deletions(-) create mode 100644 src/the/bytecode/club/bytecodeviewer/gui/AllatoriStringDecrypterOptions.java diff --git a/src/the/bytecode/club/bytecodeviewer/gui/AllatoriStringDecrypterOptions.java b/src/the/bytecode/club/bytecodeviewer/gui/AllatoriStringDecrypterOptions.java new file mode 100644 index 00000000..60842357 --- /dev/null +++ b/src/the/bytecode/club/bytecodeviewer/gui/AllatoriStringDecrypterOptions.java @@ -0,0 +1,77 @@ +package the.bytecode.club.bytecodeviewer.gui; + +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JTextField; + +import the.bytecode.club.bytecodeviewer.Resources; +import the.bytecode.club.bytecodeviewer.plugin.PluginManager; +import the.bytecode.club.bytecodeviewer.plugin.preinstalled.AllatoriStringDecrypter; + +/*************************************************************************** + * Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite * + * Copyright (C) 2014 Kalen 'Konloch' Kinloch - http://bytecodeviewer.com * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +/** + * The UI for replace strings plugin. + * + * @author Konloch + * + */ + +public class AllatoriStringDecrypterOptions extends JFrame { + public AllatoriStringDecrypterOptions() { + this.setIconImages(Resources.iconList); + setSize(new Dimension(250, 120)); + setResizable(false); + setTitle("Allatori decrypter"); + getContentPane().setLayout(null); + + JButton btnNewButton = new JButton("Decrypt"); + btnNewButton.setBounds(6, 56, 232, 23); + getContentPane().add(btnNewButton); + + + JLabel lblNewLabel = new JLabel("Class:"); + lblNewLabel.setBounds(6, 20, 67, 14); + getContentPane().add(lblNewLabel); + + textField = new JTextField(); + textField.setToolTipText("* will search all classes"); + textField.setText("*"); + textField.setBounds(80, 17, 158, 20); + getContentPane().add(textField); + textField.setColumns(10); + + + btnNewButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent arg0) { + PluginManager.runPlugin(new AllatoriStringDecrypter(textField.getText())); + dispose(); + } + }); + this.setLocationRelativeTo(null); + } + + private static final long serialVersionUID = -2662514582647810868L; + private JTextField textField; +} diff --git a/src/the/bytecode/club/bytecodeviewer/gui/MainViewerGUI.java b/src/the/bytecode/club/bytecodeviewer/gui/MainViewerGUI.java index 22164216..b72872d1 100644 --- a/src/the/bytecode/club/bytecodeviewer/gui/MainViewerGUI.java +++ b/src/the/bytecode/club/bytecodeviewer/gui/MainViewerGUI.java @@ -2029,8 +2029,12 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier { }); mntmNewMenuItem_2.addActionListener(new ActionListener() { @Override - public void actionPerformed(ActionEvent e) { - PluginManager.runPlugin(new AllatoriStringDecrypter()); + public void actionPerformed(ActionEvent arg0) { + if(BytecodeViewer.getLoadedClasses().isEmpty()) { + BytecodeViewer.showMessage("First open a class, jar, zip, apk or dex file."); + return; + } + new AllatoriStringDecrypterOptions().setVisible(true); } }); mntmNewMenuItem_1.addActionListener(new ActionListener() { diff --git a/src/the/bytecode/club/bytecodeviewer/plugin/preinstalled/AllatoriStringDecrypter.java b/src/the/bytecode/club/bytecodeviewer/plugin/preinstalled/AllatoriStringDecrypter.java index a2dbed53..f7f67d6c 100644 --- a/src/the/bytecode/club/bytecodeviewer/plugin/preinstalled/AllatoriStringDecrypter.java +++ b/src/the/bytecode/club/bytecodeviewer/plugin/preinstalled/AllatoriStringDecrypter.java @@ -1,11 +1,27 @@ package the.bytecode.club.bytecodeviewer.plugin.preinstalled; +import java.io.IOException; +import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import javax.swing.JDialog; +import javax.swing.JOptionPane; + +import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.InsnList; +import org.objectweb.asm.tree.InvokeDynamicInsnNode; +import org.objectweb.asm.tree.LdcInsnNode; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.MethodNode; import the.bytecode.club.bytecodeviewer.BytecodeViewer; +import the.bytecode.club.bytecodeviewer.JarUtils; +import the.bytecode.club.bytecodeviewer.api.ExceptionUI; import the.bytecode.club.bytecodeviewer.api.Plugin; +import the.bytecode.club.bytecodeviewer.api.PluginConsole; /*************************************************************************** * Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite * @@ -29,14 +45,218 @@ import the.bytecode.club.bytecodeviewer.api.Plugin; * Coming soon. * * @author Konloch + * @author Szperak * */ public class AllatoriStringDecrypter extends Plugin { + + PluginConsole frame = new PluginConsole("Allatori decrypter"); + StringBuilder out = new StringBuilder(); + + private String className; + + public AllatoriStringDecrypter(String className) { + this.className = className; + } + @Override public void execute(ArrayList classNodeList) { - BytecodeViewer.showMessage("This is a planned feature."); + JOptionPane pane = new JOptionPane( + "WARNING: This will load the classes into the JVM and execute allatori decrypter function" + + BytecodeViewer.nl + + "for each class. IF THE FILE YOU'RE LOADING IS MALICIOUS, DO NOT CONTINUE."); + Object[] options = new String[] { "Continue", "Cancel" }; + pane.setOptions(options); + JDialog dialog = pane.createDialog(BytecodeViewer.viewer, + "Bytecode Viewer - WARNING"); + dialog.setVisible(true); + Object obj = pane.getValue(); + int result = -1; + for (int k = 0; k < options.length; k++) + if (options[k].equals(obj)) + result = k; + + if (result == 0) { + try { + + if (!className.equals("*")) { + for (ClassNode classNode : classNodeList) { + if (classNode.name.equals(className)) + scanClassNode(classNode); + } + } else { + for (ClassNode classNode : classNodeList) { + scanClassNode(classNode); + } + } + }catch(Exception e){ + new ExceptionUI(e, "github.com/Szperak"); + } finally { + frame.appendText(out.toString()); + frame.setVisible(true); + } + } } + private void log(String msg){ + out.append(msg); + out.append(BytecodeViewer.nl); + } + + public void scanClassNode(ClassNode classNode) throws Exception { + for(MethodNode method : classNode.methods){ + scanMethodNode(classNode, method); + } + + } + + + public int readUnsignedShort(byte[] b, final int index) { + return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF); + } + private int getConstantPoolSize(String className){ + byte[] fileContents = BytecodeViewer.getFileContents(className+".class"); + return readUnsignedShort(fileContents, 8); + } + + + 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 = decrypterclassnode.getMethodByName(decryptermethodname); + 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> decrypterclasslist = the.bytecode.club.bytecodeviewer.api.BytecodeViewer + .loadClassesIntoClassLoader(new ArrayList( + 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 boolean scanDecrypter(MethodNode decryptermethodnode, int newHashCode){ + InsnList iList = decryptermethodnode.instructions; + + AbstractInsnNode insn = null, removeInsn = null; + for (AbstractInsnNode i : iList.toArray()) { + if(i instanceof MethodInsnNode){ + MethodInsnNode methodi = ((MethodInsnNode) i); + if("currentThread".equals(methodi.name)){ // find code form this instruction + insn = i; + break; + } + + } + + } + if(insn == null){ + return false; + } + + while(insn != null){ + if(insn instanceof MethodInsnNode){ + MethodInsnNode methodi = ((MethodInsnNode) insn); + if("hashCode".equals(methodi.name)){ // to this instruction + break; + } + } + removeInsn = insn; + insn = insn.getNext(); + iList.remove(removeInsn); // and remove it + } + if(insn == null) return false; + iList.set(insn, new LdcInsnNode(newHashCode)); // then replace it with pre-computed key LDC + return true; + } + + private String invokeDecrypter(Class decrypterclass, String name, String arg) throws Exception{ + try { + Method decryptermethod = decrypterclass.getDeclaredMethod(name, String.class); + + decryptermethod.setAccessible(true); + return (String) decryptermethod.invoke(null, arg); + + } catch (Exception e) { + log("Could not invoke decrypter method: "+name+" of class "+decrypterclass.getName()); + throw e; + } + } + }