diff --git a/src/main/java/net/runelite/deob/deobfuscators/RenameUnique.java b/src/main/java/net/runelite/deob/deobfuscators/RenameUnique.java index 19926f3d25..163e3820ee 100644 --- a/src/main/java/net/runelite/deob/deobfuscators/RenameUnique.java +++ b/src/main/java/net/runelite/deob/deobfuscators/RenameUnique.java @@ -1,147 +1,17 @@ package net.runelite.deob.deobfuscators; -import java.util.ArrayList; import java.util.List; import net.runelite.deob.ClassFile; import net.runelite.deob.ClassGroup; import net.runelite.deob.Deobfuscator; import net.runelite.deob.Field; -import net.runelite.deob.Interfaces; import net.runelite.deob.Method; -import net.runelite.deob.attributes.code.Exceptions; -import net.runelite.deob.pool.Class; -import net.runelite.deob.pool.NameAndType; -import net.runelite.deob.signature.Signature; -import net.runelite.deob.signature.Type; -import java.util.HashSet; -import java.util.Set; -import net.runelite.deob.attributes.Code; import net.runelite.deob.util.NameMappings; public class RenameUnique implements Deobfuscator { - private void renameClass(ClassFile on, ClassFile old, String name) - { - if (on.getParentClass().getName().equals(old.getName())) - on.setParentClass(new Class(name)); - - Interfaces interfaces = on.getInterfaces(); - List interfaceList = interfaces.getInterfaces(); - for (Class inter : interfaceList) - if (inter.getName().equals(old.getName())) - { - int idx = interfaceList.indexOf(inter); - interfaceList.remove(idx); - interfaceList.add(idx, new Class(name)); - break; - } - } - - private void renameClass(ClassGroup group, ClassFile cf, String name) - { - for (ClassFile c : group.getClasses()) - { - // rename on child interfaces and classes - renameClass(c, cf, name); - - for (Method method : c.getMethods().getMethods()) - { - // rename on instructions. this includes method calls and field accesses. - if (method.getCode() != null) - { - // rename on exception handlers - Exceptions exceptions = method.getCode().getExceptions(); - exceptions.renameClass(cf, name); - } - - // rename on parameters - Signature signature = method.getDescriptor(); - for (int i = 0; i < signature.size(); ++i) - { - Type type = signature.getTypeOfArg(i); - - if (type.getType().equals("L" + cf.getName() + ";")) - signature.setTypeOfArg(i, new Type("L" + name + ";", type.getArrayDims())); - } - - // rename return type - if (signature.getReturnValue().getType().equals("L" + cf.getName() + ";")) - signature.setTypeOfReturnValue(new Type("L" + name + ";", signature.getReturnValue().getArrayDims())); - - // rename on exceptions thrown - if (method.getExceptions() != null) - method.getExceptions().renameClass(cf, name); - } - - // rename on fields - for (Field field : c.getFields().getFields()) - if (field.getType().getType().equals("L" + cf.getName() + ";")) - field.setType(new Type("L" + name + ";", field.getType().getArrayDims())); - } - - cf.setName(name); - } - - // find the base methods for a method. search goes up from there to see if two - // different methods can be invoked with the same instruction. - private List findBaseMethods(List methods, ClassFile cf, NameAndType method) - { - if (cf == null) - return methods; - - Method m = cf.findMethod(method); - if (m != null && !m.isStatic()) - methods.add(m); - - List parentMethods = findBaseMethods(new ArrayList(), cf.getParent(), method); - - for (ClassFile inter : cf.getInterfaces().getMyInterfaces()) - findBaseMethods(parentMethods, inter, method); - - // parentMethods take precedence over our methods - return parentMethods.isEmpty() ? methods : parentMethods; - } - - private List findBaseMethods(Method method) - { - return findBaseMethods(new ArrayList<>(), method.getMethods().getClassFile(), method.getNameAndType()); - } - - private void findMethodUp(List methods, Set visited, ClassFile cf, NameAndType method) - { - if (cf == null || visited.contains(cf)) - return; - - visited.add(cf); // can do diamond inheritance with interfaces - - Method m = cf.findMethod(method); - if (m != null && !m.isStatic()) - methods.add(m); - - for (ClassFile child : cf.getChildren()) - findMethodUp(methods, visited, child, method); - } - - private List getVirutalMethods(Method method) - { - List list = new ArrayList<>(); - - if (method.isStatic()) - { - list.add(method); - return list; - } - - List bases = findBaseMethods(method); // base methods method overrides - assert !bases.isEmpty(); // must contain at least a method - - // now search up from bases, appending to list. - for (Method m : bases) - findMethodUp(list, new HashSet<>(), m.getMethods().getClassFile(), m.getNameAndType()); - - return list; - } + private Renamer renamer; private void generateClassNames(NameMappings map, ClassGroup group) { @@ -180,7 +50,7 @@ public class RenameUnique implements Deobfuscator if (method.getName().length() > 2) continue; - List virtualMethods = getVirutalMethods(method); + List virtualMethods = Renamer.getVirutalMethods(method); assert !virtualMethods.isEmpty(); String name; @@ -193,19 +63,6 @@ public class RenameUnique implements Deobfuscator map.map(m.getPoolMethod(), name); } } - - private void regeneratePool(ClassGroup group) - { - for (ClassFile cf : group.getClasses()) - for (Method m : cf.getMethods().getMethods()) - { - Code c = m.getCode(); - if (c == null) - continue; - - c.getInstructions().regeneratePool(); - } - } @Override public void run(ClassGroup group) @@ -219,48 +76,7 @@ public class RenameUnique implements Deobfuscator this.generatFieldNames(mappings, group); this.generateMethodNames(mappings, group); - int classes = 0, fields = 0, methods = 0; - - // rename fields - for (ClassFile cf : group.getClasses()) - for (Field field : cf.getFields().getFields()) - { - String newName = mappings.get(field.getPoolField()); - if (newName == null) - continue; - - field.setName(newName); - ++fields; - } - - // rename methods - for (ClassFile cf : group.getClasses()) - for (Method method : cf.getMethods().getMethods()) - { - String newName = mappings.get(method.getPoolMethod()); - if (newName == null) - continue; - - List virtualMethods = getVirutalMethods(method); - assert !virtualMethods.isEmpty(); - - for (Method m : virtualMethods) - m.setName(newName); - methods += virtualMethods.size(); - } - - for (ClassFile cf : group.getClasses()) - { - String newName = mappings.get(cf.getPoolClass()); - if (newName == null) - continue; - - renameClass(group, cf, newName); - ++classes; - } - - this.regeneratePool(group); - - System.out.println("Uniquely renamed " + classes + " classes, " + fields + " fields, and " + methods + " methods"); + renamer = new Renamer(mappings); + renamer.run(group); } } diff --git a/src/main/java/net/runelite/deob/deobfuscators/Renamer.java b/src/main/java/net/runelite/deob/deobfuscators/Renamer.java new file mode 100644 index 0000000000..1fa6d48b73 --- /dev/null +++ b/src/main/java/net/runelite/deob/deobfuscators/Renamer.java @@ -0,0 +1,214 @@ +package net.runelite.deob.deobfuscators; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import net.runelite.deob.ClassFile; +import net.runelite.deob.ClassGroup; +import net.runelite.deob.Deobfuscator; +import net.runelite.deob.Field; +import net.runelite.deob.Interfaces; +import net.runelite.deob.Method; +import net.runelite.deob.attributes.Code; +import net.runelite.deob.attributes.code.Exceptions; +import net.runelite.deob.pool.NameAndType; +import net.runelite.deob.signature.Signature; +import net.runelite.deob.signature.Type; +import net.runelite.deob.util.NameMappings; + +public class Renamer implements Deobfuscator +{ + private final NameMappings mappings; + + public Renamer(NameMappings mappings) + { + this.mappings = mappings; + } + + private void renameClass(ClassFile on, ClassFile old, String name) + { + if (on.getParentClass().getName().equals(old.getName())) + on.setParentClass(new net.runelite.deob.pool.Class(name)); + + Interfaces interfaces = on.getInterfaces(); + List interfaceList = interfaces.getInterfaces(); + for (net.runelite.deob.pool.Class inter : interfaceList) + if (inter.getName().equals(old.getName())) + { + int idx = interfaceList.indexOf(inter); + interfaceList.remove(idx); + interfaceList.add(idx, new net.runelite.deob.pool.Class(name)); + break; + } + } + + private void renameClass(ClassGroup group, ClassFile cf, String name) + { + for (ClassFile c : group.getClasses()) + { + // rename on child interfaces and classes + renameClass(c, cf, name); + + for (Method method : c.getMethods().getMethods()) + { + // rename on instructions. this includes method calls and field accesses. + if (method.getCode() != null) + { + // rename on exception handlers + Exceptions exceptions = method.getCode().getExceptions(); + exceptions.renameClass(cf, name); + } + + // rename on parameters + Signature signature = method.getDescriptor(); + for (int i = 0; i < signature.size(); ++i) + { + Type type = signature.getTypeOfArg(i); + + if (type.getType().equals("L" + cf.getName() + ";")) + signature.setTypeOfArg(i, new Type("L" + name + ";", type.getArrayDims())); + } + + // rename return type + if (signature.getReturnValue().getType().equals("L" + cf.getName() + ";")) + signature.setTypeOfReturnValue(new Type("L" + name + ";", signature.getReturnValue().getArrayDims())); + + // rename on exceptions thrown + if (method.getExceptions() != null) + method.getExceptions().renameClass(cf, name); + } + + // rename on fields + for (Field field : c.getFields().getFields()) + if (field.getType().getType().equals("L" + cf.getName() + ";")) + field.setType(new Type("L" + name + ";", field.getType().getArrayDims())); + } + + cf.setName(name); + } + + // find the base methods for a method. search goes up from there to see if two + // different methods can be invoked with the same instruction. + private static List findBaseMethods(List methods, ClassFile cf, NameAndType method) + { + if (cf == null) + return methods; + + Method m = cf.findMethod(method); + if (m != null && !m.isStatic()) + methods.add(m); + + List parentMethods = findBaseMethods(new ArrayList(), cf.getParent(), method); + + for (ClassFile inter : cf.getInterfaces().getMyInterfaces()) + findBaseMethods(parentMethods, inter, method); + + // parentMethods take precedence over our methods + return parentMethods.isEmpty() ? methods : parentMethods; + } + + private static List findBaseMethods(Method method) + { + return findBaseMethods(new ArrayList<>(), method.getMethods().getClassFile(), method.getNameAndType()); + } + + private static void findMethodUp(List methods, Set visited, ClassFile cf, NameAndType method) + { + if (cf == null || visited.contains(cf)) + return; + + visited.add(cf); // can do diamond inheritance with interfaces + + Method m = cf.findMethod(method); + if (m != null && !m.isStatic()) + methods.add(m); + + for (ClassFile child : cf.getChildren()) + findMethodUp(methods, visited, child, method); + } + + public static List getVirutalMethods(Method method) + { + List list = new ArrayList<>(); + + if (method.isStatic()) + { + list.add(method); + return list; + } + + List bases = findBaseMethods(method); // base methods method overrides + assert !bases.isEmpty(); // must contain at least a method + + // now search up from bases, appending to list. + for (Method m : bases) + findMethodUp(list, new HashSet<>(), m.getMethods().getClassFile(), m.getNameAndType()); + + return list; + } + + private void regeneratePool(ClassGroup group) + { + for (ClassFile cf : group.getClasses()) + for (Method m : cf.getMethods().getMethods()) + { + Code c = m.getCode(); + if (c == null) + continue; + + c.getInstructions().regeneratePool(); + } + } + + @Override + public void run(ClassGroup group) + { + group.buildClassGraph(); + group.lookup(); + + int classes = 0, fields = 0, methods = 0; + + // rename fields + for (ClassFile cf : group.getClasses()) + for (Field field : cf.getFields().getFields()) + { + String newName = mappings.get(field.getPoolField()); + if (newName == null) + continue; + + field.setName(newName); + ++fields; + } + + // rename methods + for (ClassFile cf : group.getClasses()) + for (Method method : cf.getMethods().getMethods()) + { + String newName = mappings.get(method.getPoolMethod()); + if (newName == null) + continue; + + List virtualMethods = getVirutalMethods(method); + assert !virtualMethods.isEmpty(); + + for (Method m : virtualMethods) + m.setName(newName); + methods += virtualMethods.size(); + } + + for (ClassFile cf : group.getClasses()) + { + String newName = mappings.get(cf.getPoolClass()); + if (newName == null) + continue; + + renameClass(group, cf, newName); + ++classes; + } + + this.regeneratePool(group); + + System.out.println("Renamed " + classes + " classes, " + fields + " fields, and " + methods + " methods"); + } +}