Java: ClassReader classReader = new ClassReader(file.getInputStream(entry)); ClassNode classNode = new ClassNode(); classReader.accept(classNode, ClassReader.SKIP_DEBUG); classes.put(entry.getName(), classNode);

Java: public void saveJar() { try (JarOutputStream jos = new JarOutputStream(new FileOutputStream("newjar.jar"), manifest)) { Collection<ClassNode> classNodes = classes.values(); List<String> names = new ArrayList<>(); for (ClassNode node : classNodes) { if (names.contains(node.name)) continue; JarEntry newEntry = new JarEntry(node.name + ".class"); jos.putNextEntry(newEntry); ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS); node.accept(writer); jos.write(writer.toByteArray()); names.add(node.name); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }

Java: JarEntry newEntry = new JarEntry(node.name + ".class"); jos.putNextEntry(newEntry); ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS); node.accept(writer); jos.write(writer.toByteArray());

Spoiler: Explanation Certain optimizations have been introduced to the JVM. One of these optimizations is the requirement for class files to specify certain information, the maximum stack size and number of variables used, specifically. This information is required for every method in the class. With this information, the JVM doesn't have to calculate this itself, which saves some time. The ClassWriter.COMPUTE_MAXS is a flag that enables automatic calculation of the maximum stack size and number of local variables in methods.

Java: JarHelper helper = new JarHelper(new JarFile("path/to/your/jarfile.jar")); ClassNode clientClass = helper.getClasses().get("Client.class"); if (clientClass != null) System.out.println("The client class is here! "+clientClass.name);

The client class is here! Client

Java: import org.objectweb.asm.tree.ClassNode; public abstract class ClassTransformer { public abstract void transform(ClassNode node); }

node.interfaces.add("java/util/List");

Java: ClassTransformer transformer = new ClientClassTransformer(); transformer.transform(clientClass); helper.saveJar();

public final class Client implements List {

ClassNode gameNode = helper.getClasses().get("Game.class");

Let's go through this step by step. We create a ClassReader object that basically takes the bytes of our jar file entry which happens to be a class file. We then create a ClassNode object which will correspond to the class file. We call the accept method of the ClassReader object, and passing our ClassNode object in as a parameter. The ClassReader essentially parses the class file, and puts the information into the ClassNode object for us to manipulate later on. Lastly, we put the ClassNode instance into our HashMap class member, and we correlate it with the name of the Class file.This is it for the actual parsing of our target jar file. The only remaining thing we need to do for this class specifically is to create the method that will save our jar file. It's pretty straightforward.We start out by creating ainstance. We will use this instance to write out the data of our class files, to create our new modified jar file with the modified classes. I added aof strings that I add to after every loop cycle. At the start of the loop, you can see that I check the name of the currentagainst the names in the list. This is simply to prevent any duplicate entries from occurring.instance and give it the name of our, and we append ".class" at the end because the ".class" part of the name is stripped when we parse the jar early on.We instantiate ainstance, and we give it this weird constant value from theclass. If you're interested in what this is, and what it means, just read the text inside the following spoiler tag. The truth is, you don't really need to know what it is, or what it does. You can get by without that kind of knowledge, but I'll explain why anyway.Next we call the accept method on theand we pass in theinstance, so we can give its information over to the. We then proceed to write out the bytes of theobject, which is now stored in theinstance, and we can continue on to the start of the loop again. I skipped over a few lines of code in my explanation, but I only did so because I deem those lines of code self-explanatory.We can nowstart manipulating some bytecode! One last thing: make sure you add a getter for theclass member in theclass, so we can retrieve it in our main class.Let's now go on to ourclass, and test to see if the classnodes are actually stored in our classes instance in theobject.How cool is that? Now that we've got ainstance, we can work with it and manipulate its bytecode.Let's create a new abstract class called. The idea is that we create a newclass for every class we want to change. Inside the abstract class, let's add an abstract method called, the method which will manipulate theobject passed to it.object in ourclass that represents the Client class of our target jar, it seems appropriate that we'll start by manipulating that class.Create a new class, call it. Make it extend theclass, and add in themethod in there. Let's start manipulating theobject. We'll start with something super easy, which only takes one line of code. What is that exactly? Well, all we're going to do is make the class implement a new interface of our choice. Alright, let's make our target class implement theinterface. From a hacking perspective, this would make no sense. However, I'm simply doing this to demonstrate how we would do this. Let's go!Wait, what? That's it? Yes, that's it. Quite anticlimactic, I dare say. This is only the basics though, it'll get more advanced, don't you worry.Before we move on, let's test and see if this actually works. Go to yourclass, and add these lines of codeyou start instantiating the jar with reflection. It only makes sense to do this before we instantiate the jar because we have to create the modified jar first, so we can load that one in later on with reflection.class file. If you did everything correctly, you should see this:It's alive! However, this code really serves no purpose for us, so we can remove it for now. We're not going to be making any changes to theclass, but wegoing to be changing a few other classes. Theclass is of great importance for us because that is where the magic happens. Let's try creating a small method inside theclass. But first we need to get theobject that represents ourclass.To be continued in the next part.